diff --git a/version5/COPYING b/version5/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/version5/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/version5/Makefile b/version5/Makefile new file mode 100644 index 0000000..0467929 --- /dev/null +++ b/version5/Makefile @@ -0,0 +1,1075 @@ +#------------------------------------------------------------------------- +# Desc: GNU makefile for XFLAIM library and utilities +# 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: Makefile 3136 2006-01-25 12:19:01 -0700 (Wed, 25 Jan 2006) dsanders $ +#------------------------------------------------------------------------- + + +############################################################################# +# +# Sample Usage: +# +# make clean debug all +# +############################################################################# + +# -- Target variables -- + +target_build_type = +usenativecc = yes +target_os_family = +target_processor = +target_word_size = +requested_target_word_size = +win_target = +unix_target = +build_output_dir = ./build + +# -- Enable command echoing + +ifneq (,$(findstring verbose,$(MAKECMDGOALS))) + ec = +else + ec = @ +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 + +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 + $(error Host operating system could not be determined) +endif + +# -- Target build type -- + +ifndef target_build_type + ifneq (,$(findstring debug,$(MAKECMDGOALS))) + target_build_type = debug + endif +endif + +ifndef target_build_type + ifneq (,$(findstring release,$(MAKECMDGOALS))) + 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))) + usenativecc = no +endif + +# -- Override platform default word size? -- + +ifneq (,$(findstring 64bit,$(MAKECMDGOALS))) + requested_target_word_size = 64 +endif + +ifneq (,$(findstring 32bit,$(MAKECMDGOALS))) + requested_target_word_size = 32 +endif + +# -- Target operating system and processor architecture -- + +ifndef target_os_family + ifeq ($(host_os_family),linux) + unix_target = yes + target_os_family = linux + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring x86,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + else + $(error Platform not supported) + endif + else + ifneq (,$(findstring x86,$(HOSTTYPE))) + ifneq (,$(findstring x86_64,$(HOSTTYPE))) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + else + ifneq (,$(findstring i386,$(HOSTTYPE))) + target_processor = x86 + target_word_size = 32 + else + $(error Platform not supported) + endif + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),solaris) + unix_target = yes + target_os_family = solaris + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring sparc,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = sparc + target_word_size = 64 + else + target_processor = sparc + target_word_size = 32 + endif + else + ifeq ($(requested_target_word_size),64) + target_processor = x86 + target_word_size = 64 + else + target_processor = x86 + target_word_size = 32 + endif + endif + else + ifneq (,$(findstring sparc,$(HOSTTYPE))) + target_processor = sparc + target_word_size = 32 + else + target_processor = x86 + target_word_size = 32 + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),osx) + unix_target = yes + target_os_family = osx + + ifeq (,$(HOSTTYPE)) + $(error HOSTTYPE environment variable has not been set) + endif + + ifdef requested_target_word_size + ifneq (,$(findstring powerpc,$(HOSTTYPE))) + ifeq ($(requested_target_word_size),64) + target_processor = powerpc + target_word_size = 64 + else + target_processor = powerpc + target_word_size = 32 + endif + else + $(error Platform not supported) + endif + else + ifneq (,$(findstring powerpc,$(HOSTTYPE))) + target_processor = powerpc + target_word_size = 32 + else + $(error Platform not supported) + endif + endif + endif +endif + +ifndef target_os_family + ifeq ($(host_os_family),win) + win_target = yes + target_os_family = win + target_processor = x86 + target_word_size = 32 + endif +endif + +ifndef target_os_family + $(error Target operating system could not be determined) +endif + +# -- Utility variables -- + +em := +sp := $(em) $(em) +comma := , + +ifeq ($(host_os_family),win) + allprereqs = $(subst /,\,$+) + copycmd = copy /Y $(subst /,\,$(1)) $(subst /,\,$(2)) >NUL + mkdircmd = -if not exist $(subst /,\,$(1)) mkdir $(subst /,\,$(1)) >NUL + runtest = cmd /C "cd $(subst /,\,$(test_dir)) && $(1) -d" +else + allprereqs = $+ + copycmd = cp -f $(1) $(2) + mkdircmd = mkdir -p $(1) + runtest = sh -c "cd $(test_dir); ./$(1) -d; exit" +endif + +# -- Target path -- + +target_path = $(build_output_dir)/$(target_os_family)-$(target_processor)-$(target_word_size)/$(target_build_type) + +# -- Tools -- + +libr = +linker = +compiler = + +ifdef unix_target + gprintf = printf +else + gprintf = gprintf +endif + +# Compiler definitions and flags + +ccflags = +ccdefs = + +ifneq (,$(findstring flm_dbg_log,$(MAKECMDGOALS))) + ccdefs += FLM_DBG_LOG +endif + +ifeq ($(target_word_size),64) + ccdefs += FLM_64BIT +endif + +# Directories + +inc_dirs = src util +obj_dir = $(target_path)/obj +util_dir = $(target_path)/util +test_dir = $(target_path)/test +sample_dir = $(target_path)/sample +shared_lib_dir = $(target_path)/lib/shared +static_lib_dir = $(target_path)/lib/static + +############################################################################## +# Win settings +############################################################################## +ifdef win_target + exe_suffix = .exe + obj_suffix = .obj + lib_prefix = + lib_suffix = .lib + shared_lib_suffix = .dll + libr = lib.exe + linker = link.exe + compiler = cl.exe + + # Compiler defines and flags + + ccflags += /nologo /c /GF /GR /J /MD /W4 /WX /Zi /Zp1 + + 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:$(subst /,\,$(@:.dll=.pdb)) \ + /map:$(subst /,\,$(@:.dll=.map)) \ + /INCREMENTAL:NO \ + /NOLOGO \ + /OUT:$(subst /,\,$@) + + util_link_flags = \ + /DEBUG /PDB:$(subst /,\,$(@:.exe=.pdb)) \ + /map:$(subst /,\,$(@:.exe=.map)) \ + /INCREMENTAL:NO \ + /FIXED:NO \ + /NOLOGO \ + /OUT:$(subst /,\,$@) + + # Libraries that our various components need to link against + + link_libs = imagehlp.lib user32.lib rpcrt4.lib wsock32.lib + + # 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 /,\,$(inc_dir))) + + # Concatenate everything into the ccflags variable + + ccflags += $(ccdefine) $(ccinclude) +endif + +############################################################################## +# Linux/Unix settings +############################################################################## +ifdef unix_target + exe_suffix = + obj_suffix = .o + lib_prefix = lib + lib_suffix = .a + shared_lib_suffix = .so + compiler = g++ + linker = g++ + + ifeq ($(target_os_family),osx) + libr = libtool + else + libr = ar + endif + + ifeq ($(usenativecc),yes) + ifeq ($(target_os_family),solaris) + compiler = CC + linker = CC + endif + endif + + # Compiler defines and flags + + ifeq ($(compiler),g++) + ccflags += -Wall -Werror -fPIC + + ifeq ($(target_word_size),64) + ccflags += -m64 + else + ccflags += -m32 + 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 + + ifeq ($(target_build_type),release) + ccflags += \ + -O \ + -fforce-mem \ + -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 \ + -funit-at-a-time \ + -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 + endif + 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$(inc_dir)) + + # Concatenate everything into the ccflags variable + + ccflags += $(ccdefine) $(ccinclude) + + # Linker switches + + shared_link_flags = + + link_flags = -o $@ + + ifeq ($(compiler),g++) + ifeq ($(target_word_size),64) + link_flags += -m64 + else + link_flags += -m32 + endif + endif + + link_libs = -lpthread + + ifeq ($(target_os_family),linux) + link_libs += -lncurses -lrt -lstdc++ -ldl + shared_link_flags += -shared -Wl,-Bsymbolic -fpic \ + -Wl,-soname,$(@F).1 -o $@ + else + link_libs += -lcurses + endif + + ifeq ($(target_os_family),solaris) + link_flags += -R /usr/lib/lwp + ifeq ($(usenativecc),yes) + ifeq ($(target_word_size),64) + link_flags += -xarch=generic64 + endif + endif + link_libs += -lm -lc -ldl -lsocket -lnsl -lrt + endif + + ifeq ($(target_os_family),osx) + link_libs += -lstdc++ -ldl + endif + + util_link_flags = $(link_flags) +endif + +# -- File lists -- + +xflaim_src = \ + $(wildcard src/*.cpp) + +utilsup_src = \ + flm_dlst.cpp \ + flm_lutl.cpp \ + ftx.cpp \ + sharutil.cpp + +checkdb_src = \ + checkdb.cpp + +rebuild_src = \ + rebuild.cpp + +view_src = \ + view.cpp \ + viewblk.cpp \ + viewdisp.cpp \ + viewedit.cpp \ + viewhdr.cpp \ + viewlfil.cpp \ + viewmenu.cpp \ + viewsrch.cpp + +sample_src = \ + sample.cpp + +xshell_src = \ + fdomedt.cpp \ + xshell.cpp \ + fshell.cpp + +ut_basictest_src = \ + flmunittest.cpp \ + basictestsrv.cpp + +ut_binarytest_src = \ + flmunittest.cpp \ + binarytest.cpp + +ut_colldeftest_src = \ + flmunittest.cpp \ + colldeftestsrv.cpp + +ut_dictchangetest_src = \ + flmunittest.cpp \ + dictchangetest.cpp + +ut_dictdeftest_src = \ + flmunittest.cpp \ + dictdeftestsrv.cpp + +ut_dirtyexittest1_src = \ + flmunittest.cpp \ + dirtyexittest1srv.cpp + +ut_dirtyexittest2_src = \ + flmunittest.cpp \ + dirtyexittest2srv.cpp + +ut_domnodetest_src = \ + flmunittest.cpp \ + domnodetestsrv.cpp + +ut_enctest_src = \ + flmunittest.cpp \ + enctestsrv.cpp + +ut_importtest_src = \ + flmunittest.cpp \ + importtestsrv.cpp + +ut_indexdeftest_src = \ + flmunittest.cpp \ + indexdeftestsrv.cpp + +ut_indextest1_src = \ + flmunittest.cpp \ + indextest1srv.cpp + +ut_indextest2_src = \ + flmunittest.cpp \ + indextest2srv.cpp + +ut_indextest3_src = \ + flmunittest.cpp \ + indextest3.cpp + +ut_metaphonetest_src = \ + flmunittest.cpp \ + metaphonetestsrv.cpp + +ut_namespacetest_src = \ + flmunittest.cpp \ + namespacetestsrv.cpp + +ut_regressiontest_src = \ + flmunittest.cpp \ + regressiontest.cpp + +ut_rfltest_src = \ + flmunittest.cpp \ + rfltestsrv.cpp + +ut_sortkeytest_src = \ + flmunittest.cpp \ + sortkeytest.cpp + +ut_sortkeytest2_src = \ + flmunittest.cpp \ + sortkeytest2.cpp + +ut_xpathtest1_src = \ + flmunittest.cpp \ + xpathtest1srv.cpp + +ut_xpathtest2_src = \ + flmunittest.cpp \ + xpathtest2srv.cpp + +# -- XFLAIM library -- + +xflaim_obj = $(patsubst src/%.cpp,$(obj_dir)/%$(obj_suffix),$(xflaim_src)) +static_xflaim_lib = $(static_lib_dir)/$(lib_prefix)xflaim$(lib_suffix) +shared_xflaim_lib = $(shared_lib_dir)/$(lib_prefix)xflaim$(shared_lib_suffix) + +# -- Unit tests -- + +ut_basictest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_basictest_src)) +ut_binarytest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_binarytest_src)) +ut_colldeftest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_colldeftest_src)) +ut_dictchangetest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_dictchangetest_src)) +ut_dictdeftest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_dictdeftest_src)) +ut_dirtyexittest1_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_dirtyexittest1_src)) +ut_dirtyexittest2_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_dirtyexittest2_src)) +ut_domnodetest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_domnodetest_src)) +ut_enctest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_enctest_src)) +ut_importtest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_importtest_src)) +ut_indexdeftest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_indexdeftest_src)) +ut_indextest1_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_indextest1_src)) +ut_indextest2_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_indextest2_src)) +ut_indextest3_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_indextest3_src)) +ut_metaphonetest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_metaphonetest_src)) +ut_namespacetest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_namespacetest_src)) +ut_regressiontest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_regressiontest_src)) +ut_rfltest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_rfltest_src)) +ut_sortkeytest_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_sortkeytest_src)) +ut_sortkeytest2_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_sortkeytest2_src)) +ut_xpathtest1_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_xpathtest1_src)) +ut_xpathtest2_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(ut_xpathtest2_src)) + +# -- Utilities -- + +checkdb_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(checkdb_src)) +rebuild_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(rebuild_src)) +view_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(view_src)) +sample_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(sample_src)) +utilsup_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(utilsup_src)) +xshell_obj = $(patsubst %.cpp,$(obj_dir)/%$(obj_suffix),$(xshell_src)) + +# -- Make system pattern search paths -- + +vpath %.cpp src util sample + +# -- default target -- + +.PHONY : all +all: status dircheck $(static_xflaim_lib) $(shared_xflaim_lib) allutils + +# -- *.cpp -> *$(obj_suffix) -- + +$(obj_dir)/%$(obj_suffix) : %.cpp +ifdef win_target + $(ec)$(compiler) $(ccflags) /Fd$(subst /,\,$(obj_dir))\tmp.pdb \ + /Fo$(subst /,\,$@) $(subst /,\,$<) +else + $(ec)$(gprintf) "$<\n" + $(ec)$(compiler) $(ccflags) -c $< -o $@ +endif + +# -- xflaim.lib and libxflaim.a -- + +$(static_xflaim_lib) : $(xflaim_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(libr) /NOLOGO $(subst /,\,$+) /OUT:$(subst /,\,$@) +else + $(ec)rm -f $@ +ifeq ($(target_os_family),osx) + $(ec)$(libr) -static -o $@ $+ +else + $(ec)$(libr) -rcs $@ $+ +endif +endif + +# -- xflaim.dll and libxflaim.so -- + +$(shared_xflaim_lib) : $(xflaim_obj) + $(ec)$(gprintf) "Building $@ ...\n" +ifdef win_target + $(ec)$(linker) $(subst /,\,$+) $(shared_link_flags) $(link_libs) +else + $(ec)rm -f $@ +ifeq ($(target_os_family),linux) + $(ec)$(linker) $+ $(shared_link_flags) $(link_libs) +endif +endif + +# -- Utility link command -- + +ifndef flm_util_link_cmd + define flm_util_link_cmd + $(ec)$(linker) $(util_link_flags) $(allprereqs) $(link_libs) + endef +endif + +# -- checkdb -- + +.PHONY : checkdb +checkdb: status dircheck $(util_dir)/checkdb$(exe_suffix) +$(util_dir)/checkdb$(exe_suffix): $(checkdb_obj) $(utilsup_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- rebuild -- + +.PHONY : rebuild +rebuild: status dircheck $(util_dir)/rebuild$(exe_suffix) +$(util_dir)/rebuild$(exe_suffix): $(rebuild_obj) $(utilsup_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- view -- + +.PHONY : view +view: status dircheck $(util_dir)/view$(exe_suffix) +$(util_dir)/view$(exe_suffix): $(view_obj) $(utilsup_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- sample -- + +.PHONY : sample +sample: status dircheck $(sample_dir)/sample$(exe_suffix) +$(sample_dir)/sample$(exe_suffix): $(sample_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + $(ec)$(call copycmd,sample/xmlfiles/*.xml,$(sample_dir)) + +# -- xshell -- + +.PHONY : xshell +xshell: status dircheck $(util_dir)/xshell$(exe_suffix) +$(util_dir)/xshell$(exe_suffix): $(xshell_obj) $(utilsup_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- basictest -- + +.PHONY : basictest +basictest: status dircheck $(test_dir)/basictest$(exe_suffix) +$(test_dir)/basictest$(exe_suffix): $(ut_basictest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- binarytest -- + +.PHONY : binarytest +binarytest: status dircheck $(test_dir)/binarytest$(exe_suffix) +$(test_dir)/binarytest$(exe_suffix): $(ut_binarytest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- colldeftest -- + +.PHONY : colldeftest +colldeftest: status dircheck $(test_dir)/colldeftest$(exe_suffix) +$(test_dir)/colldeftest$(exe_suffix): $(ut_colldeftest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- dictchangetest -- + +.PHONY : dictchangetest +dictchangetest: status dircheck $(test_dir)/dictchangetest$(exe_suffix) +$(test_dir)/dictchangetest$(exe_suffix): $(ut_dictchangetest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- dictdeftest -- + +.PHONY : dictdeftest +dictdeftest: status dircheck $(test_dir)/dictdeftest$(exe_suffix) +$(test_dir)/dictdeftest$(exe_suffix): $(ut_dictdeftest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- dirtyexittest1 -- + +.PHONY : dirtyexittest1 +dirtyexittest1: status dircheck $(test_dir)/dirtyexittest1$(exe_suffix) +$(test_dir)/dirtyexittest1$(exe_suffix): $(ut_dirtyexittest1_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- dirtyexittest2 -- + +.PHONY : dirtyexittest2 +dirtyexittest2: status dircheck $(test_dir)/dirtyexittest2$(exe_suffix) +$(test_dir)/dirtyexittest2$(exe_suffix): $(ut_dirtyexittest2_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- domnodetest -- + +.PHONY : domnodetest +domnodetest: status dircheck $(test_dir)/domnodetest$(exe_suffix) +$(test_dir)/domnodetest$(exe_suffix): $(ut_domnodetest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- enctest -- + +.PHONY : enctest +enctest: status dircheck $(test_dir)/enctest$(exe_suffix) +$(test_dir)/enctest$(exe_suffix): $(ut_enctest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- importtest -- + +.PHONY : importtest +importtest: status dircheck $(test_dir)/importtest$(exe_suffix) +$(test_dir)/importtest$(exe_suffix): $(ut_importtest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- indexdeftest -- + +.PHONY : indexdeftest +indexdeftest: status dircheck $(test_dir)/indexdeftest$(exe_suffix) +$(test_dir)/indexdeftest$(exe_suffix): $(ut_indexdeftest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- indextest1 -- + +.PHONY : indextest1 +indextest1: status dircheck $(test_dir)/indextest1$(exe_suffix) +$(test_dir)/indextest1$(exe_suffix): $(ut_indextest1_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- indextest2 -- + +.PHONY : indextest2 +indextest2: status dircheck $(test_dir)/indextest2$(exe_suffix) +$(test_dir)/indextest2$(exe_suffix): $(ut_indextest2_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- indextest3 -- + +.PHONY : indextest3 +indextest3: status dircheck $(test_dir)/indextest3$(exe_suffix) +$(test_dir)/indextest3$(exe_suffix): $(ut_indextest3_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- metaphonetest -- + +.PHONY : metaphonetest +metaphonetest: status dircheck $(test_dir)/metaphonetest$(exe_suffix) +$(test_dir)/metaphonetest$(exe_suffix): $(ut_metaphonetest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- namespacetest -- + +.PHONY : namespacetest +namespacetest: status dircheck $(test_dir)/namespacetest$(exe_suffix) +$(test_dir)/namespacetest$(exe_suffix): $(ut_namespacetest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- regressiontest -- + +.PHONY : regressiontest +regressiontest: status dircheck $(test_dir)/regressiontest$(exe_suffix) +$(test_dir)/regressiontest$(exe_suffix): $(ut_regressiontest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- rfltest -- + +.PHONY : rfltest +rfltest: status dircheck $(test_dir)/rfltest$(exe_suffix) +$(test_dir)/rfltest$(exe_suffix): $(ut_rfltest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- sortkeytest -- + +.PHONY : sortkeytest +sortkeytest: status dircheck $(test_dir)/sortkeytest$(exe_suffix) +$(test_dir)/sortkeytest$(exe_suffix): $(ut_sortkeytest_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- sortkeytest2 -- + +.PHONY : sortkeytest2 +sortkeytest2: status dircheck $(test_dir)/sortkeytest2$(exe_suffix) +$(test_dir)/sortkeytest2$(exe_suffix): $(ut_sortkeytest2_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- xpathtest1 -- + +.PHONY : xpathtest1 +xpathtest1: status dircheck $(test_dir)/xpathtest1$(exe_suffix) +$(test_dir)/xpathtest1$(exe_suffix): $(ut_xpathtest1_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- xpathtest2 -- + +.PHONY : xpathtest2 +xpathtest2: status dircheck $(test_dir)/xpathtest2$(exe_suffix) +$(test_dir)/xpathtest2$(exe_suffix): $(ut_xpathtest2_obj) $(static_xflaim_lib) + $(ec)$(gprintf) "Linking $@ ...\n" + $(flm_util_link_cmd) + +# -- misc. targets -- + +status: + $(ec)$(gprintf) "===============================================================================\n" + $(ec)$(gprintf) "Host Operating System Family = $(host_os_family)\n" + $(ec)$(gprintf) "Target Operating System Family = $(target_os_family)\n" + $(ec)$(gprintf) "Target Processor = $(target_processor)\n" + $(ec)$(gprintf) "Target Word Size = $(target_word_size)\n" + $(ec)$(gprintf) "Target Build Type = $(target_build_type)\n" + $(ec)$(gprintf) "Target Path = $(target_path)\n" + $(ec)$(gprintf) "Compiler = $(compiler)\n" + $(ec)$(gprintf) "Librarian = $(libr)\n" + $(ec)$(gprintf) "Defines = $(strip $(ccdefs))\n" + $(ec)$(gprintf) "===============================================================================\n" + +dircheck: + $(ec)$(call mkdircmd,$(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)) + +# -- phony targets -- + +.PHONY : allutils +allutils: status dircheck checkdb rebuild view sample xshell + +.PHONY : test +test: status dircheck $(static_xflaim_lib) \ + basictest binarytest colldeftest dictchangetest dictdeftest \ + dirtyexittest1 dirtyexittest2 domnodetest enctest importtest \ + indexdeftest indextest1 indextest2 indextest3 \ + namespacetest metaphonetest regressiontest rfltest sortkeytest \ + sortkeytest2 xpathtest1 xpathtest2 + $(ec)$(call copycmd,util/xmlfiles/*.xml,$(test_dir)) + $(ec)$(call runtest,basictest) + $(ec)$(call runtest,binarytest) + $(ec)$(call runtest,colldeftest) + $(ec)$(call runtest,dictchangetest) + $(ec)$(call runtest,dictdeftest) + $(ec)$(call runtest,dirtyexittest1) + $(ec)$(call runtest,dirtyexittest2) + $(ec)$(call runtest,domnodetest) + $(ec)$(call runtest,enctest) + $(ec)$(call runtest,importtest) + $(ec)$(call runtest,indexdeftest) + $(ec)$(call runtest,indextest1) + $(ec)$(call runtest,indextest2) + $(ec)$(call runtest,indextest3) + $(ec)$(call runtest,namespacetest) + $(ec)$(call runtest,metaphonetest) + $(ec)$(call runtest,regressiontest) + $(ec)$(call runtest,rfltest) + $(ec)$(call runtest,sortkeytest) + $(ec)$(call runtest,sortkeytest2) + $(ec)$(call runtest,xpathtest1) + $(ec)$(call runtest,xpathtest2) + +.PHONY : debug +debug: + $(ec)$(gprintf) "" + +.PHONY : release +release: + $(ec)$(gprintf) "" + +.PHONY : flm_dbg_log +flm_dbg_log: + $(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 : verbose +verbose: + $(ec)$(gprintf) "" + +.PHONY : clean +clean: + $(ec)$(gprintf) "Cleaning $(target_path) ...\n" +ifdef win_target + -$(ec)if exist $(subst /,\,$(target_path)) rd /s /q $(subst /,\,$(target_path)) >NUL + -$(ec)if exist *.pch del /Q *.pch >NUL +else + -$(ec)rm -rf $(target_path) 2>/dev/null +endif diff --git a/version5/gmake.exe b/version5/gmake.exe new file mode 100644 index 0000000..408b086 Binary files /dev/null and b/version5/gmake.exe differ diff --git a/version5/gprintf.exe b/version5/gprintf.exe new file mode 100644 index 0000000..cefb588 Binary files /dev/null and b/version5/gprintf.exe differ diff --git a/version5/sample/sample.cpp b/version5/sample/sample.cpp new file mode 100644 index 0000000..6ee271a --- /dev/null +++ b/version5/sample/sample.cpp @@ -0,0 +1,1195 @@ +//------------------------------------------------------------------------------ +// Desc: Sample application +// +// Tabs: 3 +// +// Copyright (c) 2002-2005 Novell, Inc. All Rights Reserved. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of version 2.1 of the GNU Lesser 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser 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.cpp 3102 2006-01-10 10:15:17 -0700 (Tue, 10 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "xflaim.h" + +#ifdef FLM_WIN + #define UI64FormatStr "I64u" + #define I64FormatStr "I64d" + #include +#elif defined( FLM_SOLARIS) + #define UI64FormatStr "llu" + #define I64FormatStr "lld" +#else + #define UI64FormatStr "Lu" + #define I64FormatStr "Ld" +#endif +#include +#include + +FLMBOOL gv_bShutdown = FALSE; + +#define DB_NAME_STR "tst.db" +#define BACKUP_NAME_STR "tst.bak" +#define NEW_NAME_STR "new.db" + +void printMessage( + const char * pszMessage, + FLMUINT uiLevel = 0); + +RCODE printDocument( + IF_Db * pDb, + IF_DOMNode * pRootNode); + +RCODE processAttributes( + IF_Db * pDb, + IF_DOMNode * pNode, + FLMBYTE ** ppszLine); + +/*************************************************************************** +Desc: Program entry point (main) +****************************************************************************/ +int main( + int, // iArgC, + char ** // ppucArgV + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucMsgBuf[ 200]; + XFLM_CREATE_OPTS createOpts; + IF_DbSystem * pDbSystem = NULL; + IF_Db * pDb = NULL; + IF_Backup * pBackup = NULL; + IF_DOMNode * pTmpNode = NULL; + FLMUINT uiMusicElementDef=0; + FLMUINT uiCdElementDef=0; + FLMUINT uiTitleElementDef=0; + FLMUINT uiArtistElementDef=0; + FLMUINT uiTracksElementDef=0; + FLMUINT uiNoAttrDef=0; + FLMUINT uiTitleAttrDef=0; + FLMUINT uiCollectionDef=0; + FLMBOOL bTranStarted = FALSE; + IF_DOMNode * pCdChild = NULL; + IF_DOMNode * pCdElement = NULL; + IF_DOMNode * pMusicElement = NULL; + IF_DOMNode * pCdAttr = NULL; + IF_DOMNode * pTrackNode = NULL; + IF_Query * pQuery = NULL; + IF_PosIStream * pPosIStream = NULL; + IF_PosIStream * pIStream = NULL; + FLMUINT uiIndex = 1; + FLMBYTE ucUntilKeyBuf[ XFLM_MAX_KEY_SIZE]; + FLMUINT uiUntilKeyLen; + FLMBYTE ucCurrentKeyBuf[ XFLM_MAX_KEY_SIZE]; + FLMUINT uiCurrentKeyLen; + FLMUINT64 ui64DocId; + char ucTitle[ 200]; + FLMUINT uiTitleLen; + char * ppszPath[] = + { + "7001e10c.xml", + "70028663.xml", + "70037c08.xml", + "70040b08.xml", + "70044808.xml", + "70045109.xml", + "70045e09.xml", + "70046709.xml", + "7004920a.xml", + "" + }; + FLMUINT uiLoop; + IF_DataVector * pFromKeyV = NULL; + IF_DataVector * pUntilKeyV = NULL; + const char * pszQueryString = "/music/cd/tracks[@title~=\"we our in luv\"]"; + const char * pszIndex = + "" + "" + ""; + + // Allocate a DbSystem object + + if( RC_BAD( rc = FlmAllocDbSystem( &pDbSystem))) + { + goto Exit; + } + + // Initialize the database system + + if( RC_BAD( rc = pDbSystem->init())) + { + goto Exit; + } + + // Create the database. This code will remove the database first if it + // exists. Once removed, it will try to create it. If that fails for + // any reason other than the database already exists, it will exit. + +RetryCreate: + + memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + + if( RC_BAD( rc = pDbSystem->dbCreate( DB_NAME_STR, NULL, NULL, NULL, + NULL, &createOpts, &pDb))) + { + if( RC_BAD( rc == NE_XFLM_FILE_EXISTS)) + { + if( RC_BAD( rc = pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + goto Exit; + } + + goto RetryCreate; + } + else + { + goto Exit; + } + } + + // Start an update transaction. All access to the database should + // be done within the confines of a transaction. When changes are being + // made to the database, an UPDATE transaction is required. It is best to + // keep transactions small if possible. + + if( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bTranStarted = TRUE; + + // Let's create a document to demonstrate how we use the DOM... + // Our document will catalog a music CD. It will look like: + + // + // + // We Are In Love + // Harry Connick Jr. + // + // + // + // + + // To accomplish this, we must define the elements of the document and a new + // collection to store the document in. + + // Create some element definitions for our document. + // Create the "music" element definition. + + if( RC_BAD( rc = pDb->createElementDef( NULL, "music", + XFLM_NODATA_TYPE, &uiMusicElementDef))) + { + goto Exit; + } + + // Create the "cd" element definition. + + if( RC_BAD( rc = pDb->createElementDef( NULL, "cd", + XFLM_NODATA_TYPE, &uiCdElementDef))) + { + goto Exit; + } + + // Create the "title" element definition. + + if( RC_BAD( rc = pDb->createElementDef( NULL, "title", + XFLM_TEXT_TYPE, &uiTitleElementDef))) + { + goto Exit; + } + + // Create the "artist" element definition. + + if( RC_BAD( rc = pDb->createElementDef( NULL, "artist", + XFLM_TEXT_TYPE, &uiArtistElementDef))) + { + goto Exit; + } + + // Create the "tracks" element definition. + + if( RC_BAD( rc = pDb->createElementDef( NULL, "tracks", + XFLM_NODATA_TYPE, &uiTracksElementDef))) + { + goto Exit; + } + + // Create the "no" attribute definition. + + if( RC_BAD( rc = pDb->createAttributeDef( NULL, "no", + XFLM_NUMBER_TYPE, &uiNoAttrDef))) + { + goto Exit; + } + + // Create the "title" attribute definition. + + if( RC_BAD( rc = pDb->createAttributeDef( NULL, "title", + XFLM_TEXT_TYPE, &uiTitleAttrDef))) + { + goto Exit; + } + + // Create our special music collection for storing music stuff. + + if( RC_BAD( rc = pDb->createCollectionDef( "Music Collection", + &uiCollectionDef))) + { + goto Exit; + } + + // We now have all of the definitions we need to build our document. + // Lets first create a new document, followed by its members... + + // Create the "music" root element + + if( RC_BAD( rc = pDb->createRootElement( uiCollectionDef, uiMusicElementDef, + &pMusicElement))) + { + goto Exit; + } + + // Create the "cd" element + + if( RC_BAD( rc = pMusicElement->createNode( pDb, ELEMENT_NODE, + uiCdElementDef, XFLM_FIRST_CHILD, &pCdElement))) + { + goto Exit; + } + + // Create the "title" element + + if( RC_BAD( rc = pCdElement->createNode( pDb, ELEMENT_NODE, + uiTitleElementDef, XFLM_FIRST_CHILD, &pCdChild))) + { + goto Exit; + } + + // Set the value for the title. + + if (RC_BAD( rc = pCdChild->setUTF8( pDb, (FLMBYTE *)"We Are In Love"))) + { + goto Exit; + } + + // Create the "artist" element + + if( RC_BAD( rc = pCdElement->createNode( pDb, ELEMENT_NODE, + uiArtistElementDef, XFLM_LAST_CHILD, &pCdChild))) + { + goto Exit; + } + + // Set the value for the title. + + if (RC_BAD( rc = pCdChild->setUTF8( pDb, (FLMBYTE *)"Harry Connick Jr."))) + { + goto Exit; + } + + // Create the first "tracks" element + + if( RC_BAD( rc = pCdElement->createNode( pDb, ELEMENT_NODE, + uiTracksElementDef, XFLM_LAST_CHILD, &pCdChild))) + { + goto Exit; + } + + // Create the "no." attribute, then the "title" attribute. + + if (RC_BAD( rc = pCdChild->createAttribute( pDb, uiNoAttrDef, &pCdAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pCdAttr->setUINT( pDb, (FLMUINT)1))) + { + goto Exit; + } + + if (RC_BAD( rc = pCdChild->createAttribute( pDb, uiTitleAttrDef, &pCdAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pCdAttr->setUTF8( pDb, (FLMBYTE *)"We Are In Love"))) + { + goto Exit; + } + + // Create the next "tracks" element + + if( RC_BAD( rc = pCdElement->createNode( pDb, ELEMENT_NODE, + uiTracksElementDef, XFLM_LAST_CHILD, &pCdChild))) + { + goto Exit; + } + + // An alternate way to create the attributes and set their values is to use + // the parent element to set the attribute values. + + // Create the two attributes. + + if (RC_BAD( rc = pCdChild->createAttribute( pDb, uiNoAttrDef, &pCdAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pCdChild->createAttribute( pDb, uiTitleAttrDef, &pCdAttr))) + { + goto Exit; + } + + // Using the parent of the attributes, set their values by specifying + // which attribute to set. + + if (RC_BAD( rc = pCdChild->setAttributeValueUINT( pDb, uiNoAttrDef, + (FLMUINT)2))) + { + goto Exit; + } + + if (RC_BAD( rc = pCdChild->setAttributeValueUTF8( + pDb, uiTitleAttrDef, (FLMBYTE *)"Only 'Cause I Don't Have You"))) + { + goto Exit; + } + + // It is always good practice to call documentDone whenever updates to a + // document are completed. + + if (RC_BAD( rc = pDb->documentDone( pMusicElement))) + { + goto Exit; + } + + // Commit the transaction + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted= FALSE; + + // Now we want to read back our document, and display it for all + // the world to see...:^) + + // Start a read transaction + + if( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0))) + { + goto Exit; + } + bTranStarted = TRUE; + + // Read the nodes. Start with the document node. + + if( RC_BAD( rc = pDb->getFirstDocument( uiCollectionDef, &pTmpNode))) + { + goto Exit; + } + + // printDocument is a simple function that walks through the document + // and displays it, node by node as an XML document. It assumes the + // node passed in is the root node. If it is not, then the display will + // look a bit odd, as you will see later in this sample program. + + if (RC_BAD( rc = printDocument( pDb, pTmpNode))) + { + goto Exit; + } + + // Commit the transaction + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted = FALSE; + + // Start an update transaction + + if( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0))) + { + goto Exit; + } + bTranStarted = TRUE; + + // Let's do a query on this document... Our query (above) is + // deliberately miss-spelling some of the words. It is looking for + // the music "tracks" with the title "We Are In Love". + // The syntax ~= means approximately equals, and is a "sounds like" + // search. + + if (RC_BAD( rc = pDbSystem->createIFQuery( &pQuery))) + { + goto Exit; + } + + if (RC_BAD( rc = pQuery->setCollection( uiCollectionDef))) + { + goto Exit; + } + + if (RC_BAD( rc = pQuery->setupQueryExpr( pDb, pszQueryString))) + { + goto Exit; + } + + if (RC_BAD( rc = pQuery->getFirst( pDb, &pTrackNode))) + { + goto Exit; + } + + printMessage( "Query:"); + printMessage( pszQueryString); + + // This will pring the tracks node, followed by all of the closing parent + // nodes. + + if (RC_BAD( rc = printDocument( pDb, pTrackNode))) + { + goto Exit; + } + + pQuery->Release(); + pQuery = NULL; + + // Commit the transaction + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted = FALSE; + + // Let's create an index to make searching easier. An index is essentially + // just another XML document to XFlaim, however it is created in the dictionary + // collection rather than in a data collection. There are two ways to create + // a document in XFlaim. One way (demonstrated above) is to create each node + // of the document, one at a time. The other is to import the document. + // Creating the index will demonstrate importing the document. Our + // index definition is shown as follows: + + // + // + // " + + // For our purposes, we will create a local variable (pszIndex) which + // holds the index document. We will import that using the importDocument + // method of the pDb object. + + // Import the index... + // We first need to create a BufferIStream object to stream the document + // from... + // Start an update transaction + + if( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bTranStarted = TRUE; + + if ( RC_BAD( rc = pDbSystem->openBufferIStream( pszIndex, strlen( pszIndex), + &pPosIStream))) + { + goto Exit; + } + + if ( RC_BAD( rc = pDb->import( pPosIStream, XFLM_DICT_COLLECTION))) + { + goto Exit; + } + + // Commit the transaction + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted = FALSE; + + // Now, let's get some additional documents in so we can do some more + // interesting stuff using a IF_DataVector. The documents we are + // interested in searching are the CD music documents. + // They have the following format: + + // + // + // 00097210 + // 2420 + // Frank Sinatra / Blue skies + // cddb/jazz + // blue skies + // . + // . + // . + // + + if( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bTranStarted = TRUE; + + // We will first need an input file stream. + + uiLoop = 0; + while( strlen( ppszPath[ uiLoop])) + { + if( RC_BAD( rc = pDbSystem->openFileIStream( + ppszPath[ uiLoop], &pIStream))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->import( pIStream, XFLM_DATA_COLLECTION))) + { + goto Exit; + } + + uiLoop++; + } + + // Commit the transaction + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted = FALSE; + + // Let's get some IF_DataVectors to work with. + + if( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS, 0))) + { + goto Exit; + } + bTranStarted = TRUE; + + if (RC_BAD( rc = pDbSystem->createIFDataVector( &pFromKeyV))) + { + goto Exit; + } + + if (RC_BAD( rc = pDbSystem->createIFDataVector( &pUntilKeyV))) + { + goto Exit; + } + + // We need to get the index. + + // Now to search the index above for all document titles in the index and + // display the document Id and title for each node. Note that the index + // number is known to be 1. + + if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, NULL, XFLM_FIRST, pFromKeyV))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, NULL, XFLM_LAST, pUntilKeyV))) + { + goto Exit; + } + + // Save the UntilKey value to compare with later. + + if (RC_BAD( rc = pUntilKeyV->outputKey( pDb, uiIndex, FALSE, + &ucUntilKeyBuf[0], XFLM_MAX_KEY_SIZE, &uiUntilKeyLen))) + { + goto Exit; + } + + for (;;) + { + // Display the current Document Id and the title. + + ui64DocId = pFromKeyV->getDocumentID(); + + uiTitleLen = sizeof(ucTitle); + if (RC_BAD( rc = pFromKeyV->getUTF8( 0, + (FLMBYTE *)&ucTitle[0], &uiTitleLen))) + { + goto Exit; + } + + sprintf( (char *)&ucMsgBuf[0], "DocId: %"UI64FormatStr"\n%s\n", + ui64DocId, ucTitle); + printMessage( (char *)&ucMsgBuf[0]); + + // Check to see if this key matches the last or UntilKeyV value. + + if (RC_BAD( rc = pFromKeyV->outputKey( pDb, uiIndex, FALSE, + &ucCurrentKeyBuf[0], XFLM_MAX_KEY_SIZE, &uiCurrentKeyLen))) + { + goto Exit; + } + + if (uiCurrentKeyLen == uiUntilKeyLen) + { + if (memcmp( ucCurrentKeyBuf, ucUntilKeyBuf, uiCurrentKeyLen) == 0) + { + // We are done! + + break; + } + } + + // Get the next key. + + if (RC_BAD( rc = pDb->keyRetrieve( uiIndex, pFromKeyV, + XFLM_EXCL, pFromKeyV))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bTranStarted = FALSE; + + // Close the database + + pDb->Release(); + pDb = NULL; + + // Close all unused files + + if( RC_BAD( rc = pDbSystem->closeUnusedFiles( 0))) + { + goto Exit; + } + + // Re-open the database + + if( RC_BAD( rc = pDbSystem->dbOpen( DB_NAME_STR, NULL, + NULL, NULL, FALSE, &pDb))) + { + goto Exit; + } + + // Backup the database + + if( RC_BAD( rc = pDb->backupBegin( XFLM_FULL_BACKUP, XFLM_READ_TRANS, + 0, &pBackup))) + { + goto Exit; + } + + if( RC_BAD( rc = pBackup->backup( BACKUP_NAME_STR, NULL, NULL, NULL, NULL))) + { + goto Exit; + } + + if (RC_BAD( rc = pBackup->endBackup())) + { + goto Exit; + } + + pBackup->Release(); + pBackup = NULL; + + // Close the database again + + pDb->Release(); + pDb = NULL; + + if( RC_BAD( rc = pDbSystem->closeUnusedFiles( 0))) + { + goto Exit; + } + + // Remove the database + + if( RC_BAD( rc = pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + goto Exit; + } + + // Restore the database + + if( RC_BAD( rc = pDbSystem->dbRestore( DB_NAME_STR, NULL, NULL, + BACKUP_NAME_STR, NULL, NULL, NULL))) + { + goto Exit; + } + + // Rename the database + + if( RC_BAD( rc = pDbSystem->dbRename( DB_NAME_STR, NULL, NULL, + NEW_NAME_STR, TRUE, NULL))) + { + goto Exit; + } + + // Copy the database + + if( RC_BAD( rc = pDbSystem->dbCopy( NEW_NAME_STR, NULL, NULL, + DB_NAME_STR, NULL, NULL, NULL))) + { + goto Exit; + } + + // Remove the new database + + if( RC_BAD( rc = pDbSystem->dbRemove( NEW_NAME_STR, NULL, NULL, TRUE))) + { + goto Exit; + } + + +Exit: + + if (bTranStarted && pDb) + { + (void)pDb->transAbort(); + } + + if (pCdChild) + { + pCdChild->Release(); + } + + if (pCdElement) + { + pCdElement->Release(); + } + + if( pMusicElement) + { + pMusicElement->Release(); + } + + if (pCdAttr) + { + pCdAttr->Release(); + } + + if (pTrackNode) + { + pTrackNode->Release(); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if (pQuery) + { + pQuery->Release(); + } + + if( pPosIStream) + { + pPosIStream->Release(); + } + + if (pIStream) + { + pIStream->Release(); + } + + if (pFromKeyV) + { + pFromKeyV->Release(); + } + + if (pUntilKeyV) + { + pUntilKeyV->Release(); + } + + if( pBackup) + { + pBackup->Release(); + } + + // Close the database object + + if( pDb) + { + pDb->Release(); + } + + if( RC_BAD( rc)) + { + sprintf( (char *)ucMsgBuf, "Error %04X -- %s\n", + (unsigned)rc, + (char *)pDbSystem->errorString( rc)); + printMessage( (char *)ucMsgBuf); + } + + // Shut down the XFlaim database engine. This call must be made + // even if the init call failed. No more XFlaim calls should be made + // by the application. + + pDbSystem->exit(); + pDbSystem->Release(); + + return( 0); +} + +/*************************************************************************** +Desc: Prints a string to stdout +****************************************************************************/ +void printMessage( + const char * pszMessage, + FLMUINT uiLevel) +{ + unsigned int uiLoop; + + for (uiLoop = 0; uiLoop < uiLevel; uiLoop++) + { + printf( " "); + } + + printf( "%s\n", pszMessage); +} + +/*============================================================================= +Desc: Simple routine to display the contents of a document. +=============================================================================*/ +RCODE printDocument( + IF_Db * pDb, + IF_DOMNode * pRootNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucLine[ 200]; + FLMBYTE * pszLine; + FLMBOOL bHasChildren = FALSE; + FLMBOOL bHadAttributes; + FLMUINT uiDataType; + char szName[ 50]; + FLMUINT uiNameLen; + IF_DOMNode * pNode = pRootNode; + char szTemp[ 200]; + FLMUINT uiTemp; + FLMBOOL bUnwinding = FALSE; + FLMBOOL bHadValue = FALSE; + FLMUINT uiThisLevel = 0; + FLMUINT uiNextLevel = 0; + eDomNodeType eNodeType; + + pNode->AddRef(); + pszLine = &ucLine[0]; + bHadAttributes = FALSE; + + while (pNode) + { + bHadValue = FALSE; + + // Write out the current line. + + if (pszLine != &ucLine[0]) + { + printMessage( (char *)ucLine, uiThisLevel); + pszLine = &ucLine[0]; + } + + // Adjust to the next level + + uiThisLevel = uiNextLevel; + eNodeType = pNode->getNodeType(); + + if( eNodeType == DOCUMENT_NODE) + { + if (!bUnwinding) + { + sprintf( (char *)pszLine, ""); + } + pszLine += strlen( (char *)pszLine); + } + else if( eNodeType == ELEMENT_NODE) + { + if (RC_BAD( rc = pNode->getQualifiedName( + pDb, szName, sizeof(szName), &uiNameLen))) + { + goto Exit; + } + + if (!bUnwinding) + { + sprintf( (char *)pszLine, "<%s", szName); + } + else + { + sprintf( (char *)pszLine, "", szName); + } + pszLine += strlen( (char *)pszLine); + } + + if (!bUnwinding) + { + if (RC_BAD( rc = processAttributes( pDb, pNode, &pszLine))) + { + goto Exit; + } + + // We need to know if this node has children to + // know how to close the line. + + if (RC_BAD( rc = pNode->hasChildren( pDb, &bHasChildren))) + { + goto Exit; + } + + if( eNodeType == DATA_NODE || eNodeType == COMMENT_NODE) + { + if (RC_BAD( rc = pNode->getDataType( pDb, &uiDataType))) + { + goto Exit; + } + + if( eNodeType == COMMENT_NODE) + { + sprintf( (char *)pszLine, ""); + pszLine += 3; + } + } + else + { + if ( !bHasChildren) + { + sprintf( (char *)pszLine, "/>"); + pszLine += 2; + } + else + { + sprintf( (char *)pszLine, ">"); + pszLine++; + } + } + + // Children + + if (bHasChildren) + { + // Get the child node. + + if (RC_BAD( rc = pNode->getFirstChild( pDb, &pNode))) + { + goto Exit; + } + + uiNextLevel++; + continue; + } + } + + bUnwinding = FALSE; + if (RC_BAD( rc = pNode->getNextSibling( pDb, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + continue; + } + + // Get the parent if there is one. Otherwise we are done. + + if (uiNextLevel) + { + --uiNextLevel; + } + + if (RC_BAD( rc = pNode->getParentNode( pDb, &pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + pNode->Release(); + pNode = NULL; + } + else + { + bUnwinding = TRUE; + } + } + + printMessage( (char *)ucLine); + +Exit: + + return( rc); +} + +/*============================================================================= +Desc: +=============================================================================*/ +RCODE processAttributes( + IF_Db * pDb, + IF_DOMNode * pNode, + FLMBYTE ** ppszLine) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pAttrNode = NULL; + FLMBOOL bHasAttrs; + char szName[ 50]; + FLMUINT uiNameLen; + FLMBYTE * pszLine = *ppszLine; + FLMUINT uiDataType; + FLMBYTE szTemp[ 200]; + FLMUINT uiTemp; + + if (RC_BAD( rc = pNode->hasAttributes( pDb, &bHasAttrs))) + { + goto Exit; + } + + if( !bHasAttrs) + { + goto Exit; + } + + // We have attributes. Let's get them and add them to the line. + + if (RC_BAD( rc = pNode->getFirstAttribute( pDb, &pAttrNode))) + { + goto Exit; + } + + for (;;) + { + if (RC_BAD( rc = pAttrNode->getQualifiedName( pDb, szName, + sizeof(szName), &uiNameLen))) + { + goto Exit; + } + + sprintf( (char *)pszLine, " %s", szName); + pszLine += (uiNameLen + 1); + + if (RC_BAD( rc = pAttrNode->getDataType( pDb, &uiDataType))) + { + goto Exit; + } + + switch (uiDataType) + { + case XFLM_TEXT_TYPE: + { + if (RC_BAD( rc = pAttrNode->getUTF8( pDb, szTemp, sizeof( szTemp), + 0, 200, &uiTemp))) + { + goto Exit; + } + sprintf( (char *)pszLine, "=\"%s\"", szTemp); + pszLine += (uiTemp + 3); + break; + } + + case XFLM_NUMBER_TYPE: + { + if (RC_BAD( rc = pAttrNode->getUINT( pDb, &uiTemp))) + { + goto Exit; + } + + sprintf( (char *)szTemp, "%u", (unsigned)uiTemp); + sprintf( (char *)pszLine, "=\"%s\"", szTemp); + pszLine += (strlen( (char *)szTemp) + 3); + break; + } + // Could also get binary data, but we won't handle it here. + } + + // Get the next attribute, if any + + if (RC_BAD( rc = pAttrNode->getNextSibling( pDb, &pAttrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + } + + // Update the line pointer on the way out. + + *ppszLine = pszLine; + +Exit: + + if (pAttrNode) + { + pAttrNode->Release(); + } + + return( rc); +} diff --git a/version5/sample/xmlfiles/7001e10c.xml b/version5/sample/xmlfiles/7001e10c.xml new file mode 100644 index 0000000..02410fe --- /dev/null +++ b/version5/sample/xmlfiles/7001e10c.xml @@ -0,0 +1,19 @@ + + +7001e10c +483 +V.A. / BONUS CD SEB 1-4 NON STOP MEGA MIX +cddb/misc +BIG BROTHER / ALEPH +SUPER SONIC LEVEL / ANTONELLA +BYE BYE BABY / MAX COVERI +HELP ME / MELA +MY WORLD / SOPHIE +DIVINE / MIKE HAMMER +POWER OF MAGIC / ALPHA TOWN +JUST A GAME / VANESSA +LOVE & PASSION / GIPSY & QUEEN +GUNFIRE / MARK FARINA +BAD DESIRE / F.C.F. +TOUCH ME / LINDA ROSS + diff --git a/version5/sample/xmlfiles/70028663.xml b/version5/sample/xmlfiles/70028663.xml new file mode 100644 index 0000000..ab63009 --- /dev/null +++ b/version5/sample/xmlfiles/70028663.xml @@ -0,0 +1,106 @@ + + +70028663 +648 +Soundscape / WES-05 Badmiton, Ping Pong, Swords +cddb/misc +Badminton Racquet (battledore) Hits Birdie (shuttlecock) In Serve +Badminton Racquet (battledore) Hits Birdie (shuttlecock) In Serve +Badminton Racquet Rim Hit Serve +Badminton Soft Shuttlecock Return +Badminton Soft Shuttlecock Return +Badminton Soft Shuttlecock Return +Badminton Soft Shuttlecock Return +Badminton Hard Shuttlecock Return +Badminton Hard Shuttlecock Return +Badminton Hard Shuttlecock Return +Badminton Battledore Rim Hit +Badminton Battledore Rim Hit +Badminton Battledore Swing With Whoosh +Badminton Battledore Swing With Whoosh +Badminton Battledore Dropped Onto Another Battledore +Badminton Shuttlecock Hits Floor +Badminton Shuttlecock Hits Floor +Ping Pong Ball Serve +Ping Pong Ball Serve +Ping Pong Serve With Ball Hitting Table Twice Then Return Hit +Ping Pong Serve With Ball Hitting Table Twice Then Return Hit +Ping Pong Serve With Ball Hitting Table Twice Then Return Hit +Ping Pong Served With Sandpaper Paddle Ball Hits Table Once Then Returned +Ping Pong Serve With Ball Hitting Table Twice Then Return Hit +Ping Pong Ball Return With Sandpaper Paddle Hits Table Twice And Then Returned +Ping Pong Sandpaper Paddle Hits Ball +Ping Pong Ball Bounced With Hand (bounces Used Before Serves) +Ping Pong Single Ball Bounce +Ping Pong Single Ball Bounce +Ping Pong Single Ball Bounce And Then Caught +Ping Pong Single Ball Bounce And Then Caught +Ping Pong Ball Bounces On Table Twice Then Off Table +Ping Pong Ball Being Bounced On Paddle +Ping Pong Ball Being Bounced On Paddle +Ping Pong Ball Bounces On Table With Paddle +Ping Pong Ball Bounces And Rolls To A Stop +Ping Pong Bouncing Ball On Table Stopped With Paddle +Ping Pong Bouncing Ball On Table Stopped With Paddle +Ping Pong Ball Drop And Then Bounces Until Stop +Ping Pong Ball Drop And Then Bounces Until Stop +Ping Pong Ball Drop And Then Bounces Until Stop +Ping Pong Ball Drop And Then Bounces Until Stop +Ping Pong Ball Drop And Then Bounces Until Stop +Ping Pong Ball Spinning On Table +Ping Pong Ball Roll +Ping Pong Ball Roll +Ping Pong Ball Catch +Ping Pong Ball Pick Up With Slight Slide +Ping Pong Ball Pick Up With Small Bounce +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Being Pick Up From Table +Ping Pong Paddle Dropped On Table +Ping Pong Paddle Set Down On Table +Ping Pong Paddle Set Down On Table +Ping Pong Paddle Tossed On Table +Ping Pong Paddle Thrown On Table +Ping Pong Paddle Slammed Down On Table +Ping Pong Ball Hit Back And Forth Several Times +Ping Pong Ball Hit Back And Forth Several Times +Ping Pong Ball Hit Back And Forth Several Times +Ping Pong Ball Hit Back And Forth Few Times +Ping Pong Ball Hit Back And Forth Several Times +Ping Pong Ball Hit Back And Forth Several Times +Ping Pong Ball Hit Back And Forth Few Times +Swords Kendo Bamboo Swords Rubbing +Swords Kendo Bamboo Swords Hit +Swords Kendo Bamboo Swords Hit +Swords Kendo Bamboo Swords Hit Two Times +Swords Kendo Bamboo Swords Hit Three Times +Swords Kendo Bamboo Swords Hit Four Times +Swords Kendo Bamboo Swords Hit Five Times +Swords Kendo Bamboo Sword Combination Strikes And Blocks +Swords Kendo Bamboo Sword Combination Strikes And Blocks +Swords Broad Swords Rubbing +Swords Broad Swords Rubbing +Swords Broad Swords Rubbing +Swords Broad Swords Clanging +Swords Broad Swords Bounce Hit +Swords Broad Swords Hit +Swords Broad Swords Hit +Swords Broad Swords Hit Two Times +Swords Broad Swords Hit Three Times +Swords Broad Swords Hit Four Times +Swords Broad Swords Hit Five Times +Swords Metal Swords Rubbing +Swords Metal Swords Rubbing +Swords Metal Swords Rubbing +Swords Metal Swords Clanging +Swords Metal Swords Hit And Clangs +Swords Metal Swords Hit +Swords Metal Swords Hit Two Times +Swords Metal Swords Hit Three Times +Swords Metal Swords Hit Four Times +Swords Metal Swords Hit Five Times + diff --git a/version5/sample/xmlfiles/70037c08.xml b/version5/sample/xmlfiles/70037c08.xml new file mode 100644 index 0000000..4306d73 --- /dev/null +++ b/version5/sample/xmlfiles/70037c08.xml @@ -0,0 +1,16 @@ + + +70037c08 +894 +Snuff / Oishie Deh +cddb/newage + ID3G: 43 +Yuki +Romeo and Juliet +Icy '97 +Umpan Man +Ambassador +Fishy +War March +Angel Attack + diff --git a/version5/sample/xmlfiles/70040b08.xml b/version5/sample/xmlfiles/70040b08.xml new file mode 100644 index 0000000..71dfd1a --- /dev/null +++ b/version5/sample/xmlfiles/70040b08.xml @@ -0,0 +1,15 @@ + + +70040b08 +1037 +The Beach Boys / Beach Boys Greatest Hits +cddb/rock +Judy +Surfin' +Surfin' Safari +Luau +Barbie +Karate +Surfer Girl +What Is A Young Girl Make Of + diff --git a/version5/sample/xmlfiles/70044808.xml b/version5/sample/xmlfiles/70044808.xml new file mode 100644 index 0000000..23aa593 --- /dev/null +++ b/version5/sample/xmlfiles/70044808.xml @@ -0,0 +1,16 @@ + + +70044808 +1098 +Champion / Come Out Swinging +cddb/rock +Phyte Records +Come Out Swingintro +Harrison and Broadway +Assume the Worst +The Insider +Left Your Mark +A Thank You Note +1 to 2 +(Hidden) + diff --git a/version5/sample/xmlfiles/70045109.xml b/version5/sample/xmlfiles/70045109.xml new file mode 100644 index 0000000..a97d324 --- /dev/null +++ b/version5/sample/xmlfiles/70045109.xml @@ -0,0 +1,16 @@ + + +70045109 +1107 +MxPx / Renaissance E.P. +cddb/rock +Lonesome Town +Letting Go +Party II (Time To Go) +Time Will Tell +The Opposite +Don't Look Back +Talk Of The Town +The Struggle +Yuri Wakes Up Screaming + diff --git a/version5/sample/xmlfiles/70045e09.xml b/version5/sample/xmlfiles/70045e09.xml new file mode 100644 index 0000000..3f73fac --- /dev/null +++ b/version5/sample/xmlfiles/70045e09.xml @@ -0,0 +1,16 @@ + + +70045e09 +1120 +Heimir Björgúlfsson / the opposite +cddb/misc +1 +2 +3 +4 +5 +6 +7 +8 +9 + diff --git a/version5/sample/xmlfiles/70046709.xml b/version5/sample/xmlfiles/70046709.xml new file mode 100644 index 0000000..daa96e8 --- /dev/null +++ b/version5/sample/xmlfiles/70046709.xml @@ -0,0 +1,16 @@ + + +70046709 +1129 +al Score) +cddb/soundtrack +Arabian Nights, Reprise #1 +Babkak, Omar, Alladin, Kassim +Arabian Nights, Reprise #2 +Friend Like Me +Proud Of Your Boy +How Quick they Forget +Arabian Nights, Reprise #3 +High Adventure +Arabian Nights, Reprise #4 + diff --git a/version5/sample/xmlfiles/7004920a.xml b/version5/sample/xmlfiles/7004920a.xml new file mode 100644 index 0000000..82d4efb --- /dev/null +++ b/version5/sample/xmlfiles/7004920a.xml @@ -0,0 +1,18 @@ + + +7004920a +1172 +Nur Immer Sauber / Kuwait On Fire +cddb/misc +| p.shah | m.fischer | c.steinmann | zurich 2001 | +Killing An Arab +Killing Another Arab +Azzuro (Blue Sky Over Kuwait) +Kuwait On Fire +Friday Night In Kuwait City +Saturday Night In Kuwait City +The Day After +Another Night +Domestic Workers Union +More Workers + diff --git a/version5/src/btreeinfo.cpp b/version5/src/btreeinfo.cpp new file mode 100644 index 0000000..60f3d5c --- /dev/null +++ b/version5/src/btreeinfo.cpp @@ -0,0 +1,823 @@ +//------------------------------------------------------------------------------ +// Desc: Class for gathering b-tree information. +// +// Tabs: 3 +// +// Copyright (c) 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: btreeinfo.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Collect information about a block. +****************************************************************************/ +RCODE F_BTreeInfo::collectBlockInfo( + F_Db * pDb, + LFILE * pLFile, + BTREE_INFO * pBTreeInfo, + F_BTREE_BLK_HDR * pBlkHdr, + IXD * pIxd) +{ + RCODE rc = NE_XFLM_OK; + XFLM_BTREE_LEVEL_INFO * pLevelInfo = &pBTreeInfo->levelInfo [m_uiCurrLevel]; + FLMUINT uiLoop; + FLMBYTE * pucOffset; + FLMBYTE * pucEntry; + FLMBYTE * pucTmp; + FLMUINT uiKeyLen; + FLMUINT uiDataLen; + F_CachedBlock * pCachedBlock = NULL; + FLMUINT uiBlkAddr; + FLMUINT uiOADataLen; + FLMBYTE * pucKey; + + // Block level better be the same as our current level we are + // supposedly processing. + + flmAssert( (FLMUINT)pBlkHdr->ui8BlkLevel == m_uiCurrLevel); + + pLevelInfo->ui64BlockCount++; + pLevelInfo->ui64BlockLength += (FLMUINT64)m_uiBlockSize; + pLevelInfo->ui64ElmOffsetOverhead += ((FLMUINT64)pBlkHdr->ui16NumKeys * 2); + pLevelInfo->ui64ElmCount += (FLMUINT64)pBlkHdr->ui16NumKeys; + pLevelInfo->ui64BlockFreeSpace += pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + + // Traverse through each key. + + pucOffset = (FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr); + for (uiLoop = 0; + uiLoop < (FLMUINT)pBlkHdr->ui16NumKeys; + uiLoop++, pucOffset += 2) + { + pucEntry = ((FLMBYTE *)pBlkHdr) + FB2UW( pucOffset); + uiDataLen = 0; + uiOADataLen = 0; + uiKeyLen = 0; + switch (pBlkHdr->stdBlkHdr.ui8BlkType) + { + case BT_LEAF: + + // Elements are: + // Key Length - 2 bytes + // Key - Key Length bytes + + uiKeyLen = (FLMUINT)FB2UW( pucEntry); + pLevelInfo->ui64ElmKeyLengthOvhd += 2; + pucKey = pucEntry + 2; + break; + case BT_NON_LEAF: + + // Elements are: + // Child Blk Address - 4 bytes + // Key Length - 2 bytes + // Key - Key Length bytes + + pLevelInfo->ui64ElmChildAddrsOvhd += 4; + uiKeyLen = (FLMUINT)FB2UW( pucEntry + 4); + pLevelInfo->ui64ElmKeyLengthOvhd += 2; + pucKey = pucEntry + 6; + break; + case BT_NON_LEAF_COUNTS: + + // Elements are: + // Child Block Address - 4 bytes + // Counts - 4 bytes + // Key Length - 2 bytes + // Key - Key Length bytes + + uiKeyLen = (FLMUINT)FB2UW( pucEntry + 8); + pLevelInfo->ui64ElmKeyLengthOvhd += 2; + pLevelInfo->ui64ElmCountsOvhd += 4; + pLevelInfo->ui64ElmChildAddrsOvhd += 4; + pucKey = pucEntry + 10; + break; + case BT_LEAF_DATA: + + // Elements are: + // Flags - 1 byte + // Key Length - 1 or 2 bytes + // Data Length - 1 or 2 bytes + // Overall data Length - 0 or 4 bytes + // Key - Key Length bytes + // Data - Data Length bytes. NOTE: May be a four byte blk + // address if data is stored in data-only blocks. + + pLevelInfo->ui64ElmFlagOvhd++; + if (!(*pucEntry & BTE_FLAG_FIRST_ELEMENT)) + { + pLevelInfo->ui64ContElmCount++; + } + pucTmp = pucEntry + 1; + if (bteKeyLenFlag( pucEntry)) + { + // Two byte key length + + uiKeyLen = (FLMUINT)FB2UW( pucTmp); + pLevelInfo->ui64ElmKeyLengthOvhd += 2; + pucTmp += 2; + } + else + { + + // One byte key length + + uiKeyLen = (FLMUINT)(*pucTmp); + pLevelInfo->ui64ElmKeyLengthOvhd++; + pucTmp++; + } + + if (bteDataLenFlag( pucEntry)) + { + + // Two byte data length + + uiDataLen = (FLMUINT)FB2UW( pucTmp); + pLevelInfo->ui64ElmDataLenOvhd += 2; + pucTmp += 2; + } + else + { + + // One byte data length. + + uiDataLen = (FLMUINT)(*pucTmp); + pLevelInfo->ui64ElmDataLenOvhd++; + pucTmp++; + } + + // Check for the presence of the OverallDataLength field (4 bytes). + + if (bteOADataLenFlag( pucEntry)) + { + uiOADataLen = (FLMUINT)FB2UD( pucTmp); + pLevelInfo->ui64ElmOADataLenOvhd += 4; + pucTmp += 4; + } + pucKey = pucTmp; + + if (bteDataBlockFlag( pucEntry)) + { + flmAssert( uiDataLen == 4); + flmAssert( uiOADataLen); + + // Skip over the key to get to the data only block address. + + pucTmp += uiKeyLen; + uiBlkAddr = (FLMUINT)FB2UD( pucTmp); + while (uiBlkAddr) + { + if (RC_BAD( pDb->m_pDatabase->getBlock( pDb, pLFile, + uiBlkAddr, NULL, &pCachedBlock))) + { + goto Exit; + } + + // Block better be a data-only block. + + flmAssert( pCachedBlock->m_pBlkHdr->ui8BlkType == BT_DATA_ONLY); + pLevelInfo->ui64DataOnlyBlockCount++; + pLevelInfo->ui64DataOnlyBlockLength += (FLMUINT64)m_uiBlockSize; + pLevelInfo->ui64DataOnlyBlockFreeSpace += + (FLMUINT64)pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + + // Subtract from uiIODataLen - should go to exactly + // zero by the time we leave this loop. + + uiOADataLen -= (m_uiBlockSize - sizeofDOBlkHdr( pCachedBlock->m_pBlkHdr) - + pCachedBlock->m_pBlkHdr->ui16BlkBytesAvail); + + uiBlkAddr = (FLMUINT)pCachedBlock->m_pBlkHdr->ui32NextBlkInChain; + + ScaReleaseCache( pCachedBlock, FALSE); + pCachedBlock = NULL; + } + + // Better have accounted for the exact amount of data that + // was given in the overall data length field. + + flmAssert( !uiOADataLen); + } + break; + + default: + pucKey = NULL; + flmAssert( 0); + break; + } + if (uiKeyLen) + { + pLevelInfo->ui64ElmKeyLength += (FLMUINT64)uiKeyLen; + } + if (uiDataLen) + { + pLevelInfo->ui64ElmDataLength += (FLMUINT64)uiDataLen; + } + + // If this is an index, parse the key. + + if (pIxd && uiKeyLen) + { + FLMUINT uiComponentLen; + FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; + ICD * pIcd; + + pIcd = pIxd->pFirstKey; + while (pIcd) + { + flmAssert( pucKey + 2 <= pucKeyEnd); + uiComponentLen = getKeyComponentLength( pucKey); + if (uiComponentLen == KEY_HIGH_VALUE || + uiComponentLen == KEY_LOW_VALUE) + { + uiComponentLen = 0; + } + pLevelInfo->ui64KeyComponentLengthsSize += 2; + if (uiComponentLen) + { + pLevelInfo->ui64KeyDataSize += (FLMUINT64)uiComponentLen; + } + pucKey += (2 + uiComponentLen); + pIcd = pIcd->pNextKeyComponent; + } + pLevelInfo->ui64KeyIdSize += (FLMUINT64)(pucKeyEnd - pucKey); + } + } + +Exit: + + if (pCachedBlock) + { + ScaReleaseCache( pCachedBlock, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Collect information on a b-tree. +****************************************************************************/ +RCODE F_BTreeInfo::collectBTreeInfo( + F_Db * pDb, + LFILE * pLFile, + BTREE_INFO * pBTreeInfo, + IXD * pIxd) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNameBufSize; + FLMUINT uiLeftBlocks [MAX_LEVELS]; + F_Database * pDatabase = pDb->m_pDatabase; + F_CachedBlock * pCachedBlock = NULL; + F_BTREE_BLK_HDR * pBlkHdr; + const char * pszName = NULL; + + m_uiBlockSize = pDatabase->m_uiBlockSize; + + // Get the size of buffer needed to hold the name of the b-tree. + + uiNameBufSize = 0; + if (pIxd) + { + switch (pLFile->uiLfNum) + { + case XFLM_DICT_NUMBER_INDEX: + pszName = "DictNumberIx"; + break; + case XFLM_DICT_NAME_INDEX: + pszName = "DictNameIx"; + break; + default: + if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( + pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, + pLFile->uiLfNum, NULL, NULL, &uiNameBufSize, + NULL, NULL, NULL, NULL, TRUE))) + { + goto Exit; + } + break; + } + } + else + { + switch (pLFile->uiLfNum) + { + case XFLM_MAINT_COLLECTION: + pszName = "MaintCollection"; + break; + case XFLM_DATA_COLLECTION: + pszName = "DataCollection"; + break; + case XFLM_DICT_COLLECTION: + pszName = "DictCollection"; + break; + default: + if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( + pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, + pLFile->uiLfNum, NULL, NULL, &uiNameBufSize, + NULL, NULL, NULL, NULL, TRUE))) + { + goto Exit; + } + break; + } + } + + if (pszName) + { + // Allocate a name buffer. + + uiNameBufSize = f_strlen( pszName) + 1; + if (RC_BAD( rc = m_pool.poolAlloc( uiNameBufSize, (void **)(&pBTreeInfo->pszLfName)))) + { + goto Exit; + } + f_strcpy( pBTreeInfo->pszLfName, pszName); + } + else + { + + // Allocate a name buffer. + + uiNameBufSize++; + if (RC_BAD( rc = m_pool.poolAlloc( uiNameBufSize, (void **)(&pBTreeInfo->pszLfName)))) + { + goto Exit; + } + + // Get the name of the b-tree. + + if (RC_BAD( rc = pDb->m_pDict->m_pNameTable->getFromTagTypeAndNum( + pDb, pIxd ? ELM_INDEX_TAG : ELM_COLLECTION_TAG, + pLFile->uiLfNum, NULL, pBTreeInfo->pszLfName, &uiNameBufSize, + NULL, NULL, NULL, NULL, TRUE))) + { + goto Exit; + } + } + m_uiCurrLfNum = pLFile->uiLfNum; + m_bIsCollection = pIxd ? FALSE : TRUE; + m_pszCurrLfName = pBTreeInfo->pszLfName; + + // Reset the information for the b-tree. uiLfNum should have already + // been set by the caller. + + flmAssert( pBTreeInfo->uiLfNum == pLFile->uiLfNum); + pBTreeInfo->uiNumLevels = 0; + f_memset( &pBTreeInfo->levelInfo [0], 0, sizeof( pBTreeInfo->levelInfo)); + + // Read the root block to see how many levels are in the b-tree. + + if (RC_BAD( pDatabase->getBlock( pDb, pLFile, pLFile->uiRootBlk, + NULL, &pCachedBlock))) + { + goto Exit; + } + pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; + + pBTreeInfo->uiNumLevels = pBlkHdr->ui8BlkLevel + 1; + flmAssert( pBTreeInfo->uiNumLevels <= MAX_LEVELS); + + // Better be a root block, and better not have a prev and next + // block address. + + flmAssert( isRootBlk( pBlkHdr)); + flmAssert( pBlkHdr->stdBlkHdr.ui32BlkAddr == (FLMUINT32)pLFile->uiRootBlk); + flmAssert( !pBlkHdr->stdBlkHdr.ui32PrevBlkInChain); + flmAssert( !pBlkHdr->stdBlkHdr.ui32NextBlkInChain); + m_uiCurrLevel = pBlkHdr->ui8BlkLevel; + + // Gather information for the root block. + + if (RC_BAD( rc = collectBlockInfo( pDb, pLFile, pBTreeInfo, + pBlkHdr, pIxd))) + { + goto Exit; + } + m_ui64CurrLfBlockCount = 1; + m_ui64CurrLevelBlockCount = 1; + m_ui64TotalBlockCount = 1; + if (RC_BAD( rc = doCallback())) + { + goto Exit; + } + + // Get all of the leftmost blocks in the b-tree. + + uiLeftBlocks [pBlkHdr->ui8BlkLevel] = pLFile->uiRootBlk; + if (!pBlkHdr->ui8BlkLevel) + { + flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF || + pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA); + } + else + { + FLMUINT uiLevel; + FLMBYTE * pucEntry; + FLMUINT uiBlkAddr; + + // Gather up the leftmost blocks + + uiLevel = pBlkHdr->ui8BlkLevel; + while (uiLevel) + { + + uiLevel--; + + // Get the left-most down-block pointer - which is the first one + // in the array. + + pucEntry = ((FLMBYTE *)pBlkHdr) + sizeofBTreeBlkHdr( pBlkHdr); + pucEntry = ((FLMBYTE *)pBlkHdr) + FB2UW( pucEntry); + uiLeftBlocks [uiLevel] = (FLMUINT)FB2UD( pucEntry); + + // Get the leftmost block at the next level down in the b-tree + + ScaReleaseCache( pCachedBlock, FALSE); + pCachedBlock = NULL; + if (RC_BAD( pDatabase->getBlock( pDb, pLFile, + uiLeftBlocks [uiLevel], NULL, &pCachedBlock))) + { + goto Exit; + } + pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; + + // If we are at level zero, we better be on a leaf block. + + if (!uiLevel) + { + flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF || + pBlkHdr->stdBlkHdr.ui8BlkType == BT_LEAF_DATA); + } + } + + // Now do each level of the b-tree. Have already done the root + // block, so we start one level down from it. + + m_uiCurrLevel = pBTreeInfo->uiNumLevels - 2; + for (;;) + { + uiBlkAddr = uiLeftBlocks [m_uiCurrLevel]; + + m_ui64CurrLevelBlockCount = 0; + while (uiBlkAddr) + { + if (pCachedBlock) + { + ScaReleaseCache( pCachedBlock, FALSE); + pCachedBlock = NULL; + } + if (RC_BAD( pDatabase->getBlock( pDb, pLFile, uiBlkAddr, + NULL, &pCachedBlock))) + { + goto Exit; + } + pBlkHdr = (F_BTREE_BLK_HDR *)pCachedBlock->m_pBlkHdr; + + // Gather information for the block. + + if (RC_BAD( rc = collectBlockInfo( pDb, pLFile, pBTreeInfo, + pBlkHdr, pIxd))) + { + goto Exit; + } + m_ui64CurrLfBlockCount++; + m_ui64CurrLevelBlockCount++; + m_ui64TotalBlockCount++; + if (RC_BAD( rc = doCallback())) + { + goto Exit; + } + + // Go to the next block in the chain. + + uiBlkAddr = pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + } + + if (!m_uiCurrLevel) + { + break; + } + + // Go down to the next level in the b-tree. + + m_uiCurrLevel--; + } + } + +Exit: + + if (pCachedBlock) + { + ScaReleaseCache( pCachedBlock, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Collect b-tree information for an index. If uiIndexNum is zero, + collect b-tree information for ALL indexes. + If we already have information on the index, we will clear the + information and get it again. +****************************************************************************/ +RCODE XFLMAPI F_BTreeInfo::collectIndexInfo( + IF_Db * ifpDb, + FLMUINT uiIndexNum, + IF_BTreeInfoStatus * pInfoStatus) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + BTREE_INFO * pIndexInfo; + IXD * pIxd; + FLMUINT uiLoop; + + // Start a read transaction, if no other transaction is going. + + if (pDb->getTransType() == XFLM_NO_TRANS) + { + if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + m_pInfoStatus = pInfoStatus; + m_uiCurrLfNum = 0; + m_bIsCollection = FALSE; + m_pszCurrLfName = NULL; + m_uiCurrLevel = 0; + m_ui64CurrLfBlockCount = 0; + m_ui64CurrLevelBlockCount = 0; + m_ui64TotalBlockCount = 0; + + if (!uiIndexNum) + { + m_uiNumIndexes = 0; + for (;;) + { + if ((pIxd = pDb->m_pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + + if (RC_BAD( rc = collectIndexInfo( ifpDb, uiIndexNum, pInfoStatus))) + { + goto Exit; + } + } + } + else + { + // See if we can find the b-tree already in our list. + + uiLoop = 0; + pIndexInfo = m_pIndexArray; + while (uiLoop < m_uiNumIndexes && pIndexInfo->uiLfNum != uiIndexNum) + { + uiLoop++; + pIndexInfo++; + } + if (uiLoop == m_uiNumIndexes) + { + pIndexInfo = NULL; + } + + // See if the index is defined in the database + + if (RC_BAD( rc = pDb->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + if (rc == NE_XFLM_BAD_IX) + { + rc = NE_XFLM_OK; + + // If we previously had the index, remove it from the array. + + if (pIndexInfo) + { + if (uiLoop < m_uiNumIndexes - 1) + { + f_memmove( pIndexInfo, &pIndexInfo[ 1], + sizeof( BTREE_INFO) * (m_uiNumIndexes - uiLoop - 1)); + } + m_uiNumIndexes--; + } + } + goto Exit; + } + + // If we previously had the index, reset its information. + // Otherwise, create a new index info structure and + // add it to the array. + + if (!pIndexInfo) + { + + // Allocate space for a new index info structure in the array. + + if (m_uiNumIndexes == m_uiIndexArraySize) + { + if (RC_BAD( rc = f_realloc( sizeof( BTREE_INFO) * (m_uiIndexArraySize + 5), + &m_pIndexArray))) + { + goto Exit; + } + m_uiIndexArraySize += 5; + } + pIndexInfo = &m_pIndexArray [m_uiNumIndexes]; + pIndexInfo->uiLfNum = uiIndexNum; + m_uiNumIndexes++; + } + + // Get the index information + + if (RC_BAD( rc = collectBTreeInfo( pDb, &pIxd->lfInfo, pIndexInfo, pIxd))) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Collect b-tree information for a collection. If uiCollectionNum is + zero, collect b-tree information for ALL collections. + + If we already have information on the collection, we will clear the + information and get it again. +****************************************************************************/ +RCODE XFLMAPI F_BTreeInfo::collectCollectionInfo( + IF_Db * ifpDb, + FLMUINT uiCollectionNum, + IF_BTreeInfoStatus * pInfoStatus) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + BTREE_INFO * pCollectionInfo; + F_COLLECTION * pCollection; + FLMUINT uiLoop; + + // Start a read transaction, if no other transaction is going. + + if (pDb->getTransType() == XFLM_NO_TRANS) + { + if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + m_pInfoStatus = pInfoStatus; + m_uiCurrLfNum = 0; + m_bIsCollection = FALSE; + m_pszCurrLfName = NULL; + m_uiCurrLevel = 0; + m_ui64CurrLfBlockCount = 0; + m_ui64CurrLevelBlockCount = 0; + m_ui64TotalBlockCount = 0; + + if (!uiCollectionNum) + { + m_uiNumCollections = 0; + for (;;) + { + if ((pCollection = pDb->m_pDict->getNextCollection( uiCollectionNum, TRUE)) == NULL) + { + break; + } + uiCollectionNum = pCollection->lfInfo.uiLfNum; + + if (RC_BAD( rc = collectCollectionInfo( ifpDb, uiCollectionNum, pInfoStatus))) + { + goto Exit; + } + } + } + else + { + // See if we can find the b-tree already in our list. + + uiLoop = 0; + pCollectionInfo = m_pCollectionArray; + while (uiLoop < m_uiNumCollections && pCollectionInfo->uiLfNum != uiCollectionNum) + { + uiLoop++; + pCollectionInfo++; + } + if (uiLoop == m_uiNumCollections) + { + pCollectionInfo = NULL; + } + + // See if the index is defined in the database + + if (RC_BAD( rc = pDb->m_pDict->getCollection( uiCollectionNum, &pCollection, TRUE))) + { + if (rc == NE_XFLM_BAD_COLLECTION) + { + rc = NE_XFLM_OK; + + // If we previously had the index, remove it from the array. + + if (pCollectionInfo) + { + if (uiLoop < m_uiNumCollections - 1) + { + f_memmove( pCollectionInfo, &pCollectionInfo[ 1], + sizeof( BTREE_INFO) * (m_uiNumCollections - uiLoop - 1)); + } + m_uiNumCollections--; + } + } + goto Exit; + } + + // If we previously had the index, reset its information. + // Otherwise, create a new index info structure and + // add it to the array. + + if (!pCollectionInfo) + { + + // Allocate space for a new index info structure in the array. + + if (m_uiNumCollections == m_uiCollectionArraySize) + { + if (RC_BAD( rc = f_realloc( sizeof( BTREE_INFO) * (m_uiCollectionArraySize + 5), + &m_pCollectionArray))) + { + goto Exit; + } + m_uiCollectionArraySize += 5; + } + pCollectionInfo = &m_pCollectionArray [m_uiNumCollections]; + pCollectionInfo->uiLfNum = uiCollectionNum; + m_uiNumCollections++; + } + + // Get the index information + + if (RC_BAD( rc = collectBTreeInfo( pDb, &pCollection->lfInfo, + pCollectionInfo, NULL))) + { + goto Exit; + } + } + +Exit: + + if (bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Create an empty b-tree info. object and return it's interface... +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::createIFBTreeInfo( + IF_BTreeInfo ** ppBTreeInfo) +{ + RCODE rc = NE_XFLM_OK; + + if ((*ppBTreeInfo = f_new F_BTreeInfo) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + diff --git a/version5/src/checksum.cpp b/version5/src/checksum.cpp new file mode 100644 index 0000000..fc3ac85 --- /dev/null +++ b/version5/src/checksum.cpp @@ -0,0 +1,753 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the routine which calculates a block checksum. +// +// 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: checksum.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#if (defined( FLM_WIN) && !defined( FLM_64BIT)) || defined( FLM_NLM) + +static unsigned long gv_mmxCheckSumFlag = 1; + +#if defined( FLM_WATCOM_NLM) + + extern void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + extern void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + extern unsigned long GetMMXSupported(void); + +#else + + static void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + static void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes); + + static unsigned long GetMMXSupported(void); + +#endif + +/******************************************************************** +Desc: Returns 1 if the CPU supports MMX +Ret: 0 or 1 if CPU supports MMX +*********************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux GetMMXSupported parm; + #pragma aux GetMMXSupported = \ + 0xB8 0x01 0x00 0x00 0x00 /* mov eax, 1 */\ + 0x0F 0xA2 /* CPUID */\ + 0x33 0xC0 /* xor eax, eax */\ + 0xF7 0xC2 0x00 0x00 0x80 0x00 /* test edx, (1 SHL 23) ;check for MMX Instruction support */\ + 0x0F 0x95 0xC0 /* setnz al */\ + modify exact [EAX EBX ECX EDX]; + +#else + + unsigned long GetMMXSupported( void) + { + unsigned long bMMXSupported; + __asm + { + mov eax, 1 + cpuid + xor eax, eax + test edx, (1 SHL 23) + setnz al + mov bMMXSupported, eax + } + return bMMXSupported; + } + +#endif + +/******************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using MMX instructions. +*********************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux FastBlockCheckSumMMX parm [ESI] [eax] [ebx] [ecx]; + #pragma aux FastBlockCheckSumMMX = \ + 0x50 /* push eax ;save the sum pointer */\ + 0x53 /* push ebx ;save the xor pointer */\ + 0x8B 0x10 /* mov edx, [eax] ;for local add */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x8B 0x1B /* mov ebx, [ebx] ;for local xor */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x8B 0xF9 /* mov edi, ecx ;save the amount to copy */\ + 0x83 0xF9 0x20 /* cmp ecx, 32 ;see if we have enough for the big loop */\ + 0x0F 0x82 0x63 0x00 0x00 0x00 /* jb #MediumStuff */\ + 0xC1 0xE9 0x05 /* shr ecx, 5 ;convert length to 32 byte blocks */\ + 0x83 0xE7 0x1F /* and edi, 01fh ;change saved length to remainder */\ + 0x0F 0xEF 0xED /* pxor mm5, mm5 ;wasted space to 16 byte align the upcoming loop */\ + 0x0F 0x6E 0xE2 /* movd mm4,edx */\ + 0x0F 0x6E 0xEB /* movd mm5,ebx */\ + /* #BigStuffLoop: */\ + /* ;load up mm0 - mm3 with 8 bytes each of data. */\ + 0x0F 0x6F 0x06 /* movq mm0, [esi] */\ + 0x0F 0x6F 0x4E 0x08 /* movq mm1, [esi + 8] */\ + 0x0F 0x6F 0x56 0x10 /* movq mm2, [esi + 16] */\ + 0x0F 0x6F 0x5E 0x18 /* movq mm3, [esi + 24] */\ + 0x83 0xC6 0x20 /* add esi, 32 ;move the data pointer ahead 32 */\ + /* ;add mm0 - mm3 to mm4 */\ + /* ;xor mm0 - mm3 with mm5 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0xEF 0xE8 /* pxor mm5, mm0 */\ + 0x0F 0xFC 0xE1 /* paddb mm4, mm1 */\ + 0x0F 0xEF 0xE9 /* pxor mm5, mm1 */\ + 0x0F 0xFC 0xE2 /* paddb mm4, mm2 */\ + 0x0F 0xEF 0xEA /* pxor mm5, mm2 */\ + 0x0F 0xFC 0xE3 /* paddb mm4, mm3 */\ + 0x0F 0xEF 0xEB /* pxor mm5, mm3 */\ + 0x49 /* dec ecx ;see if there is more to do */\ + 0x75 0xD3 /* jnz #BigStuffLoop */\ + /* ;mm4 contains the sum to this point */\ + /* ;mm5 contains the xor to this point */\ + /* ;edi contains the bytes left */\ + /* ;esi points to data left to do */\ + /* ;extract the xor value from mm5 and put it in ebx */\ + 0x0F 0x7E 0xEB /* movd ebx, mm5 */\ + 0x0F 0x73 0xD5 0x20 /* psrlq mm5, 32 */\ + 0x0F 0x7E 0xE8 /* movd eax, mm5 */\ + 0x33 0xD8 /* xor ebx, eax */\ + /* ;extract the sum value from mm4 and put it in dl & dh */\ + 0x0F 0x6F 0xC4 /* movq mm0, mm4 */\ + 0x0F 0x73 0xD0 0x20 /* psrlq mm0, 32 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0x6F 0xC4 /* movq mm0, mm4 */\ + 0x0F 0x73 0xD0 0x10 /* psrlq mm0, 16 */\ + 0x0F 0xFC 0xE0 /* paddb mm4, mm0 */\ + 0x0F 0x7E 0xE2 /* movd edx, mm4 */\ + 0x0F 0x77 /* emms ;end of MMX stuff */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #MediumStuff: */\ + 0x83 0xF9 0x04 /* cmp ecx, 4 */\ + 0x0F 0x82 0x1D 0x00 0x00 0x00 /* jb #SmallStuff */\ + 0xC1 0xE9 0x02 /* shr ecx, 2 */\ + 0x83 0xE7 0x03 /* and edi, 3 */\ + /* #DSSumLoop: */\ + 0x8B 0x06 /* mov eax, [esi] */\ + 0x83 0xC6 0x04 /* add esi, 4 */\ + 0x33 0xD8 /* xor ebx, eax */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0x49 /* dec ecx */\ + 0x75 0xEB /* jnz #DSSumLoop */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #SmallStuff: */\ + 0x02 0xD6 /* add dl, dh ;get complete sum in dl */\ + 0x8B 0xC3 /* mov eax, ebx ;get complete xor in bl*/\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x66 0x33 0xD8 /* xor bx, ax */\ + 0x32 0xDF /* xor bl, bh */\ + 0x83 0xF9 0x00 /* cmp ecx, 0 ;see if anything left to do - 3 or less bytes */\ + 0x0F 0x84 0x0A 0x00 0x00 0x00 /* jz #Done */\ + /* #SmallStuffLoop: */\ + 0x8A 0x06 /* mov al, [esi] */\ + 0x46 /* inc esi */\ + 0x02 0xD0 /* add dl, al */\ + 0x32 0xD8 /* xor bl, al */\ + 0x49 /* dec ecx */\ + 0x75 0xF6 /* jnz #SmallStuffLoop */\ + /* #Done: */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x58 /* pop eax */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x5F /* pop edi */\ + 0x89 0x18 /* mov [eax], ebx */\ + 0x89 0x17 /* mov [edi], edx */\ + parm [ESI] [eax] [ebx] [ecx] \ + modify exact [eax ebx ecx edx ESI EDI]; + +#else + + static void FastBlockCheckSumMMX( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes) + { + __asm + { + mov esi, pBlk + + // Load up the starting checksum values into edx (add) and ebx (XOR) + + mov eax, puiChecksum + mov edx, [eax] // Set local add + and edx, 0ffh ;clear unneeded bits + mov eax, puiXORdata + mov ebx, [eax] + and ebx, 0ffh ;clear unneeded bits + mov ecx, uiNumberOfBytes + mov edi, ecx ;save the amount to copy + + cmp ecx, 32 ;see if we have enough for the big loop + jb MediumStuff + + shr ecx, 5 ;convert length to 32 byte blocks + and edi, 01fh ;change saved length to remainder + pxor mm5, mm5 ;wasted space to 16 byte align the upcoming loop - check tHIS.. + + movd mm4, edx ;set ADD + movd mm5, ebx ;set XOR + + BigStuffLoop: + ;load up mm0 - mm3 with 8 bytes each of data. + movq mm0, [esi] + movq mm1, [esi + 8] + movq mm2, [esi + 16] + movq mm3, [esi + 24] + add esi, 32 ;move the data pointer ahead 32 + ;add mm0 - mm3 to mm4 + ;xor mm0 - mm3 with mm5 + paddb mm4, mm0 + pxor mm5, mm0 + paddb mm4, mm1 + pxor mm5, mm1 + paddb mm4, mm2 + pxor mm5, mm2 + paddb mm4, mm3 + pxor mm5, mm3 + dec ecx ;see if there is more to do + jnz BigStuffLoop + ;mm4 contains the sum to this point + ;mm5 contains the xor to this point + ;edi contains the bytes left + ;esi points to data left to do + ;extract the xor value from mm5 and put it in ebx + movd ebx, mm5 + psrlq mm5, 32 + movd eax, mm5 + xor ebx, eax + ;extract the sum value from mm4 and put it in dl & dh + movq mm0, mm4 + psrlq mm0, 32 + paddb mm4, mm0 + movq mm0, mm4 + psrlq mm0, 16 + paddb mm4, mm0 + movd edx, mm4 + emms ;end of MMX stuff + + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + MediumStuff: + cmp ecx, 4 + jb SmallStuff + shr ecx, 2 + and edi, 3 + + DSSumLoop: + mov eax, [esi] + add esi, 4 + xor ebx, eax + add dl, al + add dh, ah + shr eax, 16 + add dl, al + add dh, ah + dec ecx + jnz DSSumLoop + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + SmallStuff: + add dl, dh ;get complete sum in dl + mov eax, ebx ;get complete xor in bl + shr eax, 16 + xor bx, ax + xor bl, bh + cmp ecx, 0 ;see if anything left to do - 3 or less bytes + jz Done + + SmallStuffLoop: + mov al, [esi] + inc esi + add dl, al + xor bl, al + dec ecx + jnz SmallStuffLoop + Done: + and edx, 0ffh ;clear unneeded bits + and ebx, 0ffh ;clear unneeded bits + + // Set the return values. + + mov eax, puiChecksum // Address of add result/start + mov [eax], edx + + mov eax, puiXORdata // Address of xor result/start + mov [eax], ebx + } + return; + } +#endif + +/****************************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using 386 and NOT MMX instructions. +******************************************************************************/ +#if defined( FLM_WATCOM_NLM) + + #pragma aux FastBlockCheckSum386 parm [ESI] [eax] [ebx] [ecx]; + + #pragma aux FastBlockCheckSum386 = \ + 0x50 /* push eax ;save the sum pointer */\ + 0x53 /* push ebx ;save the xor pointer */\ + 0x8B 0x10 /* mov edx, [eax] ;for local add */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x8B 0x1B /* mov ebx, [ebx] ;for local xor */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + /* ;dl contains the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + 0x83 0xF9 0x04 /* cmp ecx, 4 */\ + 0x0F 0x82 0x1F 0x00 0x00 0x00 /* jb #SmallStuff */\ + 0x8B 0xF9 /* mov edi, ecx */\ + 0xC1 0xE9 0x02 /* shr ecx, 2 */\ + 0x83 0xE7 0x03 /* and edi, 3 */\ + /* #DSSumLoop: */\ + 0x8B 0x06 /* mov eax, [esi] */\ + 0x83 0xC6 0x04 /* add esi, 4 */\ + 0x33 0xD8 /* xor ebx, eax */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x02 0xD0 /* add dl, al */\ + 0x02 0xF4 /* add dh, ah */\ + 0x49 /* dec ecx */\ + 0x75 0xEB /* jnz #DSSumLoop */\ + 0x8B 0xCF /* mov ecx, edi ;load up the rest of the length */\ + /* ;dl contains half the sum to this point */\ + /* ;dh contains half the sum to this point */\ + /* ;ebx contains the xor to this point - 32 bits wide. */\ + /* ;ecx contains the bytes still left to do */\ + /* ;esi contains pointer to data to checksum */\ + /* #SmallStuff: */\ + 0x02 0xD6 /* add dl, dh ;get complete sum in dl */\ + 0x8B 0xC3 /* mov eax, ebx ;get complete xor in bl*/\ + 0xC1 0xE8 0x10 /* shr eax, 16 */\ + 0x66 0x33 0xD8 /* xor bx, ax */\ + 0x32 0xDF /* xor bl, bh */\ + 0x83 0xF9 0x00 /* cmp ecx, 0 ;see if anything left to do - 3 or less bytes */\ + 0x0F 0x84 0x0A 0x00 0x00 0x00 /* jz #Done */\ + /* #SmallStuffLoop: */\ + 0x8A 0x06 /* mov al, [esi] */\ + 0x46 /* inc esi */\ + 0x02 0xD0 /* add dl, al */\ + 0x32 0xD8 /* xor bl, al */\ + 0x49 /* dec ecx */\ + 0x75 0xF6 /* jnz #SmallStuffLoop */\ + /* #Done: */\ + 0x81 0xE2 0xFF 0x00 0x00 0x00 /* and edx, 0ffh ;clear unneeded bits */\ + 0x58 /* pop eax */\ + 0x81 0xE3 0xFF 0x00 0x00 0x00 /* and ebx, 0ffh ;clear unneeded bits */\ + 0x5F /* pop edi */\ + 0x89 0x18 /* mov [eax], ebx */\ + 0x89 0x17 /* mov [edi], edx */\ + parm [ESI] [eax] [ebx] [ecx] \ + modify exact [eax ebx ecx edx ESI EDI]; +#else + +static void FastBlockCheckSum386( + void * pBlk, + unsigned long *puiChecksum, + unsigned long *puiXORdata, + unsigned long uiNumberOfBytes) +{ + __asm + { + mov esi, pBlk + + // Load up the starting checksum values into edx (add) and ebx (XOR) + + mov eax, puiChecksum + mov edx, [eax] // Set local add + and edx, 0ffh ;clear unneeded bits + mov eax, puiXORdata + mov ebx, [eax] + and ebx, 0ffh ;clear unneeded bits + mov ecx, uiNumberOfBytes + + ;dl contains the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + cmp ecx, 4 + jb SmallStuff + mov edi, ecx + shr ecx, 2 + and edi, 3 +DSSumLoop: + mov eax, [esi] + add esi, 4 + xor ebx, eax + add dl, al + add dh, ah + shr eax, 16 + add dl, al + add dh, ah + dec ecx + jnz DSSumLoop + mov ecx, edi ;load up the rest of the length + ;dl contains half the sum to this point + ;dh contains half the sum to this point + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum +SmallStuff: + add dl, dh ;get complete sum in dl + mov eax, ebx ;get complete xor in bl + shr eax, 16 + xor bx, ax + xor bl, bh + cmp ecx, 0 ;see if anything left to do - 3 or less bytes + jz Done + +SmallStuffLoop: + mov al, [esi] + inc esi + add dl, al + xor bl, al + dec ecx + jnz SmallStuffLoop +Done: + and edx, 0ffh ;clear unneeded bits + and ebx, 0ffh ;clear unneeded bits + + // Set the return values. + + mov eax, puiChecksum // Address of add result/start + mov [eax], edx + + mov eax, puiXORdata // Address of xor result/start + mov [eax], ebx + } + return; +} +#endif + +/****************************************************************************** +Desc: Performs part of the FLAIM block checksum algorithm + using MMX or 386 instructions. +Note: FastBlockCheckSum will start with the checksum and xordata you + pass in. It assumes that the data is already dword aligned. +******************************************************************************/ + +void FastBlockCheckSum( + void * pBlk, + FLMUINT * puiChecksum, + FLMUINT * puiXORdata, + FLMUINT uiNumberOfBytes) +{ + if( gv_mmxCheckSumFlag == 1) + { + FastBlockCheckSumMMX( (void *) pBlk, (unsigned long *) puiChecksum, + (unsigned long *) puiXORdata, (unsigned long) uiNumberOfBytes); + } + else + { + FastBlockCheckSum386( (void *) pBlk, (unsigned long *) puiChecksum, + (unsigned long *) puiXORdata, (unsigned long) uiNumberOfBytes); + } +} + +/****************************************************************************** +Desc: Sets the global variable to check if MMX instructions are allowed. +******************************************************************************/ +void F_DbSystem::initFastBlockCheckSum( void) +{ + /* NOTE that GetMMXSupported assumes that we are running on at least a + * pentium. The check to see if we are on a pentium requires that we + * modify the flags register, and we can't do that if we are running + * in ring3. Because NetWare 5 - according to our product marketing - + * requires at least a P5 90Mhz, we will be safe. When you port this + * code to NT, you may need to come up with a safe way to see if we + * can do MMX instructions - unless you can assume that even on NT you + * will be on at least a P5. + */ + gv_mmxCheckSumFlag = GetMMXSupported(); +} + +#endif + +/******************************************************************** +Desc: Calculate the checksum for a packet. +*********************************************************************/ +FLMBYTE RflCalcChecksum( + FLMBYTE * pucPacket, + FLMUINT uiPacketBodyLen) +{ + FLMUINT uiBytesToChecksum; + FLMUINT uiChecksum; + FLMBYTE * pucStart; + + // Checksum is calculated for every byte in the packet that + // comes after the checksum byte. + + uiBytesToChecksum = (FLMUINT)(uiPacketBodyLen + + RFL_PACKET_OVERHEAD - + (RFL_PACKET_CHECKSUM_OFFSET + 1)); + + pucStart = &pucPacket [RFL_PACKET_CHECKSUM_OFFSET + 1]; + +#if defined( FLM_WIN) && !defined( FLM_64BIT) + if( gv_mmxCheckSumFlag == 1) + { + __asm + { + mov esi, pucStart + mov ecx, uiBytesToChecksum + mov edi, ecx ;save the amount to copy + pxor mm7, mm7 ;clear the destination register + xor ebx, ebx + + cmp ecx, 32 ;see if we at least 32 bytes + jb Min16 + + shr ecx, 5 ;convert length to 32 byte blocks + and edi, 01fh ;change saved length to remainder + + Min32Loop: + movq mm0, [esi] + movq mm1, [esi + 8] + movq mm2, [esi + 16] + movq mm3, [esi + 24] + add esi, 32 ;move the data pointer ahead 32 + ;xor mm0 - mm3 with mm7 + pxor mm7, mm0 + pxor mm7, mm1 + pxor mm7, mm2 + pxor mm7, mm3 + dec ecx ;see if there is more to do + jnz Min32Loop + ;mm7 contains the xor to this point + ;edi contains the bytes left + ;esi points to data left to do + ;extract the xor value from mm7 and put it in ebx + movd ebx, mm7 + psrlq mm7, 32 + movd eax, mm7 + xor ebx, eax + + cmp edi, 0 + jz SmallStuff + + mov ecx, edi ;load up the rest of the length + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + movd mm7, ebx + + Min16: + cmp ecx, 16 + jb Min4 + + shr ecx, 4 ;convert length to 16 byte blocks + and edi, 0fh ;change saved length to remainder + + Min16Loop: + ;load up mm0 - mm1 with 8 bytes each of data. + movq mm0, [esi] + movq mm1, [esi + 8] + add esi, 16 ;move the data pointer ahead 16 + ;xor mm0 - mm3 with mm7 + pxor mm7, mm0 + pxor mm7, mm1 + dec ecx ;see if there is more to do + jnz Min16Loop + ;mm7 contains the xor to this point + ;edi contains the bytes left + ;esi points to data left to do + ;extract the xor value from mm7 and put it in ebx + movd ebx, mm7 + psrlq mm7, 32 + movd eax, mm7 + xor ebx, eax + + cmp edi, 0 + jz SmallStuff + + mov ecx, edi ;load up the rest of the length + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + + emms ;end of MMX stuff + + Min4: + cmp ecx, 4 + jb SmallStuff + shr ecx, 2 + and edi, 3 + + Min4Loop: + mov eax, [esi] + add esi, 4 + xor ebx, eax + dec ecx + jnz Min4Loop + mov ecx, edi ;load up the rest of the length + ;ebx contains the xor to this point - 32 bits wide. + ;ecx contains the bytes still left to do + ;esi contains pointer to data to checksum + SmallStuff: + mov eax, ebx ;get complete xor in bl + shr eax, 16 + xor bx, ax + xor bl, bh + cmp ecx, 0 ;see if anything left to do - 3 or less bytes + jz Done + + SmallStuffLoop: + mov al, [esi] + inc esi + xor bl, al + dec ecx + jnz SmallStuffLoop + + Done: + and ebx, 0ffh ;clear unneeded bits + + // Set the checksum value + + mov uiChecksum, ebx + } + + return( (FLMBYTE)(uiChecksum ? uiChecksum : 1)); + } +#endif + + FLMBYTE * pucEnd; + FLMBYTE * pucSectionEnd; + FLMBYTE * pucCur; + FLMBYTE ucTmp; + + uiChecksum = 0; + pucCur = pucStart; + pucEnd = pucStart + uiBytesToChecksum; + +#ifdef FLM_64BIT + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x7)); +#else + pucSectionEnd = pucStart + (sizeof( FLMUINT) - ((FLMUINT)pucStart & 0x3)); +#endif + + if( pucSectionEnd > pucEnd) + { + pucSectionEnd = pucEnd; + } + + while( pucCur < pucSectionEnd) + { + uiChecksum = (uiChecksum << 8) + *pucCur++; + } + +#ifdef FLM_64BIT + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFFFFFFFFF8); +#else + pucSectionEnd = (FLMBYTE *)((FLMUINT)pucEnd & 0xFFFFFFFC); +#endif + + while( pucCur < pucSectionEnd) + { + uiChecksum ^= *((FLMUINT *)pucCur); + pucCur += sizeof( FLMUINT); + } + + while( pucCur < pucEnd) + { + uiChecksum ^= *pucCur++; + } + + ucTmp = (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + +#ifdef FLM_64BIT + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; + + uiChecksum >>= 8; + ucTmp ^= (FLMBYTE)uiChecksum; +#endif + + ucTmp ^= (FLMBYTE)(uiChecksum >> 8); + + if( (uiChecksum = (FLMUINT)ucTmp) == 0) + { + uiChecksum = 1; + } + + return( (FLMBYTE)uiChecksum); +} + diff --git a/version5/src/ddcreate.cpp b/version5/src/ddcreate.cpp new file mode 100644 index 0000000..2c28df8 --- /dev/null +++ b/version5/src/ddcreate.cpp @@ -0,0 +1,811 @@ +//------------------------------------------------------------------------------ +// Desc: Routines to service creation of a database dictionary. +// +// 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: ddcreate.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +// Internal Static Routines + +/**************************************************************************** +Desc: Read in LFH headers. +****************************************************************************/ +RCODE F_Db::dictReadLFH( void) +{ + RCODE rc = NE_XFLM_OK; + LFILE * pLFile; + F_COLLECTION * pCollection; + F_CachedBlock * pSCache; + FLMBOOL bReleaseCache = FALSE; + F_BLK_HDR * pBlkHdr; + FLMUINT uiBlkAddress; + FLMUINT uiPos; + FLMUINT uiEndPos; + FLMUINT uiBlkSize = m_pDatabase->m_uiBlockSize; + LFILE TmpLFile; + F_COLLECTION TmpCollection; + + f_memset( &TmpLFile, 0, sizeof( LFILE)); + f_memset( &TmpCollection, 0, sizeof( F_COLLECTION)); + + uiBlkAddress = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr; + while (uiBlkAddress) + { + if (RC_BAD( rc = m_pDatabase->getBlock( this, NULL, + uiBlkAddress, NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + pBlkHdr = pSCache->m_pBlkHdr; + uiPos = SIZEOF_STD_BLK_HDR; + uiEndPos = blkGetEnd( uiBlkSize, SIZEOF_STD_BLK_HDR, pBlkHdr); + + // Read through all of the logical file definitions in the block + + for( ; uiPos + sizeof( F_LF_HDR) <= uiEndPos; uiPos += sizeof( F_LF_HDR)) + { + F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)(pBlkHdr) + uiPos); + eLFileType eLfType = (eLFileType)pLfHdr->ui32LfType; + + // Have to fix up the offsets later when they are read in + + if (eLfType == XFLM_LF_INVALID) + { + continue; + } + + // Populate the LFILE in the dictionary, if one has been set up. + + if (eLfType == XFLM_LF_INDEX) + { + FSLFileIn( (FLMBYTE *)pLfHdr, + &TmpLFile, NULL, uiBlkAddress, uiPos); + + if (RC_OK( m_pDict->getIndex( TmpLFile.uiLfNum, &pLFile, + NULL, TRUE))) + { + f_memcpy( pLFile, &TmpLFile, sizeof( LFILE)); + } + + // LFILE better have a non-zero root block. + + if (!TmpLFile.uiRootBlk) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + else + { + + // Better be a container + + flmAssert( eLfType == XFLM_LF_COLLECTION); + + FSLFileIn( (FLMBYTE *)pLfHdr, + &TmpCollection.lfInfo, &TmpCollection, uiBlkAddress, uiPos); + + if (RC_OK( m_pDict->getCollection( TmpCollection.lfInfo.uiLfNum, + &pCollection, TRUE))) + { + f_memcpy( pCollection, &TmpCollection, sizeof( F_COLLECTION)); + } + + // LFILE better have a non-zero root block. + + if (!TmpCollection.lfInfo.uiRootBlk) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + } + + // Get the next block in the chain + + uiBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain; + ScaReleaseCache( pSCache, FALSE); + bReleaseCache = FALSE; + } + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc ); +} + +/**************************************************************************** +Desc: Read in all element, attribute, index, or collection definitions - as + specified in uiDictType. +****************************************************************************/ +RCODE F_Db::dictReadDefs( + FLMUINT uiDictType) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector key; + LFILE * pLFile; + IXD * pIxd; + F_Btree * pbTree = NULL; + FLMBYTE ucKeyBuf [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT uiFoundDictType; + FLMUINT uiLowest; + FLMUINT uiHighest; + FLMUINT uiDictNum; + IXKeyCompare compareObject; + + if (RC_BAD( rc = m_pDict->getIndex( XFLM_DICT_NUMBER_INDEX, &pLFile, &pIxd))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // First determine the low and high field numbers. + + // If the LFILE is not yet set up, the index has not yet been + // created, so there will be no definitions to read. This will + // be the case when we are first creating the dictionary. We have + // started a transaction, and it is trying to read in the definitions + // but there are none. + + flmAssert( pLFile->uiRootBlk); + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) + { + goto Exit; + } + + // Open the B-Tree + + compareObject.setIxInfo( this, pIxd); + compareObject.setCompareNodeIds( FALSE); + compareObject.setCompareDocId( FALSE); + compareObject.setSearchKey( &key); + + if (RC_BAD( rc = pbTree->btOpen( this, pLFile, FALSE, FALSE, + &compareObject))) + { + goto Exit; + } + + if (RC_BAD( rc = key.setUINT( 0, uiDictType))) + { + goto Exit; + } + if (RC_BAD( rc = key.outputKey( pIxd, 0, + ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, SEARCH_KEY_FLAG))) + { + goto Exit; + } + + // Position to the first key, if any + + if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen, XFLM_INCL, NULL))) + { + + // May not have found anything. + + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + key.reset(); + + if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) + { + goto Exit; + } + + // See if we went past the last key of this type. + + if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) + { + goto Exit; + } + + if (uiFoundDictType != uiDictType) + { + goto Exit; // Will return NE_XFLM_OK + } + + if (RC_BAD( rc = key.getUINT( 1, &uiLowest))) + { + goto Exit; + } + uiHighest = uiLowest; + + // Position to the end of keys of this type + + key.reset(); + if (RC_BAD( rc = key.setUINT( 0, uiDictType))) + { + goto Exit; + } + if (RC_BAD( rc = key.setUINT( 1, 0xFFFFFFFF))) + { + goto Exit; + } + if (RC_BAD( rc = key.outputKey( pIxd, 0, + ucKeyBuf, sizeof( ucKeyBuf), &uiKeyLen, SEARCH_KEY_FLAG))) + { + goto Exit; + } + + // Position to just past the specified key. + + if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen, XFLM_EXCL, NULL))) + { + + // May not have found anything, in which case we need to + // position to the last key in the index. + + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + if (RC_BAD( rc = pbTree->btLastEntry( ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + // Backup one key - since we will have gone just beyond + // keys of this type. + + if (RC_BAD( rc = pbTree->btPrevEntry( ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen))) + { + goto Exit; + } + } + + // At this point we better be positioned on the last key of this type + + key.reset(); + + if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) + { + goto Exit; + } + if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) + { + goto Exit; + } + + // See if we went past the last key of this type - should not + // be possible, unless there is a corruption. + + if (uiFoundDictType != uiDictType) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + if (RC_BAD( rc = key.getUINT( 1, &uiHighest))) + { + goto Exit; + } + + // uiHighest better be >= uiLowest or we have + // b-tree corruption. + + if (uiHighest < uiLowest) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + // Pre-allocate the tables + + if (uiDictType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = m_pDict->allocElementTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + else if (uiDictType == ELM_ATTRIBUTE_TAG) + { + if (RC_BAD( rc = m_pDict->allocAttributeTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + else if (uiDictType == ELM_INDEX_TAG) + { + if (RC_BAD( rc = m_pDict->allocIndexTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + else if (uiDictType == ELM_PREFIX_TAG) + { + if (RC_BAD( rc = m_pDict->allocPrefixTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + else if (uiDictType == ELM_ENCDEF_TAG) + { + if (RC_BAD( rc = m_pDict->allocEncDefTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + else // (uiDictType == ELM_COLLECTION_TAG) + { + flmAssert( uiDictType == ELM_COLLECTION_TAG); + + if (RC_BAD( rc = m_pDict->allocCollectionTable( uiLowest, uiHighest))) + { + goto Exit; + } + } + + // Position back to the first key for this type + + key.reset(); + if (RC_BAD( rc = key.setUINT( 0, uiDictType))) + { + goto Exit; + } + if (RC_BAD( rc = key.outputKey( pIxd, 0, + ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen, SEARCH_KEY_FLAG))) + { + goto Exit; + } + if (RC_BAD( rc = pbTree->btLocateEntry( ucKeyBuf, sizeof( ucKeyBuf), + &uiKeyLen, XFLM_INCL, NULL))) + { + + // May not have found anything. + + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // Loop through all of the keys of this dictionary type + + for (;;) + { + key.reset(); + + if (RC_BAD( rc = key.inputKey( pIxd, ucKeyBuf, uiKeyLen))) + { + goto Exit; + } + + // See if we went past the last key of this type. + + if (RC_BAD( rc = key.getUINT( 0, &uiFoundDictType))) + { + goto Exit; + } + + if (uiFoundDictType != uiDictType) + { + break; + } + + // Get the dictionary number + + if (RC_BAD( rc = key.getUINT( 1, &uiDictNum))) + { + goto Exit; + } + + // No need to process any more elements or attributes if the + // dictionary number is in the extended range. + + if ((uiDictType == ELM_ELEMENT_TAG && + uiDictNum >= FLM_LOW_EXT_ELEMENT_NUM) || + (uiDictType == ELM_ATTRIBUTE_TAG && + uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM)) + { + if (uiDictType == ELM_ELEMENT_TAG) + { + m_pDict->m_pNameTable->m_bLoadedAllElements = FALSE; + } + else + { + m_pDict->m_pNameTable->m_bLoadedAllAttributes = FALSE; + } + break; + } + + if (RC_BAD( rc = m_pDict->updateDict( this, + uiDictType, key.getDocumentID(), 0, + TRUE, FALSE))) + { + goto Exit; + } + + // Go to the next key + + if (RC_BAD( rc = pbTree->btNextEntry( ucKeyBuf, + sizeof( ucKeyBuf), + &uiKeyLen))) + { + + // May not have found anything. + + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + } + +Exit: + + if (pbTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: Open a dictionary by reading in all of the dictionary tables + from the dictionaries. +****************************************************************************/ +RCODE F_Db::dictOpen( void) +{ + RCODE rc = NE_XFLM_OK; + + // At this point, better not be pointing to a dictionary. + + flmAssert( !m_pDict); + + // Should never get here for a temporary database. + + flmAssert( !m_pDatabase->m_bTempDb); + + // Allocate a new F_Dict object for reading the dictionary + // into memory. + + if ((m_pDict = f_new F_Dict) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Allocate the name table + + if (RC_BAD( rc = m_pDict->allocNameTable())) + { + goto Exit; + } + + // Add in all of the reserved dictionary tags to the name table. + + if (RC_BAD( rc = m_pDict->getNameTable()->addReservedDictTags())) + { + goto Exit; + } + + // Allocate the fixed collections and indexes and set them up + + if (RC_BAD( rc = m_pDict->setupPredefined( + m_pDatabase->m_uiDefaultLanguage))) + { + goto Exit; + } + + // Read in the LFH's for the predefined stuff. + + if (RC_BAD( rc = dictReadLFH())) + { + goto Exit; + } + + // If dictionary collection is not yet set up, do nothing. + + if (m_pDict->m_pDictCollection->lfInfo.uiBlkAddress && + m_pDict->m_pDictCollection->lfInfo.uiOffsetInBlk) + { + + // Read in definitions in the following order: + // 1) attribute definitions + // 2) element definitions + // 3) collection definitions + // 4) index definitions + // This guarantees that things will be defined by the + // time they are referenced. + + if (RC_BAD( rc = dictReadDefs( ELM_ATTRIBUTE_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = dictReadDefs( ELM_ELEMENT_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = dictReadDefs( ELM_COLLECTION_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = dictReadDefs( ELM_INDEX_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = dictReadDefs( ELM_PREFIX_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = dictReadDefs( ELM_ENCDEF_TAG))) + { + goto Exit; + } + + // Must read LFHs to get the LFILE information for the + // collections and indexes we have just added. + + if (RC_BAD( rc = dictReadLFH())) + { + goto Exit; + } + } + + m_pDict->getNameTable()->sortTags(); + + if (m_pDatabase) + { + m_pDict->m_bInLimitedMode = m_pDatabase->inLimitedMode(); + } + // VISIT: Should we assume limited mode if we don't have a database file ? + +Exit: + + if (RC_BAD( rc) && m_pDict) + { + m_pDict->Release(); + m_pDict = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: Creates a new dictionary for a database. + This occurs on database create and on a dictionary change. +****************************************************************************/ +RCODE F_Db::createNewDict( void) +{ + RCODE rc = NE_XFLM_OK; + + // Unlink the DB from the current F_Dict object, if any. + + if (m_pDict) + { + m_pDatabase->lockMutex(); + unlinkFromDict(); + m_pDatabase->unlockMutex(); + } + + // Allocate a new F_Dict object for the new dictionary we + // are going to create. + + if (RC_BAD( rc = dictOpen())) + { + goto Exit; + } + + // Update the F_Db flags to indicate that the dictionary + // was updated. + + m_uiFlags |= FDB_UPDATED_DICTIONARY; + + // Create a special document in the dictionary to hold + // the next element, next attribute, next index, and next + // collection numbers. + + if (RC_BAD( rc = m_pDict->createNextDictNums( this))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add data dictionary records to the data dictionary. +****************************************************************************/ +RCODE F_Db::dictCreate( + const char * pszDictPath, // Name of dictionary file. This is only + // used if dictBuf is NULL. If both + // dictPath and dictBuf are NULL, the + // database will be created with an empty + // dictionary + const char * pszDictBuf) // Buffer containing dictionary in ASCII + // GEDCOM If NULL pszDictPath will be used +{ + RCODE rc = NE_XFLM_OK; + IF_FileHdl * pDictFileHdl = NULL; + FLMBOOL bFileOpen = FALSE; + LFILE TempLFile; + F_COLLECTION TempCollection; + char * pszXMLBuffer = NULL; + FLMUINT64 ui64FileSize; + FLMUINT uiBytesRead; + F_BufferIStream stream; + + // This should never be called for a temporary database. + + flmAssert( !m_pDatabase->m_bTempDb); + + // Create the default data collection + + if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, + &TempCollection, XFLM_DATA_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) + { + goto Exit; + } + + // Create the dictionary collection and indexes + + if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, + &TempCollection, + XFLM_DICT_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDatabase->lFileCreate( this, + &TempLFile, NULL, XFLM_DICT_NUMBER_INDEX, XFLM_LF_INDEX, FALSE, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDatabase->lFileCreate( this, + &TempLFile, NULL, XFLM_DICT_NAME_INDEX, XFLM_LF_INDEX, FALSE, TRUE))) + { + goto Exit; + } + + // Create the maintenance collection + + if (RC_BAD(rc = m_pDatabase->lFileCreate( this, &TempCollection.lfInfo, + &TempCollection, + XFLM_MAINT_COLLECTION, XFLM_LF_COLLECTION, FALSE, TRUE))) + { + goto Exit; + } + + // Create a new dictionary we can work with. + + if (RC_BAD( rc = createNewDict())) + { + goto Exit; + } + + // If we have an XML buffer, there is no need to open the file. + + if (!pszDictBuf && pszDictPath) + { + if (RC_BAD( rc = gv_pFileSystem->Open( + pszDictPath, XFLM_IO_RDONLY, &pDictFileHdl))) + { + goto Exit; + } + bFileOpen = TRUE; + + // Get the file size and allocate a buffer to hold the entire thing. + + if (RC_BAD( rc = pDictFileHdl->Size( &ui64FileSize))) + { + goto Exit; + } + + // Add 1 to size so we can NULL terminate the string we read. + + if (RC_BAD( rc = f_alloc( (FLMUINT)(ui64FileSize + 1), &pszXMLBuffer))) + { + goto Exit; + } + + // Read the entire file into the buffer + + if (RC_BAD( rc = pDictFileHdl->Read( 0, (FLMUINT)ui64FileSize, + pszXMLBuffer, &uiBytesRead))) + { + goto Exit; + } + pszXMLBuffer [uiBytesRead] = 0; + pszDictBuf = pszXMLBuffer; + } + if (!pszDictBuf || !(*pszDictBuf)) + { + + // Neither a dictionary buffer or file were specified. + + goto Exit; + } + + // Parse through the buffer, extracting each XML document, + // add to the dictionary and F_Dict object. The import method + // reads stuff from the stream, parses it into XML documents, + // and calls documentDone when the document is complete. + // The documentDone method checks the dictionary syntax, + // adds to the dictionary, etc. + + if (RC_BAD( rc = stream.open( (FLMBYTE *)pszDictBuf, 0))) + { + goto Exit; + } + + if (RC_BAD( import( &stream, XFLM_DICT_COLLECTION))) + { + goto Exit; + } + + m_pDict->getNameTable()->sortTags(); + +Exit: + + if (bFileOpen) + { + pDictFileHdl->Close(); + } + + if (pDictFileHdl) + { + pDictFileHdl->Release(); + } + + if (pszXMLBuffer) + { + f_free( pszXMLBuffer); + } + + return( rc); +} diff --git a/version5/src/f64bitfh.cpp b/version5/src/f64bitfh.cpp new file mode 100644 index 0000000..59c1a06 --- /dev/null +++ b/version5/src/f64bitfh.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 "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_64BitFileHandle::F_64BitFileHandle( + FLMUINT uiMaxFileSize) +{ + m_bOpen = FALSE; + m_szPath[ 0] = 0; + m_ui64EOF = 0; + m_pLockFileHdl = NULL; + f_memset( m_pFileHdlList, 0, sizeof( FH_INFO) * F_64BIT_FHDL_LIST_SIZE); + m_uiMaxFileSize = uiMaxFileSize; + if( !m_uiMaxFileSize) + { + m_uiMaxFileSize = F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_64BitFileHandle::~F_64BitFileHandle() +{ + if( m_bOpen) + { + Close(); + } + + flmAssert( !m_pLockFileHdl); +} + +/**************************************************************************** +Desc: Closes all data files associated with the object +****************************************************************************/ +void F_64BitFileHandle::Close( + FLMBOOL bDelete) +{ + FLMUINT uiLoop; + IF_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_XFLM_OK; + + if( !m_bOpen) + { + return; + } + + for( uiLoop = 0; uiLoop < F_64BIT_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->Delete( 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_64BitFileHandle::Delete( + const char * pszPath) +{ + IF_DirHdl * pDir = NULL; + char szTmpPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_XFLM_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_XFLM_FAILURE)); + } + + if( RC_BAD( rc = gv_pFileSystem->Exists( 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->Delete( 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->Delete( szTmpPath); + } + + pDir->Release(); + pDir = NULL; + rc = NE_XFLM_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_64BitFileHandle::Create( + const char * pszPath) +{ + FLMBOOL bCreatedDir = FALSE; + RCODE rc = NE_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_64BitFileHandle::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_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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_64BitFileHandle::Open( + const char * pszPath) +{ + IF_DirHdl * pDir = NULL; + FLMUINT uiTmp; + FLMUINT uiHighFileNum = 0; + FLMUINT64 ui64HighOffset = 0; + RCODE rc = NE_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( RC_BAD( gv_pFileSystem->Exists( pszPath)) || + !gv_pFileSystem->IsDir( pszPath)) + { + rc = RC_SET( NE_XFLM_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_XFLM_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_64BitFileHandle::Flush( void) +{ + FLMUINT uiLoop; + RCODE rc = NE_XFLM_OK; + + if( !m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + for( uiLoop = 0; uiLoop < F_64BIT_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_64BitFileHandle::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_XFLM_OK; + + /* + Handle the case of a 0-byte read + */ + + if( !uiLength) + { + if( ui64Offset >= m_ui64EOF) + { + rc = RC_SET( NE_XFLM_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_XFLM_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_XFLM_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_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pFileHdl->Read( uiFileOffset, uiBytesToRead, + pvBuffer, &uiTmp))) + { + if( rc == NE_XFLM_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_XFLM_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_64BitFileHandle::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_XFLM_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_64BitFileHandle::GetFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + IF_FileHdl ** ppFileHdl) +{ + FLMUINT uiSlot; + IF_FileHdl * pTmpHdl; + char szPath[ F_PATH_MAX_SIZE]; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bOpen); + + *ppFileHdl = NULL; + + uiSlot = uiFileNum % F_64BIT_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->Open( szPath, XFLM_IO_RDWR, &pTmpHdl))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND && bGetForWrite) + { + if( RC_BAD( rc = gv_pFileSystem->Create( szPath, + XFLM_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_64BitFileHandle::GetFileNum( + const char * pszFileName, + FLMUINT * puiFileNum) +{ + FLMUINT uiCnt = 0; + FLMUINT uiDigit; + FLMUINT uiFileNum = 0; + RCODE rc = NE_XFLM_OK; + + if( f_strlen( pszFileName) != 11) // XXXXXXXX.64 + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + + if( f_strcmp( &pszFileName[ 8], ".64") != 0) + { + rc = RC_SET( NE_XFLM_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_XFLM_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_64BitFileHandle::CreateLockFile( + const char * pszBasePath) +{ + RCODE rc = NE_XFLM_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_XFLM_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, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) + { +#ifndef FLM_UNIX + if (RC_BAD( gv_pFileSystem->Delete( szLockPath))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + else if (RC_BAD( pLockFileHdl->Create( szLockPath, + XFLM_IO_RDWR | XFLM_IO_EXCL | + XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#else + + if( RC_BAD( pLockFileHdl->Open( szLockPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + } + +#ifdef FLM_UNIX + if( RC_BAD( pLockFileHdl->Lock())) + { + rc = RC_SET( NE_XFLM_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_64BitFileHandle::Truncate( + FLMUINT64 ui64NewSize + ) +{ + RCODE rc = NE_XFLM_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/version5/src/f64bitfh.h b/version5/src/f64bitfh.h new file mode 100644 index 0000000..26483db --- /dev/null +++ b/version5/src/f64bitfh.h @@ -0,0 +1,204 @@ +//------------------------------------------------------------------------------ +// Desc: FLAIM's 64-bit file abstraction class +// +// 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: f64bitfh.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef F64BITFH_H +#define F64BITFH_H + +#define F_64BIT_FHDL_LIST_SIZE 8 +#define F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE ((FLMUINT)0xFFFFFFFF) + +typedef struct +{ + IF_FileHdl * pFileHdl; + FLMUINT uiFileNum; + FLMBOOL bDirty; +} FH_INFO; + +/*=========================================================================== +Desc: This object is used to simulate a 64-bit file system. +===========================================================================*/ +class F_64BitFileHandle : public XF_RefCount, public XF_Base +{ +public: + + F_64BitFileHandle( // Constructor + FLMUINT uiMaxFileSize = F_64BIT_FHDL_DEFAULT_MAX_FILE_SIZE); + + ~F_64BitFileHandle(); // Destructor + + void Close( // Close a file - The destructor will call this + FLMBOOL bDelete = FALSE); + + + RCODE Create( + const char * pszPath); // File to be created (creates a directory for + // the data files) + + RCODE CreateUnique( // Create a new file (with a unique file name -- + // creates a directory for the data files). + const char * pszPath, // Directory where the file is to be created + const char * pszFileExtension); // Extension to be used on the new file. + + RCODE Delete( // Delete a file + const char * pszPath); + + RCODE Open( // Initiates access to an existing file. + const char * pszPath); // File to be opened (specifies the data + // file directory). + + RCODE Flush( void); // Flushes a file's buffers to disk + + RCODE Read( // Reads a buffer of data from a file + FLMUINT64 ui64Offset, // Offset to begin reading + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer + FLMUINT * puiBytesRead); // [out] Number of bytes read + + RCODE Write( // Writes a buffer of data to a file. + 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. + + RCODE GetPath( // Returns the full path to the data file + // directory + char * pszFilePath); + + FINLINE RCODE Size( + FLMUINT64 * pui64FileSize) + { + *pui64FileSize = m_ui64EOF; + return( NE_XFLM_OK); + } + + RCODE Truncate( + FLMUINT64 ui64NewSize); + +private: + + /* + Methods + */ + + RCODE GetFileHdl( // Get / Open / Create data file + FLMUINT uiFileNum, + FLMBOOL bGetForWrite, + IF_FileHdl ** ppFileHdl); + + RCODE CreateLockFile( // Creates a lock file for exclusive + // access to the 64-bit file. This is + // necessary since this object maintains + // an in-memory EOF offset. + 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 + } + } + + /*=========================================================================== + Private: FormatFileNum + Desc: Formats the file number into a string using characters 0 - 9 and + a - z + ===========================================================================*/ + FINLINE void FormatFileNum( + FLMUINT uiFileNum, + char * pszStr) + { + f_sprintf( pszStr, "%08X.64", (unsigned)uiFileNum); + } + + RCODE GetFileNum( + const char * pszFileName, + FLMUINT * puiFileNum); + + /*=========================================================================== + Private: DataFilePath + Desc: Returns the specified data file's path + ===========================================================================*/ + 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)); + } + + /* + Data + */ + + FH_INFO m_pFileHdlList[ F_64BIT_FHDL_LIST_SIZE]; + char m_szPath[ F_PATH_MAX_SIZE]; // Data file directory path + FLMBOOL m_bOpen; + FLMUINT64 m_ui64EOF; + FLMUINT m_uiMaxFileSize; + IF_FileHdl * m_pLockFileHdl; +}; + +#endif // #ifndef F64BITFH_H diff --git a/version5/src/f_btpool.cpp b/version5/src/f_btpool.cpp new file mode 100644 index 0000000..f0a8dd2 --- /dev/null +++ b/version5/src/f_btpool.cpp @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// Desc: B-Tree pool class file +// +// 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_btpool.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Method for initializing the Btree Pool. +****************************************************************************/ +RCODE F_BtPool::btpInit( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( !m_bInitialized); + + // Create a mutex to control access to the pool. + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + m_bInitialized = TRUE; + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Method to reserve a btree object from the pool. +****************************************************************************/ +RCODE F_BtPool::btpReserveBtree( + F_Btree ** ppBtree) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bInitialized); + + // Lock the mutex first! + f_mutexLock( m_hMutex); + + if (m_pBtreeList) + { + *ppBtree = m_pBtreeList; + m_pBtreeList = m_pBtreeList->m_pNext; + (*ppBtree)->m_pNext = NULL; + } + else + { + if (( *ppBtree = f_new F_Btree()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + +Exit: + + f_mutexUnlock( m_hMutex); + + return rc; +} + +/**************************************************************************** +Desc: Method to return a Btree to the pool. +****************************************************************************/ +void F_BtPool::btpReturnBtree( + F_Btree ** ppBtree) +{ + flmAssert( m_bInitialized); + + // Close the Btree + + (*ppBtree)->btClose(); + + // Lock the mutex first! + + f_mutexLock( m_hMutex); + + (*ppBtree)->m_pNext = m_pBtreeList; + m_pBtreeList = *ppBtree; + *ppBtree = NULL; + + f_mutexUnlock( m_hMutex); +} diff --git a/version5/src/f_btpool.h b/version5/src/f_btpool.h new file mode 100644 index 0000000..3c9fe73 --- /dev/null +++ b/version5/src/f_btpool.h @@ -0,0 +1,79 @@ +//------------------------------------------------------------------------------ +// Desc: Header file for the B-Tree pool +// +// 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_btpool.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef F_BTPOOL_H +#define F_BTPOOL_H + +#include "f_btree.h" + +class F_BtPool : public XF_RefCount, public XF_Base +{ +public: + F_BtPool( void) + { + m_pBtreeList = NULL; + m_hMutex = F_MUTEX_NULL; + m_bInitialized = FALSE; + } + + ~F_BtPool( void) + { + while (m_pBtreeList) + { + F_Btree * pBtree; + + pBtree = m_pBtreeList; + m_pBtreeList = m_pBtreeList->m_pNext; + + pBtree->Release(); + } + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + m_bInitialized = FALSE; + } + + RCODE btpInit( void); + + RCODE btpReserveBtree( + F_Btree ** ppBtree); + + void btpReturnBtree( + F_Btree ** ppBtree); + +private: + + F_Btree * m_pBtreeList; + F_MUTEX m_hMutex; + FLMBOOL m_bInitialized; +}; + +#endif + + + diff --git a/version5/src/f_btree.cpp b/version5/src/f_btree.cpp new file mode 100644 index 0000000..ff2a239 --- /dev/null +++ b/version5/src/f_btree.cpp @@ -0,0 +1,11707 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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: Constructor +****************************************************************************/ +F_Btree::F_Btree( void) +{ + m_bOpened = FALSE; + m_pStack = NULL; + m_uiStackLevels = 0; + m_uiRootLevel = 0; + f_memset(m_Stack, 0, sizeof(m_Stack)); + m_pLFile = NULL; + m_pDb = NULL; + m_bTempDb = FALSE; + m_pucTempBlk = NULL; + m_pucTempDefragBlk = NULL; + 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_ui64CurrTransID = 0; + m_ui64LastBlkTransId = 0; + m_ui64PrimaryBlkTransId = 0; + m_uiBlkChangeCnt = 0; + m_ui64LowTransId = FLM_MAX_UINT64; + m_bMostCurrent = FALSE; + 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_pSCache = NULL; + m_pBlkHdr = NULL; + m_uiSearchLevel = BH_MAX_LEVELS; + m_pNext = NULL; + m_pCompare = NULL; +} + +/*************************************************************************** +Desc: Destructor +****************************************************************************/ +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. Upon return, the Root block address and the block address + will be set in the LFile. +****************************************************************************/ +RCODE F_Btree::btCreate( + F_Db * pDb, // In + LFILE * pLFile, // In/Out + FLMBOOL bCounts, // In + FLMBOOL bData // In + ) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = 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_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify that we are in an update transaction. + if (pDb->m_eTransType != XFLM_UPDATE_TRANS && !pDb->m_pDatabase->m_bTempDb) + { + rc = RC_SET_AND_ASSERT( pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // Initialize the returned root block address to 0 incase of an error. + pLFile->uiRootBlk = 0; + + // Call createBlock to create a new block + if (RC_BAD( rc = pDb->m_pDatabase->createBlock( pDb, &pSCache))) + { + goto Exit; + } + + // Save the new block address as the root block address + pLFile->uiRootBlk = pSCache->m_uiBlkAddress; + + // Save the block address and identify the block as the root block. + if (RC_BAD( rc = btOpen( pDb, pLFile, bCounts, bData))) + { + goto Exit; + } + pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + + setRootBlk( pBlkHdr); + pBlkHdr->ui16LogicalFile = (FLMUINT16)pLFile->uiLfNum; + setBlkLfType( pBlkHdr, pLFile->eLfType); + 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 & 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 (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Btree initialization function. +****************************************************************************/ +RCODE F_Btree::btOpen( + F_Db * pDb, + LFILE * pLFile, + FLMBOOL bCounts, + FLMBOOL bData, + IF_ResultSetCompare * pCompare) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase= pDb->m_pDatabase; + + if ( m_bOpened) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + if (pDb->m_eTransType == XFLM_NO_TRANS && !pDatabase->m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + + if( !pLFile->uiRootBlk) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + m_pLFile = pLFile; + m_uiBlockSize = pDatabase->m_uiBlockSize; + m_uiDefragThreshold = m_uiBlockSize / 20; + m_uiOverflowThreshold = (m_uiBlockSize * 8) / 5; + m_bCounts = bCounts; + m_bData = bData; + m_pDb = pDb; + m_bTempDb = pDatabase->m_bTempDb; + m_pReplaceInfo = NULL; + m_uiReplaceLevels = 0; + m_ui64CurrTransID = 0; + m_ui64LastBlkTransId = 0; + m_ui64PrimaryBlkTransId = 0; + m_uiBlkChangeCnt = 0; + m_uiSearchLevel = BH_MAX_LEVELS; + + m_bSetupForRead = FALSE; + m_bSetupForWrite = FALSE; + m_bSetupForReplace = FALSE; + + // Buffer is required to hold at least the maximum number of + // offsets possible given the block size with a minimum entry size of + // 5 bytes. Each offset is 2 bytes. We only need this buffer when we + // are in an update transaction. + + if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb) + { + m_uiBufferSize = m_uiBlockSize * 2; + } + else + { + m_uiBufferSize = 0; + } + + // If we are in an update transaction, there are certain buffers that + // are used. Make sure these are allocated. + + if( (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb) && + !pDatabase->m_pucUpdBuffer) + { + // Buffer should be at least 80% of two blocks. To make sure the other + // structures being allocated here are aligned, we allocate 2 times the + // database's block size for the update buffer. + + pDatabase->m_uiUpdBufferSize = m_uiBlockSize * 2; + flmAssert( pDatabase->m_uiUpdBufferSize >= m_uiOverflowThreshold); + + if( RC_BAD( rc = f_alloc( + pDatabase->m_uiUpdBufferSize + + (m_uiBlockSize * 2) + + m_uiBufferSize + + sizeof( BTREE_REPLACE_STRUCT) * BH_MAX_LEVELS, + &pDatabase->m_pucUpdBuffer))) + { + goto Exit; + } + + // Temporary buffers for the F_Btree class will immediately follow + // the update buffer. + + pDatabase->m_pucBTreeTmpBlk = + pDatabase->m_pucUpdBuffer + pDatabase->m_uiUpdBufferSize; + pDatabase->m_pucBTreeTmpDefragBlk = + pDatabase->m_pucBTreeTmpBlk + pDatabase->m_uiBlockSize; + pDatabase->m_pucBtreeBuffer = + pDatabase->m_pucBTreeTmpDefragBlk + pDatabase->m_uiBlockSize; + pDatabase->m_pucReplaceStruct = + pDatabase->m_pucBtreeBuffer + m_uiBufferSize; + } + + // NOTE: These temporary buffers may be NULL. They are only allocated the + // first time we detect that we are operating inside an update + // transaction - because we need to make sure that only one thread + // actually does the allocation. The assumption here is that the + // buffers are only ever used during update operations, which will + // *always* be inside update transactions. + + m_pucTempBlk = pDatabase->m_pucBTreeTmpBlk; + m_pucTempDefragBlk = pDatabase->m_pucBTreeTmpDefragBlk; + m_pucBuffer = pDatabase->m_pucBtreeBuffer; + m_pReplaceStruct = (BTREE_REPLACE_STRUCT *)pDatabase->m_pucReplaceStruct; + + 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; + + // Ok to close multiple times. + if (!m_bOpened) + { + // Btree is not open. Just return. + return; + } + + m_pLFile = NULL; + m_pDb = NULL; + m_bTempDb = FALSE; + + for (uiLoop = 0; uiLoop < BH_MAX_LEVELS; uiLoop++) + { + m_Stack[ uiLoop].pucKeyBuf = NULL; + m_Stack[ uiLoop].uiKeyBufSize = 0; + } + + // Release any blocks still held in the stack. + btRelease(); + + if (m_pSCache) + { + flmAssert( 0); + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + if (m_pCompare) + { + m_pCompare->Release(); + m_pCompare = NULL; + } + + m_bOpened = FALSE; +} + +/*************************************************************************** +Desc: Delete the entire tree +****************************************************************************/ +RCODE F_Btree::btDeleteTree( + IF_DeleteStatus * ifpDeleteStatus) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumLevels; + FLMUINT puiBlkAddrs[ BH_MAX_LEVELS]; + FLMUINT uiLoop; + + flmAssert( m_bOpened); + + // Verify the transaction type + + if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // 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( + m_pDb, m_pLFile, puiBlkAddrs[ uiLoop], 0, NULL, NULL, + ifpDeleteStatus))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE btFreeBlockChain( + F_Db * pDb, + LFILE * pLFile, + FLMUINT uiStartAddr, + FLMUINT uiBlocksToFree, + FLMUINT * puiBlocksFreed, + FLMUINT * puiEndAddr, + IF_DeleteStatus * ifpDeleteStatus) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->getDatabase(); + F_CachedBlock * pCurrentBlk = NULL; + F_CachedBlock * pDOSCache = 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; + } + + // Verify the transaction type + + if( pDb->getTransType() != XFLM_UPDATE_TRANS) + { + rc = RC_SET_AND_ASSERT( pDb->getTransType() == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // Now, go through the chain and delete the blocks... + + uiCurrentBlkAddr = uiStartAddr; + while( uiCurrentBlkAddr) + { + if( !bFreeAll && uiTreeBlocksFreed >= uiBlocksToFree) + { + break; + } + + if( RC_BAD( pDatabase->getBlock( pDb, pLFile, + uiCurrentBlkAddr, NULL, &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( (FLMBYTE *)&ucDOBlkAddr[ 0]); + while( uiDOBlkAddr) + { + if( RC_BAD( + rc = pDatabase->getBlock( pDb, pLFile, + uiDOBlkAddr, NULL, &pDOSCache))) + { + goto Exit; + } + + uiDOBlkAddr = pDOSCache->getBlockPtr()->ui32NextBlkInChain; + rc = pDatabase->blockFree( pDb, pDOSCache); + pDOSCache = NULL; + + if (RC_BAD( rc)) + { + goto Exit; + } + + uiDataBlocksFreed++; + } + } + } + } + + rc = pDatabase->blockFree( pDb, pCurrentBlk); + pCurrentBlk = NULL; + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( ifpDeleteStatus && pLFile && ++uiStatusCounter >= 25) + { + uiStatusCounter = 0; + if( RC_BAD( rc = ifpDeleteStatus->reportDelete( + pLFile->uiLfNum, pLFile->eLfType == XFLM_LF_INDEX + ? TRUE + : FALSE, + uiTreeBlocksFreed + uiDataBlocksFreed, + pDatabase->getBlockSize()))) + { + goto Exit; + } + } + + uiTreeBlocksFreed++; + uiCurrentBlkAddr = uiNextBlkAddr; + } + + if( puiBlocksFreed) + { + *puiBlocksFreed = uiTreeBlocksFreed; + } + + if( puiEndAddr) + { + *puiEndAddr = uiCurrentBlkAddr; + } + +Exit: + + if( pDOSCache) + { + ScaReleaseCache( pDOSCache, FALSE); + } + + if( pCurrentBlk) + { + ScaReleaseCache( pCurrentBlk, FALSE); + } + + 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_XFLM_OK; + FLMUINT uiNumLevels = 0; + FLMUINT uiLFileNum; + FLMBOOL bIsIndex; + FLMUINT32 ui32NextBlkAddr; + F_CachedBlock * pCurrentBlk = NULL; + FLMBYTE * pucBlk; + FLMBYTE * pucEntry; + + flmAssert( m_bOpened); + + // Verify the transaction type + + if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // 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_pLFile->uiRootBlk; + uiLFileNum = m_pLFile->uiLfNum; + bIsIndex = m_pLFile->eLfType == XFLM_LF_INDEX ? TRUE : FALSE; + + while( ui32NextBlkAddr) + { + puiBlockAddrs[ uiNumLevels++] = ui32NextBlkAddr; + + if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &pCurrentBlk))) + { + goto Exit; + } + + pucBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; + + 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 + + ScaReleaseCache( pCurrentBlk, FALSE); + pCurrentBlk = NULL; + } + + *puiNumLevels = uiNumLevels; + +Exit: + + if( pCurrentBlk) + { + ScaReleaseCache( pCurrentBlk, FALSE); + } + + 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_XFLM_OK; + F_BLK_HDR * pBlkHdr; + FLMBYTE pucDOAddr[ 4]; + + if ( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || + (m_bSetupForWrite && bFirst)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + flmAssert( !m_pDb->m_pDatabase->m_pRfl || + !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if( !uiKeyLen) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Verify the transaction type + + if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_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, XFLM_EXACT); + + // Should not find this entry. If we get back anything other than + // an NE_XFLM_NOT_FOUND, then there is a problem. + + if( rc != NE_XFLM_NOT_FOUND) + { + if( RC_OK( rc)) + { + rc = RC_SET( NE_XFLM_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_pSCache == NULL); + if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) + { + goto Exit; + } + + // The data going in will be stored in Data-only blocks. + // Setup the block header... + + pBlkHdr = m_pSCache->m_pBlkHdr; + 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 = m_pSCache->m_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + FLMBYTE * pucValue = NULL; + FLMUINT uiLen = 0; + + if ( !m_bOpened) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify the Txn type + if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + flmAssert( !m_pDb->m_pDatabase->m_pRfl || + !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + btResetBtree(); + + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + // We need to locate where we should remove the entry. + if (RC_BAD( rc = findEntry( pucKey, + uiKeyLen, + XFLM_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_XFLM_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_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + flmAssert( !m_pDb->m_pDatabase->m_pRfl || + !m_pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if (!uiKeyLen) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Verify the Txn type + if (m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_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, XFLM_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_pSCache == NULL); + + if( m_bOrigInDOBlocks) + { + // Need to get the first DO block, and work from there. + + m_ui32DOBlkAddr = bteGetBlkAddr( pucLocalData); + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32DOBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + else + { + // Get one empty block to begin with + + if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) + { + goto Exit; + } + + // The data going in will be stored in Data-only blocks. + // Setup the block header... + + pBlkHdr = m_pSCache->m_pBlkHdr; + 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_pSCache->m_pBlkHdr); + m_uiDataLength = 0; + m_uiOADataLength = 0; + m_bDataOnlyBlock = TRUE; + m_bSetupForReplace = TRUE; + m_ui32DOBlkAddr = m_pSCache->m_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. + + if( m_bOrigInDOBlocks && m_pSCache && + m_pSCache->m_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + FLMBYTE * pucEntry = NULL; + + flmAssert( pucKey && uiKeyBufSize && puiKeyLen); + + if (!m_bOpened || m_bSetupForWrite || m_bSetupForReplace) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + m_bSetupForRead = FALSE; + + // Verify the Txn type + + if (m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + 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_ui64LowTransId = m_pStack->pBlkHdr->stdBlkHdr.ui64TransID; + m_bMostCurrent = (m_pStack->pSCache->m_ui64HighTransID == FLM_MAX_UINT64) + ? TRUE + : FALSE; + + 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. + if (puiDataLength && + m_pStack->pSCache->m_pBlkHdr->ui8BlkType == BT_LEAF_DATA) + { + btGetEntryDataLength( pucEntry, NULL, puiDataLength, NULL); + } + else if (puiDataLength) + { + *puiDataLength = 0; + } + + if( RC_BAD( rc = setupReadState( m_pStack->pSCache->m_pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such as + // in the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, + // we will pass back the key we actually found. + + if( uiMatch != XFLM_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 uiKeyBufSize, + FLMUINT uiKeyLen, + FLMBYTE * pucData, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEntry; + + if( !m_bOpened || !m_bSetupForRead || + m_bSetupForWrite || m_bSetupForReplace) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + if( puiDataLen) + { + *puiDataLen = 0; + } + + // Is there anything there to get? + + if( m_uiOADataRemaining == 0) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // If the transaction Id or the Block Change Count has changed, + // we must re-sync ourselves. + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // We must call btLocateEntry so we can re-initialize the read + + if( !m_bFirstRead) + { + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, + &uiKeyLen, XFLM_EXACT))) + { + goto Exit; + } + + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + else + { + rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + } + } + + // Get the current block. It is either a DO or a Btree block. + + if( m_pSCache == NULL) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, + m_pSCache->m_ui64HighTransID); + + // Now to find where we were the last time through. + + if( !m_bDataOnlyBlock) + { + pucEntry = BtEntry( (FLMBYTE *)m_pSCache->m_pBlkHdr, + m_uiCurOffset); + + btGetEntryDataLength( pucEntry, &m_pucDataPtr, NULL, NULL); + } + else + { + m_pucDataPtr = (FLMBYTE *)m_pSCache->m_pBlkHdr + + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_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_pSCache && m_pSCache->m_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + FLMBYTE * pucEntry = NULL; + FLMBOOL bAdvanced = FALSE; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify the transaction type + + if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + 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; + m_ui64PrimaryBlkTransId = m_ui64LastBlkTransId; + } + + // Do we need to resynchronize? + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + // Doing a find with XFLM_EXCL will result in our being positioned at + // the next entry. + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + XFLM_EXCL, puiDataLength))) + { + goto Exit; + } + + bAdvanced = TRUE; + } + } + + // Get the current block if we need it. + + if( !m_pSCache) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + 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( (FLMBYTE *)m_pSCache->m_pBlkHdr, 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( (FLMBYTE *)m_pSCache->m_pBlkHdr, 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); + } + + if( RC_BAD( rc = setupReadState( m_pSCache->m_pBlkHdr, pucEntry))) + { + goto Exit; + } + + // Incase the returning key is not what was originally requested, such as in + // the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, + // we will pass back the key we actually found. + + if( RC_BAD( rc = setReturnKey( pucEntry, m_pSCache->m_pBlkHdr->ui8BlkType, + pucKey, puiKeyLen, uiKeyBufSize))) + { + goto Exit; + } + + if( pui32BlkAddr) + { + *pui32BlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + + m_bFirstRead = FALSE; + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + FLMBYTE * pucEntry = NULL; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify the transaction type + + if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + 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; + m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; + + // Do we need to resynchronize? + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + // Doing a find with XFLM_INCL will allow for the possibility that + // the original entry is no longer there. We will still have + // to backup to the previous entry. + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + XFLM_INCL, puiDataLength))) + { + goto Exit; + } + } + } + + if( !m_pSCache) + { + // Fetch the current block, then backup from there. + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + 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( (FLMBYTE *)m_pSCache->m_pBlkHdr, 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); + } + + if( RC_BAD( rc = setupReadState( m_pSCache->m_pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such as in + // the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and possibly XFLM_INCL, + // we will pass back the key we actually found. + + if( RC_BAD( rc = setReturnKey( pucEntry, m_pSCache->m_pBlkHdr->ui8BlkType, + pucKey, puiKeyLen, uiKeyBufSize))) + { + goto Exit; + } + + if( pui32BlkAddr) + { + *pui32BlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; + } + + if( puiOffsetIndex) + { + *puiOffsetIndex = m_uiCurOffset; + } + + m_bFirstRead = FALSE; + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + + m_Stack[ 0].pucKeyBuf = pucKey; + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + XFLM_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_XFLM_OK; + + m_Stack[ 0].pucKeyBuf = pucKey; + m_Stack[ 0].uiKeyBufSize = uiKeyBufSize; + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + XFLM_LAST, NULL, puiDataLength, pui32BlkAddr, puiOffsetIndex))) + { + if( rc == NE_XFLM_BOF_HIT) + { + rc = RC_SET( NE_XFLM_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_XFLM_OK; + FLMBYTE * pucEntry = NULL; + + flmAssert( pucKey && uiKeyBufSize && puiKeyLen); + + m_bSetupForRead = FALSE; + + if( !m_bOpened || !m_bCounts) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify the transaction type + + if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // Find the entry we are interested in. + + if( RC_BAD(rc = positionToEntry( uiPosition))) + { + goto Exit; + } + + m_ui64LowTransId = m_pStack->pBlkHdr->stdBlkHdr.ui64TransID; + m_bMostCurrent = (m_pStack->pSCache->m_ui64HighTransID == FLM_MAX_UINT64) + ? TRUE + : FALSE; + + 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( m_pStack->pSCache->m_pBlkHdr, pucEntry))) + { + goto Exit; + } + + // In case the returning key is not what was originally requested, such + // as in the case of XFLM_FIRST, XFLM_LAST, XFLM_EXCL and + // possibly XFLM_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT * puiPosition) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiKeyBufSize = uiKeyLen; + FLMUINT uiLocalKeyLen = uiKeyLen; + + if( !m_bOpened || !m_bSetupForRead || !m_bCounts) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Verify the transaction type + + if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + *puiPosition = 0; + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; + + // Do we need to resynchronize? + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // We can get the position easily if we have to re-sync. + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, &uiLocalKeyLen, + XFLM_EXACT, puiPosition))) + { + goto Exit; + } + + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + } + else + { + // 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->pSCache == NULL); + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_pStack->ui32BlkAddr, NULL, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + + *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( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = NULL; + + if( !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + m_ui64LastBlkTransId = m_ui64PrimaryBlkTransId; + + // Do we need to resync? + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + flmAssert( m_pSCache == NULL); + + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // Won't need this block anymore. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + if( RC_BAD( rc = btLocateEntry( pucKey, uiKeyBufSize, puiKeyLen, + XFLM_EXACT))) + { + goto Exit; + } + + goto Exit; + } + } + + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32DOBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + m_ui64LastBlkTransId = pSCache->m_pBlkHdr->ui64TransID; + + // Local amount of data in this block + + m_uiDataRemaining = m_uiBlockSize - + sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr) - + pSCache->m_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. + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + + m_bFirstRead = FALSE; + m_bSetupForRead = TRUE; + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + + if( !m_bSetupForRead || !pUntilBtree->m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + // Ensure that both Btrees are from the same container. + + if( m_pLFile->uiRootBlk != pUntilBtree->m_pLFile->uiRootBlk) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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].pSCache) + { + if( m_Stack[ uiLevel].pSCache->m_uiUseCount) + { + ScaReleaseCache( m_Stack[ uiLevel].pSCache, FALSE); + } + + m_Stack[ uiLevel].pSCache = 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_XFLM_OK; + F_CachedBlock * pNewSCache = NULL; + F_CachedBlock * pPrevSCache = 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_pDb->m_pDatabase->createBlock( m_pDb, &pNewSCache))) + { + goto Exit; + } + + *pbBlockSplit = TRUE; + + // Setup the header ... + + pBlkHdr = (F_BTREE_BLK_HDR *)pNewSCache->m_pBlkHdr; + + unsetRootBlk( pBlkHdr); + setBlkLfType( pBlkHdr, m_pLFile->eLfType); + + pBlkHdr->ui16NumKeys = 0; + pBlkHdr->ui8BlkLevel = (FLMUINT8)m_pStack->uiLevel; + pBlkHdr->stdBlkHdr.ui8BlkType = m_pStack->pBlkHdr->stdBlkHdr.ui8BlkType; + pBlkHdr->ui16LogicalFile = m_pStack->pBlkHdr->ui16LogicalFile; + + // 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 pSCache could + // have changed since making this call, so we need to update the block + // header + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiBlkAddr, NULL, &pPrevSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pPrevSCache))) + { + 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( pPrevSCache) + { + pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = + pBlkHdr->stdBlkHdr.ui32BlkAddr; + + // Release the old previous block since we no longer need it. + + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = 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, &pNewSCache))) + { + 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->pSCache))) + { + 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. + + ScaReleaseCache( m_pStack->pSCache, FALSE); + + m_pStack->pSCache = pNewSCache; + pNewSCache = 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. + + ScaReleaseCache( m_pStack->pSCache, FALSE); + + m_pStack->pSCache = pNewSCache; + pNewSCache = 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; + } + } + + ScaReleaseCache( m_pStack->pSCache, FALSE); + + flmAssert( pNewSCache); + + m_pStack->pSCache = pNewSCache; + pNewSCache = 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( *pbBlockSplit) + { + if( m_pDb->m_pDbStats) + { + XFLM_LFILE_STATS * pLFileStats; + + if( (pLFileStats = m_pDb->getLFileStatPtr( m_pLFile)) != NULL) + { + pLFileStats->bHaveStats = TRUE; + pLFileStats->ui64BlockSplits++; + } + } + } + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + if( pNewSCache) + { + ScaReleaseCache( pNewSCache, FALSE); + } + + 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_XFLM_OK; + F_CachedBlock * pNewSCache = NULL; + FLMBYTE * pSrcBlk; + FLMBYTE * pDstBlk; + F_BTREE_BLK_HDR * pBlkHdr; + FLMUINT uiCounts = 0; + FLMBYTE * pucEntry; + FLMBYTE * pucNull = NULL; + FLMBYTE ucBuffer[ MAX_KEY_SIZ + BTE_NLC_KEY_START]; + FLMUINT uiMaxNLKey = MAX_KEY_SIZ + 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_XFLM_BTREE_FULL); + goto Exit; + } + + // Create a new block to copy the contents of the root block into + + if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &pNewSCache))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // Log that we are about to change the root block + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + // Update the stack since the pSCache could have changed + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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; + pBlkHdr = (F_BTREE_BLK_HDR *)pNewSCache->m_pBlkHdr; + + // Check for encryption + + if( isEncryptedBlk( (F_BLK_HDR *)m_pStack->pBlkHdr)) + { + setBlockEncrypted( (F_BLK_HDR *)pBlkHdr); + } + + pDstBlk = (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + unsetRootBlk( pBlkHdr); + setBlkLfType( pBlkHdr, m_pLFile->eLfType); + + pBlkHdr->ui16LogicalFile = (FLMUINT16)m_pLFile->uiLfNum; + pBlkHdr->ui16NumKeys = m_pStack->pBlkHdr->ui16NumKeys; + pBlkHdr->ui8BlkLevel = m_pStack->pBlkHdr->ui8BlkLevel; + pBlkHdr->ui16HeapSize = m_pStack->pBlkHdr->ui16HeapSize; + + pBlkHdr->stdBlkHdr.ui8BlkType = + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui8BlkType; + + pBlkHdr->stdBlkHdr.ui16BlkBytesAvail = + ((F_BLK_HDR *)m_pStack->pBlkHdr)->ui16BlkBytesAvail; + + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain = 0; + pBlkHdr->stdBlkHdr.ui32NextBlkInChain = 0; + + // Copy the data from the root block to the new block. + + f_memcpy( pDstBlk, pSrcBlk, m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr)); + + // 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 = pBlkHdr; // Point to new block + m_pStack->ui32BlkAddr = (FLMUINT32)pNewSCache->m_uiBlkAddress; + m_pStack->pSCache = pNewSCache; + pNewSCache = NULL; + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 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( pNewSCache) + { + ScaReleaseCache( pNewSCache, FALSE); + } + + 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_XFLM_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_XFLM_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( + F_CachedBlock * pChildSCache, + F_CachedBlock ** ppParentSCache, + FLMUINT uiParentElm) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCounts; + FLMBYTE * pucCounts; + F_CachedBlock * pParentSCache; + FLMBYTE * pBlk = (FLMBYTE *)pChildSCache->m_pBlkHdr; + + flmAssert( getBlkType( pBlk) == BT_NON_LEAF_COUNTS); + uiCounts = countKeys( pBlk); + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, ppParentSCache))) + { + goto Exit; + } + + pParentSCache = *ppParentSCache; + pucCounts = BtEntry( (FLMBYTE *)pParentSCache->m_pBlkHdr, 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_XFLM_OK; + F_CachedBlock * pPrevSCache = NULL; + const FLMBYTE * pucLocalData = pucData; + FLMUINT uiDataToWrite = uiDataLen; + F_BLK_HDR * pBlkHdr = NULL; + FLMBYTE * pDestPtr = NULL; + FLMUINT uiAmtToCopy; + + if( bSaveKey) + { + if( !m_pSCache) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + + // Assert that the current block is empty and has no previous link. + + flmAssert( m_pSCache->m_pBlkHdr->ui16BlkBytesAvail == + m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr)); + + flmAssert( m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); + + pBlkHdr = m_pSCache->m_pBlkHdr; + pDestPtr = (FLMBYTE *)pBlkHdr + + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr); + + 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_pSCache) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + + if( !bSaveKey) + { + pBlkHdr = m_pSCache->m_pBlkHdr; + + // 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) + { + pPrevSCache = m_pSCache; + m_pSCache = NULL; + + // Now create a new block + + if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( m_pDb, &m_pSCache))) + { + goto Exit; + } + + pBlkHdr = m_pSCache->m_pBlkHdr; + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = pPrevSCache->m_pBlkHdr->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)); + + pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; + + m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = NULL; + } + } + } + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + 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_XFLM_OK; + F_CachedBlock * pPrevSCache = NULL; + const FLMBYTE * pucLocalData = pucData; + FLMUINT uiDataToWrite = uiDataLen; + F_BLK_HDR * pBlkHdr = NULL; + FLMBYTE * pDestPtr = NULL; + FLMUINT uiAmtToCopy; + FLMUINT32 ui32NextBlkAddr; + + // Do we need to store the key too? + + if( bSaveKey) + { + if( !m_pSCache) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + + // Assert that the current block is empty and has no previous link. + + flmAssert( m_pSCache->m_pBlkHdr->ui16BlkBytesAvail == + m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr)); + + flmAssert( m_pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); + + pBlkHdr = m_pSCache->m_pBlkHdr; + pDestPtr = (FLMBYTE *)pBlkHdr + + sizeofDOBlkHdr( (F_BLK_HDR *)m_pSCache->m_pBlkHdr ); + + 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_pSCache) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + + if( !bSaveKey) + { + pBlkHdr = m_pSCache->m_pBlkHdr; + + // 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) + { + pPrevSCache = m_pSCache; + m_pSCache = NULL; + ui32NextBlkAddr = pPrevSCache->m_pBlkHdr->ui32NextBlkInChain; + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pSCache))) + { + goto Exit; + } + pBlkHdr = m_pSCache->m_pBlkHdr; + } + else + { + // Now create a new block + + if( RC_BAD( rc = m_pDb->m_pDatabase->createBlock( + m_pDb, &m_pSCache))) + { + goto Exit; + } + + pBlkHdr = m_pSCache->m_pBlkHdr; + pBlkHdr->ui8BlkType = BT_DATA_ONLY; + pBlkHdr->ui32PrevBlkInChain = pPrevSCache->m_pBlkHdr->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)); + } + + pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = pBlkHdr->ui32BlkAddr; + + m_ui32CurBlkAddr = pBlkHdr->ui32BlkAddr; + m_uiDataRemaining = m_uiBlockSize - sizeofDOBlkHdr( pBlkHdr); + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = 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_pSCache); + + ui32NextBlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; + m_pSCache->m_pBlkHdr->ui32NextBlkInChain = 0; + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + // If there are any blocks left over, they must be freed. + + while( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + ui32NextBlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; + + rc = m_pDb->m_pDatabase->blockFree( m_pDb, m_pSCache); + m_pSCache = NULL; + + if( RC_BAD( rc)) + { + goto Exit; + } + } + } + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + 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_XFLM_OK; + FLMBYTE * pucTemp = pucBuffer; + + if( puiEntrySize) + { + *puiEntrySize = calcEntrySize( uiBlkType, uiFlags, + uiKeyLen, uiDataLen, uiOADataLen); + + if( !(*puiEntrySize) || *puiEntrySize > uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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_XFLM_OK; + FLMUINT16 * pui16OffsetArray; + FLMUINT uiNumKeys; + FLMUINT uiEntrySize; + FLMUINT uiTmp; + FLMBYTE * pucEntry; + FLMBOOL bDOBlock; + F_CachedBlock * pSCache = NULL; + FLMUINT uiBlkAddr; + FLMBYTE * pucEndOfHeap; + F_BTREE_BLK_HDR * pBlkHdr; + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, + &m_pStack->pSCache))) + { + goto Exit; + } + + pBlkHdr = m_pStack->pBlkHdr = + (F_BTREE_BLK_HDR *)m_pStack->pSCache->getBlockPtr(); + + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + + uiNumKeys = pBlkHdr->ui16NumKeys; + + if( !uiNumKeys) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + // Get the next block address (if any) + + uiBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; + + // Now put the block into the Avail list. + + rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); + pSCache = NULL; + + if( RC_BAD( rc)) + { + 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( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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_XFLM_OK; + FLMUINT16 * pui16OffsetArray; + FLMUINT uiNumKeys; + FLMUINT uiEntrySize; + FLMBYTE * pucEntry; + FLMBOOL bDOBlock; + F_CachedBlock * pSCache = NULL; + FLMUINT uiBlkAddr; + FLMUINT uiCurOffset; + FLMUINT uiCounter; + FLMBYTE * pucEndOfHeap; + FLMBYTE * pucStartOfHeap; + F_BTREE_BLK_HDR * pBlkHdr; + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + pBlkHdr = m_pStack->pBlkHdr = + (F_BTREE_BLK_HDR *)m_pStack->pSCache->getBlockPtr(); + m_pStack->pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pBlkHdr, 0); + uiNumKeys = pBlkHdr->ui16NumKeys; + + if( !uiNumKeys) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + // Get the next block address (if any) + + uiBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; + + // Now put the block into the Avail list. + + rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); + pSCache = NULL; + + if( RC_BAD( rc)) + { + 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( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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, + F_CachedBlock ** ppPrevSCache, + FLMBOOL * pbEntriesWereMoved) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLocalAvailSpace; + FLMUINT uiAvailSpace; + FLMUINT uiHeapSize; + F_CachedBlock * pPrevSCache = NULL; + FLMUINT uiPrevBlkAddr; + FLMUINT uiOAEntrySize = 0; + FLMUINT uiStart; + FLMUINT uiFinish; + FLMUINT uiCount; + FLMUINT uiOffset; + + // 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 = + m_pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain) == 0) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiPrevBlkAddr, NULL, &pPrevSCache))) + { + goto Exit; + } + + uiLocalAvailSpace = m_pStack->pBlkHdr->stdBlkHdr.ui16BlkBytesAvail; + uiAvailSpace = pPrevSCache->m_pBlkHdr->ui16BlkBytesAvail; + uiHeapSize = ((F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr)->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( &pPrevSCache))) + { + 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, &pPrevSCache))) + { + goto Exit; + } + + // We will need to return this block. + + *ppPrevSCache = pPrevSCache; + pPrevSCache = 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 (pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: This method will move entries beginning at uiStart, up to and + including uiFinish from the current block (m_pStack) to pPrevSCache. + 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, + F_CachedBlock ** ppPrevSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT16 * pui16DstOffsetA = NULL; + F_BTREE_BLK_HDR * pSrcBlkHdr = NULL; + F_BTREE_BLK_HDR * pDstBlkHdr = NULL; + FLMBYTE * pucSrcEntry; + FLMBYTE * pucDstEntry; + FLMUINT uiEntrySize; + FLMUINT uiIndex; + F_CachedBlock * pPrevSCache; + FLMBOOL bEntriesCombined = FALSE; + + // 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_pDb->m_pDatabase->logPhysBlk( m_pDb, ppPrevSCache))) + { + goto Exit; + } + + pPrevSCache = *ppPrevSCache; + pSrcBlkHdr = m_pStack->pBlkHdr; + pDstBlkHdr = (F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr; + pui16DstOffsetA = BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0); + + pucDstEntry = getBlockEnd( pDstBlkHdr); + + // 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))) + { + goto Exit; + } + + if( bEntriesCombined) + { + F_BTSK tmpStack; + F_BTSK_p pTmpStack; + + tmpStack.pSCache = pPrevSCache; + 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( &pPrevSCache))) + { + goto Exit; + } + } + + pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; + f_memcpy( pucDstEntry, m_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: + + 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_XFLM_OK; + FLMUINT uiLocalAvailSpace; + FLMUINT uiAvailSpace; + FLMUINT uiHeapSize; + F_CachedBlock * pNextSCache = NULL; + FLMUINT uiNextBlkAddr; + FLMUINT uiOAEntrySize = 0; + FLMUINT uiStart; + FLMUINT uiFinish; + FLMUINT uiCount; + FLMUINT uiOffset; + F_CachedBlock * pChildSCache = NULL; + F_CachedBlock * pParentSCache = NULL; + F_BTSK_p pParentStack; + FLMUINT uiLevel; + FLMBOOL bReleaseChild = FALSE; + FLMBOOL bReleaseParent = FALSE; + FLMBOOL bCommonParent = FALSE; + + // Assume nothing to move. + + *pbEntriesWereMoved = FALSE; + + // Get the next block. + + if( (uiNextBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain) == 0) + { + goto Exit; + } + + if( (FLMUINT16)m_pStack->uiCurOffset >= (m_pStack->pBlkHdr->ui16NumKeys - 1)) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiNextBlkAddr, NULL, &pNextSCache))) + { + 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 = pNextSCache->m_pBlkHdr->ui16BlkBytesAvail; + uiHeapSize = ((F_BTREE_BLK_HDR *)pNextSCache->m_pBlkHdr)->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( &pNextSCache))) + { + 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, + &pNextSCache))) + { + 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 pNextSCache + // block as the child. Otherwise, we want to use the previous parent + // block as the child. + + if( uiLevel == m_pStack->uiLevel) + { + pChildSCache = pNextSCache; + bReleaseChild = TRUE; + pNextSCache = NULL; + } + else + { + pChildSCache = pParentSCache; + 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))) + { + pParentSCache = pParentStack->pSCache; + bReleaseParent = FALSE; + + if (RC_BAD( rc = updateParentCounts( pChildSCache, &pParentSCache, + (bCommonParent + ? pParentStack->uiCurOffset + : pParentStack->uiCurOffset + 1)))) + { + goto Exit; + } + + // The parent has changed, so update the stack. + + pParentStack->pBlkHdr = + (F_BTREE_BLK_HDR *)pParentSCache->m_pBlkHdr; + + pParentStack->pSCache = pParentSCache; + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiNextBlkAddr, NULL, &pParentSCache))) + { + goto Exit; + } + + bReleaseParent = TRUE; + + if( RC_BAD( rc = updateParentCounts( pChildSCache, + &pParentSCache, 0))) + { + goto Exit; + } + } + + if( bReleaseChild) + { + ScaReleaseCache( pChildSCache, FALSE); + pChildSCache = NULL; + bReleaseChild = FALSE; + } + } + } + + *pbEntriesWereMoved = TRUE; + +Exit: + + if( pChildSCache && bReleaseChild) + { + ScaReleaseCache( pChildSCache, FALSE); + } + + if( pParentSCache && bReleaseParent) + { + ScaReleaseCache( pParentSCache, FALSE); + } + + if( pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: This method will move entries beginning at uiStart, down to and + including uiFinish from the current block (m_pStack) to pNextSCache. + 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, + F_CachedBlock ** ppNextSCache) +{ + RCODE rc = NE_XFLM_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; + F_CachedBlock * pNextSCache = *ppNextSCache; + FLMBOOL bEntriesCombined; + FLMBYTE * pucOffsetArray; + + // 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_pDb->m_pDatabase->logPhysBlk( m_pDb, &pNextSCache))) + { + goto Exit; + } + + // SCache block may have changed. Need to pass it back. + + *ppNextSCache = pNextSCache; + + pSrcBlkHdr = m_pStack->pBlkHdr; + pDstBlkHdr = (F_BTREE_BLK_HDR *)pNextSCache->m_pBlkHdr; + + // 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 > m_uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + pui16DstOffsetA = BtOffsetArray((FLMBYTE *)pDstBlkHdr, 0); + pucOffsetArray = &m_pucBuffer[ m_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))) + { + goto Exit; + } + + if( bEntriesCombined) + { + F_BTSK tmpStack; + F_BTSK_p pTmpStack; + + tmpStack.pSCache = pNextSCache; + 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( &pNextSCache))) + { + goto Exit; + } + + // Refresh the saved offset array. + + uiBytesToCopy -= 2; + pucOffsetArray = &m_pucBuffer[ m_uiBufferSize] - uiBytesToCopy; + + f_memcpy( pucOffsetArray, + (FLMBYTE *)BtOffsetArray( (FLMBYTE *)pDstBlkHdr, 0), + uiBytesToCopy); + } + + pucDstEntry = getBlockEnd( pDstBlkHdr) - uiEntrySize; + f_memcpy( pucDstEntry, m_pucTempBlk, 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, + &m_pucBuffer[ m_uiBufferSize] - (FLMBYTE *)pui16DstOffsetA); + + // Now remove the entries from the Src block. + + if( RC_BAD( rc = removeRange( uiFinish, uiStart, FALSE))) + { + goto Exit; + } + +Exit: + + 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_XFLM_EOF_HIT will be returned. +****************************************************************************/ +RCODE F_Btree::advanceToNextElement( + FLMBOOL bAdvanceStack) +{ + RCODE rc = NE_XFLM_OK; + F_BTREE_BLK_HDR * pBlkHdr; + + flmAssert( m_pSCache); + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; + + 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_pSCache))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; + m_uiPrimaryOffset = 0; + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = 0; + + if( bAdvanceStack) + { + if( RC_BAD( rc = moveStackToNext( m_pSCache))) + { + goto Exit; + } + + // This block now has two uses. It will be released twice. + + m_pSCache->m_uiUseCount++; + } + } + else + { + m_uiPrimaryOffset++; + m_uiCurOffset++; + m_pStack->uiCurOffset++; + } + +Exit: + + // We do not want to release the m_pSCache here. That is to be done by the + // caller. + + 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_XFLM_BOF_HIT will be returned. +****************************************************************************/ +RCODE F_Btree::backupToPrevElement( + FLMBOOL bBackupStack) +{ + RCODE rc = NE_XFLM_OK; + F_BTREE_BLK_HDR * pBlkHdr; + + flmAssert( m_pSCache); + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; + + 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_pSCache))) + { + goto Exit; + } + + m_ui32PrimaryBlkAddr = m_pSCache->m_pBlkHdr->ui32BlkAddr; + + m_uiPrimaryOffset = + ((F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr)->ui16NumKeys - 1; + + m_ui32CurBlkAddr = m_ui32PrimaryBlkAddr; + m_uiCurOffset = m_uiPrimaryOffset; + + if( bBackupStack) + { + if( RC_BAD( rc = moveStackToPrev( m_pSCache))) + { + goto Exit; + } + + // This block now has two uses. It will be released twice. + + m_pSCache->m_uiUseCount++; + } + } + else + { + m_uiPrimaryOffset--; + m_uiCurOffset--; + m_pStack->uiCurOffset--; + } + +Exit: + + // We do not want to release the m_pSCache here. That is to be done by the + // caller. + + 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_XFLM_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_XFLM_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_XFLM_OK; + F_BTSK * pStack = NULL; + FLMUINT32 ui32BlkAddress; + F_CachedBlock * pSCache = 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 == XFLM_FIRST || uiMatch == XFLM_LAST) + { + uiKeyLen = 0; + } + + if( uiKeyLen > MAX_KEY_SIZ) + { + rc = RC_SET( NE_XFLM_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_pLFile->uiRootBlk; + + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddress, NULL, &pSCache))) + { + goto Exit; + } + + // We are building the stack inverted to make traversing it a bit easier. + + uiLevel = ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BlkLevel; + pStack = &m_Stack[ uiLevel]; + + m_uiStackLevels++; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + pStack->ui32BlkAddr = ui32BlkAddress; + pStack->pSCache = pSCache; + pSCache = 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_XFLM_NOT_FOUND) && (rc != NE_XFLM_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 == XFLM_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( + (FLMBYTE *)pStack->pSCache->m_pBlkHdr, 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_XFLM_NOT_FOUND) || (rc == NE_XFLM_EOF_HIT)) + { + if( pStack) + { + m_pStack = pStack; + } + } + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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 XFLM_FIRST & XFLM_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_XFLM_OK; + F_BTSK * pStack; + F_CachedBlock * pSCache = NULL; + FLMBYTE * pucEntry; + const FLMBYTE * pucBlkKey; + FLMUINT uiBlkKeyLen; + + // 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + *pui32BlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + if( !blkIsBTree( pSCache->getBlockPtr())) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + // Verify that the block belongs to the correct collection number + + if( ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui16LogicalFile != + m_pLFile->uiLfNum) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + // Verify that we are looking at the same type of block, + // i.e. collection vs index. + + if( ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BTreeFlags & BLK_IS_INDEX && + m_pLFile->eLfType != XFLM_LF_INDEX) + { + rc = RC_SET( NE_XFLM_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 *)pSCache->m_pBlkHdr)->ui8BlkLevel != 0) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + pStack = &m_Stack[ 0]; + m_uiStackLevels++; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + pStack->ui32BlkAddr = *pui32BlkAddr; + pStack->pSCache = pSCache; + pSCache = 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); + + // VISIT: These counts aren't accurate in this case. + + *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( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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 XFLM_FIRST & XFLM_LAST + values will ignore the key altogether and just return the first or last + key respectively. The XFLM_INCL value will return the key if found or the + first key following if not found. The XFLM_EXACT will return an + NE_XFLM_NOT_FOUND if the key cannot be found. XFLM_EXCL will return + the first key following the target key. +****************************************************************************/ +RCODE F_Btree::scanBlock( + F_BTSK_p pStack, + FLMUINT uiMatch) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTop; + FLMUINT uiMid; + FLMUINT uiBottom; + FLMINT iResult; + F_CachedBlock * pSCache = NULL; + const FLMBYTE * pucBlockKey; + FLMBYTE * pucEntry; + FLMUINT uiBlockKeyLen; + + if( pStack->pBlkHdr->ui16NumKeys == 0) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + + uiTop = 0; + uiBottom = (FLMUINT)(pStack->pBlkHdr->ui16NumKeys - 1); + + if( uiMatch == XFLM_FIRST) + { + pStack->uiCurOffset = uiTop; + goto Exit; + } + + if( uiMatch == XFLM_LAST || pStack->uiKeyLen == 0) + { + pStack->uiCurOffset = uiBottom; + goto Exit; + } + + flmAssert( uiMatch == XFLM_INCL || uiMatch == XFLM_EXCL || + uiMatch == XFLM_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 == XFLM_EXACT) + { + rc = RC_SET( NE_XFLM_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 != XFLM_INCL) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + } + + uiMid = uiBottom; + goto VerifyPosition; + } + +ResultGreater2: + + for( ;;) + { + + if( uiTop == uiBottom) + { + // We're done - didn't find it. + + if( uiMatch == XFLM_EXACT) + { + rc = RC_SET( NE_XFLM_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 != XFLM_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 == XFLM_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_XFLM_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_XFLM_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_XFLM_EOF_HIT); + } + } + else + { + pStack->uiCurOffset = uiMid; + } + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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_XFLM_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_XFLM_OK; + F_BTSK * pStack = NULL; + FLMUINT32 ui32BlkAddress; + F_CachedBlock * pSCache = 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_pLFile->uiRootBlk; + + // 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddress, NULL, &pSCache))) + { + goto Exit; + } + + uiLevel = ((F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr)->ui8BlkLevel; + pStack = &m_Stack[ uiLevel]; + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + pStack->ui32BlkAddr = ui32BlkAddress; + pStack->pSCache = pSCache; + pSCache = 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_XFLM_NOT_FOUND) || (rc == NE_XFLM_EOF_HIT)) + { + m_pStack = pStack; + } + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Btree::searchBlock( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT * puiPrevCounts, + FLMUINT uiPosition, + FLMUINT * puiOffset) +{ + RCODE rc = NE_XFLM_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_XFLM_BTREE_ERROR); + } + + *puiOffset = uiOffset; + return( rc); +} + +/*************************************************************************** +Desc: Method to move all the data in the block into a contiguous space. +****************************************************************************/ +RCODE F_Btree::defragmentBlock( + F_CachedBlock ** ppSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumKeys; + FLMBOOL bSorted; + FLMBYTE * pucCurEntry; + FLMBYTE * pucPrevEntry; + FLMBYTE * pucTempEntry; + FLMUINT uiTempToMove; + FLMUINT uiIndex; + FLMUINT uiAmtToMove; + FLMUINT uiFirstHole; + FLMUINT16 ui16BlkBytesAvail; + FLMUINT16 * pui16OffsetArray; + F_CachedBlock * pSCache = *ppSCache; + F_BTREE_BLK_HDR * pBlk = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + F_BTREE_BLK_HDR * pOldBlk = NULL; + FLMBYTE * pucHeap; + FLMBYTE * pucBlkEnd; + F_CachedBlock * pOldSCache = NULL; + + flmAssert( pBlk->stdBlkHdr.ui16BlkBytesAvail != pBlk->ui16HeapSize); + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( + m_pDb, &pSCache, &pOldSCache))) + { + goto Exit; + } + + pBlk = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + *ppSCache = pSCache; + 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) + { + 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( !pOldSCache) + { + f_memcpy( m_pucTempDefragBlk, pSCache->m_pBlkHdr, m_uiBlockSize); + pOldBlk = (F_BTREE_BLK_HDR *)m_pucTempDefragBlk; + } + else + { + pOldBlk = (F_BTREE_BLK_HDR *)pOldSCache->m_pBlkHdr; + } + + 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( pOldSCache) + { + ScaReleaseCache( pOldSCache, FALSE); + } + + 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_XFLM_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, XFLM_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, XFLM_EXCL); + + // We could find this entry. If we get back anything other than + // an NE_XFLM_EOF_HIT or NE_XFLM_OK, then there is a problem. + + if( rc != NE_XFLM_OK && rc != NE_XFLM_EOF_HIT && + rc != NE_XFLM_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_XFLM_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_XFLM_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; + F_CachedBlock * pPrevSCache = 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->pSCache))) + { + 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, &pPrevSCache, + &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->pSCache))) + { + 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). + // + // VISIT: Should I assert that we are not at the leaf level + // if we get in here? + + 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( pPrevSCache))) + { + 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 (pPrevSCache). + + pucEntry = BtEntry( (FLMBYTE *)m_pStack->pBlkHdr, m_pStack->uiCurOffset); + + *puiKeyLen = getEntryKeyLength( pucEntry, + pPrevSCache->m_pBlkHdr->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->pSCache))) + { + 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_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, m_pStack->pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, + NULL, &pPrevSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = moveStackToPrev( pPrevSCache))) + { + 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_XFLM_OK; + FLMUINT uiBlkType = m_pStack->pSCache->m_pBlkHdr->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 + // pSCache could change, we must update the block header after the call. + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + pBlk = m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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_XFLM_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_XFLM_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_XFLM_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; + + 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 + { + // 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( + m_pStack->pSCache->m_pBlkHdr->ui8BlkType, + uiFlags, *ppucKey, *puiKeyLen, pucValue, uiLen, uiOADataLen, + *puiChildBlkAddr, *puiCounts, m_pucTempBlk, m_uiBlockSize, + &uiEntrySize))) + { + goto Exit; + } + + if( RC_BAD( rc = replace( m_pucTempBlk, 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, XFLM_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 && + m_pStack->pSCache->m_pBlkHdr->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->pSCache))) + { + 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: + + 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_XFLM_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_XFLM_OK; + FLMBYTE * pucReplaceAt; + FLMUINT uiNumKeys; + FLMBYTE * pBlk; + FLMUINT uiOldEntrySize; + + *pbLastEntry = FALSE; + + // Log this block before making any changes to it. Since the + // pSCache could change, we must update the block header after the call. + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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 pSCache block. 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( + F_CachedBlock * pSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkAddr; + F_BTREE_BLK_HDR * pBlkHdr; + F_BTSK_p pStack = m_pStack; + F_CachedBlock * pPrevSCache = NULL; + + if( pSCache) + { + if( pStack->pSCache) + { + // Make sure the block we passed in really is the previous + // block in the chain. + + if( pSCache->m_uiBlkAddress != + pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + // Cannot be the same block. + + if( pSCache == pStack->pSCache) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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. + + ScaReleaseCache( pStack->pSCache, FALSE); + } + + pStack->pSCache = pSCache; + pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + 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->pSCache == NULL) + { + // Don't continue if we don't have this level in the stack. + + if( pStack->ui32BlkAddr == 0) + { + break; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pStack->ui32BlkAddr, NULL, &pStack->pSCache))) + { + goto Exit; + } + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; + } + + // 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiBlkAddr, NULL, &pPrevSCache))) + { + goto Exit; + } + + // Release the old block + + ScaReleaseCache( pStack->pSCache, FALSE); + + pBlkHdr = (F_BTREE_BLK_HDR *)pPrevSCache->m_pBlkHdr; + pStack->pSCache = pPrevSCache; + pPrevSCache = 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_XFLM_BOF_HIT); + goto Exit; + } + } + else + { + pStack->uiCurOffset--; // Previous Entry + break; + } + + pStack++; + } + +Exit: + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to rebuild the stack so that it references the parentage of + the parameter pSCache block. 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( + F_CachedBlock * pSCache, + FLMBOOL bReleaseCurrent) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkAddr; + F_BTREE_BLK_HDR * pBlkHdr; + F_BTSK_p pStack = m_pStack; + F_CachedBlock * pNextSCache = NULL; + + if( pSCache) + { + if( pStack->pSCache) + { + // Make sure the block we passed in really is the next in chain. + + if( pSCache->m_uiBlkAddress != + pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + // Cannot be the same block. + + if( pSCache == pStack->pSCache) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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) + { + ScaReleaseCache( pStack->pSCache, FALSE); + } + } + + pStack->pSCache = pSCache; + pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + 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->pSCache == NULL) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pStack->ui32BlkAddr, NULL, &pStack->pSCache))) + { + goto Exit; + } + + pStack->pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; + } + + // 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->pSCache))) + { + goto Exit; + } + + pBlkHdr = (F_BTREE_BLK_HDR *)pStack->pSCache->m_pBlkHdr; + 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_XFLM_BTREE_ERROR); + goto Exit; + } + } + else + { + pStack->uiCurOffset++; // Next Entry + break; + } + + pStack++; + } + +Exit: + + if( pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + + 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_XFLM_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_XFLM_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_XFLM_OK; + BTREE_REPLACE_STRUCT * pPrev; + F_BTSK_p pStack = m_pStack; + const FLMBYTE * pucParentKey; + FLMBYTE * pucEntry; + + if( m_uiReplaceLevels + 1 >= BH_MAX_LEVELS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_OK; + RCODE rcTmp = NE_XFLM_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, XFLM_EXACT); + + m_uiSearchLevel = uiSearchLevel; + + if ((rcTmp != NE_XFLM_OK) && + (rcTmp != NE_XFLM_NOT_FOUND) && + (rcTmp != NE_XFLM_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_XFLM_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_XFLM_BTREE_ERROR); + goto Exit; + } + } + else + { + break; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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_XFLM_EOF_HIT); + goto Exit; + } + + if( uiKeyLen <= uiKeyBufSize) + { + f_memcpy( pucKey, pucKeyRV, uiKeyLen); + *puiKeyLen = uiKeyLen; + } + else + { + rc = RC_SET( NE_XFLM_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_pSCache 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_XFLM_OK; + FLMBYTE * pucDestPtr = pucBuffer; + FLMUINT32 ui32BlkAddr = 0; + FLMBOOL bNewBlock; + FLMUINT uiDataLen = 0; + + flmAssert( m_pSCache); + + 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_XFLM_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_pSCache->m_pBlkHdr)->ui16NumKeys - 1))) + { + m_uiCurOffset++; + bNewBlock = FALSE; + } + else + { + // Get the next block address + + ui32BlkAddr = m_pSCache->m_pBlkHdr->ui32NextBlkInChain; + + // Release the current block before we get the next one. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, + m_pSCache->m_ui64HighTransID); + + m_ui64LastBlkTransId = m_pSCache->m_pBlkHdr->ui64TransID; + bNewBlock = TRUE; + } + + // If this is a data only block, then we can get the local data size + // from the header. + + if( m_bDataOnlyBlock) + { + flmAssert( m_pSCache->m_pBlkHdr->ui8BlkType == BT_DATA_ONLY); + + m_pucDataPtr = (FLMBYTE *)m_pSCache->m_pBlkHdr + + sizeofDOBlkHdr( m_pSCache->m_pBlkHdr); + + m_uiDataRemaining = m_uiBlockSize - + sizeofDOBlkHdr( m_pSCache->m_pBlkHdr) - + m_pSCache->m_pBlkHdr->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_pSCache->m_pBlkHdr; + + if( pBlkHdr->ui16NumKeys == 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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_pSCache) + { + // We must release the SCache block + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_OK; + F_CachedBlock * pSCache = NULL; + const FLMBYTE * pucData; + + // 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; + + // Watch the transaction id and the transaction count during streaming + // read operations. If either changes after an initial read, then + // we abort the operation. + + m_ui64CurrTransID = m_pDb->m_ui64CurrTransID; + m_uiBlkChangeCnt = m_pDb->m_uiBlkChangeCnt; + m_ui64LastBlkTransId = pBlkHdr->ui64TransID; + m_ui64PrimaryBlkTransId = pBlkHdr->ui64TransID; + + // 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32DOBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + m_ui64LastBlkTransId = pSCache->m_pBlkHdr->ui64TransID; + + // Local amount of data in this block + + m_uiDataRemaining = m_uiBlockSize - + sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr) - + pSCache->m_pBlkHdr->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( pSCache->m_pBlkHdr->ui32PrevBlkInChain == 0) + { + FLMBYTE * pucPtr = (FLMBYTE *)pSCache->m_pBlkHdr + + sizeofDOBlkHdr((F_BLK_HDR *)pSCache->m_pBlkHdr); + 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. + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to remove extra entries after a replace operation. +****************************************************************************/ +RCODE F_Btree::removeRemainingEntries( + const FLMBYTE * pucKey, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_XFLM_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, XFLM_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_XFLM_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->pSCache->m_pBlkHdr; + + // 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, XFLM_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_XFLM_OK; + FLMUINT32 ui32PrevBlkAddr; + FLMUINT32 ui32NextBlkAddr; + F_CachedBlock * pSCache = 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_pDb->m_pDatabase->blockFree(m_pDb, m_pStack->pSCache); + + m_pStack->pSCache = NULL; + m_pStack->pBlkHdr = NULL; + + if( RC_BAD( rc)) + { + goto Exit; + } + + // Update the previous block. + + if( ui32PrevBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32PrevBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) + { + goto Exit; + } + + pSCache->m_pBlkHdr->ui32NextBlkInChain = ui32NextBlkAddr; + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + + // Update the next block + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) + { + goto Exit; + } + + pSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32PrevBlkAddr; + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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_XFLM_OK; + FLMUINT32 ui32NextBlkAddr; + F_CachedBlock * pSCache = NULL; + + ui32NextBlkAddr = ui32BlkAddr; + + while( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + flmAssert( getBlkType( (FLMBYTE *)pSCache->m_pBlkHdr) == BT_DATA_ONLY); + ui32NextBlkAddr = pSCache->m_pBlkHdr->ui32NextBlkInChain; + + rc = m_pDb->m_pDatabase->blockFree( m_pDb, pSCache); + pSCache = NULL; + + if( RC_BAD( rc)) + { + goto Exit; + } + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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_XFLM_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_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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_pSCache, 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_XFLM_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: + + // Only release the m_pSCache if the use count is greater than 1. It is + // pointed to by the stack also. + + if( m_pSCache && m_pSCache->m_uiUseCount > 1) + { + ScaReleaseCache( m_pSCache, FALSE); + } + + m_pSCache = 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_XFLM_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_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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_pSCache, so + // let's put it there f or 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_XFLM_BTREE_ERROR); + goto Exit; + } + } + + *peAction = ELM_DONE; + +Exit: + + // Only release the m_pSCache if the use count is greater than 1. It is + // pointed to by the stack also. + + if( m_pSCache && m_pSCache->m_uiUseCount > 1) + { + ScaReleaseCache( m_pSCache, FALSE); + } + + m_pSCache = 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( + F_CachedBlock ** ppSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT32 ui32BlkAddr; + + ui32BlkAddr = (*ppSCache)->m_pBlkHdr->ui32NextBlkInChain; + + ScaReleaseCache( *ppSCache, FALSE); + *ppSCache = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddr, NULL, ppSCache))) + { + 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( + F_CachedBlock ** ppSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT32 ui32BlkAddr; + + ui32BlkAddr = (*ppSCache)->m_pBlkHdr->ui32PrevBlkInChain; + + ScaReleaseCache( *ppSCache, FALSE); + *ppSCache = NULL; + + if( ui32BlkAddr == 0) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddr, NULL, ppSCache))) + { + 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_XFLM_OK; + FLMUINT uiLevel; + + for( uiLevel = m_pStack->uiLevel; + uiLevel < m_uiStackLevels - 1; + uiLevel++) + { + if( RC_BAD( rc = updateParentCounts( m_Stack[ uiLevel].pSCache, + &m_Stack[ uiLevel + 1].pSCache, m_Stack[ uiLevel + 1].uiCurOffset))) + { + goto Exit; + } + + m_Stack[ uiLevel + 1].pBlkHdr = + (F_BTREE_BLK_HDR *)m_Stack[ uiLevel + 1].pSCache->m_pBlkHdr; + } + +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_XFLM_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->pSCache))) + { + 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_XFLM_OK; + F_CachedBlock * pParentSCache = NULL; + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, m_pLFile->uiRootBlk, NULL, &pParentSCache))) + { + goto Exit; + } + + if( (pParentSCache->m_pBlkHdr->ui8BlkType == BT_NON_LEAF) || + (pParentSCache->m_pBlkHdr->ui8BlkType == BT_NON_LEAF_COUNTS)) + { + if( RC_BAD( rc = verifyChildLinks( pParentSCache))) + { + goto Exit; + } + } + +Exit: + + if( pParentSCache) + { + ScaReleaseCache( pParentSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Private method (recursive) that checks the child links in the given + blocks to ensure they are correct. +****************************************************************************/ +RCODE F_Btree::verifyChildLinks( + F_CachedBlock * pParentSCache) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumKeys; + F_CachedBlock * pChildSCache = 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 *)pParentSCache->m_pBlkHdr; + 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_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, ui32BlkAddr, NULL, &pChildSCache))) + { + goto Exit; + } + + pChildBlkHdr = (F_BTREE_BLK_HDR *)pChildSCache->m_pBlkHdr; + + // 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_XFLM_BTREE_ERROR); + goto Exit; + } + + if( f_memcmp( pucParentKey, pucChildKey, uiParentKeyLen) != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + if( (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF) || + (pChildBlkHdr->stdBlkHdr.ui8BlkType == BT_NON_LEAF_COUNTS)) + { + if( RC_BAD( rc = verifyChildLinks( pChildSCache))) + { + goto Exit; + } + } + + ScaReleaseCache( pChildSCache, FALSE); + pChildSCache = NULL; + } + +Exit: + + if( pChildSCache) + { + ScaReleaseCache( pChildSCache, FALSE); + } + + 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_p pFromStack, + F_BTSK_p pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness) +{ + RCODE rc = NE_XFLM_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_p pStack, + FLMUINT uiFirstOffset, + FLMUINT uiLastOffset, + FLMUINT * puiKeyCount, + FLMUINT * puiElementCount) +{ + RCODE rc = NE_XFLM_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_p pFromStack, + F_BTSK_p pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness) +{ + RCODE rc = NE_XFLM_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_p pStack1, + F_BTSK_p pStack2) +{ + RCODE rc = NE_XFLM_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_pLFile->uiRootBlk; + } + + if( pStack2->uiLevel == m_uiRootLevel) + { + pStack2->ui32BlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; + } + + if( RC_BAD( m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, pStack1->ui32BlkAddr, NULL, &pStack1->pSCache))) + { + goto Exit; + } + + pStack1->pBlkHdr = (F_BTREE_BLK_HDR *)pStack1->pSCache->m_pBlkHdr; + + if( RC_BAD( m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, pStack2->ui32BlkAddr, NULL, &pStack2->pSCache))) + { + goto Exit; + } + + pStack2->pBlkHdr = (F_BTREE_BLK_HDR *)pStack2->pSCache->m_pBlkHdr; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Method to tally the counts in a block between (inclusive) the + uiFromOffset & uiUntilOffset parameters. +****************************************************************************/ +FLMUINT F_Btree::countRangeOfKeys( + F_BTSK_p 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_p pFromStack, + F_BTSK_p 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_XFLM_OK; + FLMUINT32 ui32PrevBlkAddr; + F_CachedBlock * pPrevSCache = NULL; + FLMUINT32 ui32NextBlkAddr; + F_CachedBlock * pNextSCache = NULL; + + *pbMergedWithPrev = FALSE; + *pbMergedWithNext = FALSE; + + // Our first check is to see if we can merge the current block with its + // previous block. + + ui32PrevBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32PrevBlkInChain; + if( ui32PrevBlkAddr) + { + // Get the block. + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, ui32PrevBlkAddr, NULL, &pPrevSCache))) + { + goto Exit; + } + + // Is there room to merge? + + if( (FLMUINT)(pPrevSCache->m_pBlkHdr->ui16BlkBytesAvail + + m_pStack->pSCache->m_pBlkHdr->ui16BlkBytesAvail) >= + (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( + (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_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( &pPrevSCache, &m_pStack->pSCache))) + { + goto Exit; + } + + // Save the changed block header address + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_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( pPrevSCache))) + { + goto Exit; + } + pPrevSCache = 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. + + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = NULL; + } + } + + // Can we merge with the next block? + + ui32NextBlkAddr = m_pStack->pSCache->m_pBlkHdr->ui32NextBlkInChain; + if( ui32NextBlkAddr) + { + // Get the block. + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, ui32NextBlkAddr, NULL, &pNextSCache))) + { + goto Exit; + } + + // Is there room to merge? + + if( (FLMUINT)(pNextSCache->m_pBlkHdr->ui16BlkBytesAvail + + m_pStack->pSCache->m_pBlkHdr->ui16BlkBytesAvail) >= + (FLMUINT)(m_uiBlockSize - sizeofBTreeBlkHdr( + (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr))) + { + // Looks like we can merge these two. + + if( RC_BAD( rc = merge( &m_pStack->pSCache, &pNextSCache))) + { + goto Exit; + } + + // Save the changed block header address. + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + + // Update the counts for the current block and the next block. + + if( m_bCounts) + { + pPrevSCache = m_pStack->pSCache; + + // 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( pNextSCache, FALSE))) + { + goto Exit; + } + pNextSCache = 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( pPrevSCache))) + { + goto Exit; + } + + pPrevSCache = 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. + + ScaReleaseCache( pNextSCache, FALSE); + pNextSCache = NULL; + } + } + +Exit: + + if( *pbMergedWithPrev || *pbMergedWithNext) + { + if( m_pDb->m_pDbStats != NULL) + { + XFLM_LFILE_STATS * pLFileStats; + + if( (pLFileStats = m_pDb->getLFileStatPtr( m_pLFile)) != NULL) + { + pLFileStats->bHaveStats = TRUE; + pLFileStats->ui64BlockCombines++; + } + } + } + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + if( pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Method to move the contents of the ppFromSCache block into the + ppToSCache block. Note that all merges are a move to next operation. +****************************************************************************/ +RCODE F_Btree::merge( + F_CachedBlock ** ppFromSCache, + F_CachedBlock ** ppToSCache) +{ + RCODE rc = NE_XFLM_OK; + F_BTSK tempStack; + F_BTSK_p pStack = NULL; + F_CachedBlock * pSCache; + F_BTREE_BLK_HDR * pBlkHdr; + + // May need to defragment the blocks first. + + pBlkHdr = (F_BTREE_BLK_HDR *)(*ppToSCache)->m_pBlkHdr; + if( pBlkHdr->stdBlkHdr.ui16BlkBytesAvail != pBlkHdr->ui16HeapSize) + { + if( RC_BAD( rc = defragmentBlock( ppToSCache))) + { + goto Exit; + } + } + + // Make a temporary stack entry so we can "fool" the moveToNext + // function into moving the entries for us. + + pSCache = *ppFromSCache; + tempStack.pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->m_pBlkHdr; + tempStack.ui32BlkAddr = pSCache->m_pBlkHdr->ui32BlkAddr; + tempStack.pSCache = pSCache; + tempStack.uiCurOffset = 0; + tempStack.uiLevel = m_pStack->uiLevel; + tempStack.pui16OffsetArray = BtOffsetArray( (FLMBYTE *)pSCache->m_pBlkHdr, 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, ppToSCache))) + { + goto Exit; + } + + // Return the changed block structure + + *ppFromSCache = tempStack.pSCache; + +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. If they can, then they will be combined and stored in the + m_pucTempBlk buffer. +****************************************************************************/ +RCODE F_Btree::combineEntries( + F_BTREE_BLK_HDR * pSrcBlkHdr, + FLMUINT uiSrcOffset, + F_BTREE_BLK_HDR * pDstBlkHdr, + FLMUINT uiDstOffset, + FLMBOOL * pbEntriesCombined, + FLMUINT * puiEntrySize) +{ + RCODE rc = NE_XFLM_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_XFLM_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 = &m_pucTempBlk[ 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; + } + + m_pucTempBlk[ 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_XFLM_OK; + FLMUINT uiType; + + if( !m_bOpened || m_bSetupForRead || m_bSetupForReplace || + (m_bSetupForWrite)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( m_uiSearchLevel >= BH_MAX_LEVELS); + + // Verify the Txn type + + if( m_pDb->m_eTransType != XFLM_UPDATE_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( m_pDb->m_eTransType == XFLM_NO_TRANS + ? NE_XFLM_NO_TRANS_ACTIVE + : NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32FromBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + // Find out if this is a Btree block or a DO block. + + uiType = getBlkType((FLMBYTE *)m_pSCache->m_pBlkHdr); + + if( uiType == BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_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; + F_CachedBlock * pSCache = NULL; + FLMUINT uiKeyLen; + + // m_pSCache has already been retrieved. + + flmAssert( m_pSCache); + + pBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; + 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. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + if( RC_BAD( rc = findEntry( pucKey, uiKeyLen, XFLM_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_XFLM_BTREE_ERROR); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32ToBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr) != BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + + pNewBlkHdr = (F_BTREE_BLK_HDR *)m_pSCache->m_pBlkHdr; + + 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->ui16LogicalFile = pBlkHdr->ui16LogicalFile; + 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) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pBlkHdr->stdBlkHdr.ui32PrevBlkInChain, NULL, &pSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) + { + goto Exit; + } + + flmAssert( pSCache->m_pBlkHdr->ui32NextBlkInChain == ui32FromBlkAddr); + pSCache->m_pBlkHdr->ui32NextBlkInChain = ui32ToBlkAddr; + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + + if( pBlkHdr->stdBlkHdr.ui32NextBlkInChain) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pBlkHdr->stdBlkHdr.ui32NextBlkInChain, NULL, &pSCache))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) + { + goto Exit; + } + + flmAssert( pSCache->m_pBlkHdr->ui32PrevBlkInChain == ui32FromBlkAddr); + pSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32ToBlkAddr; + + ScaReleaseCache( pSCache, FALSE); + pSCache = 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)) + { + // We need to update LFile to reflect the new root block address. + + F_COLLECTION * pCollection = NULL; + F_Database * pDatabase= m_pDb->m_pDatabase; + + m_pLFile->uiRootBlk = ui32ToBlkAddr; + + if( m_pLFile->eLfType == XFLM_LF_COLLECTION) + { + // Cannot use collections in a temporary DB. + + flmAssert( !m_bTempDb); + + if( RC_BAD( rc = m_pDb->m_pDict->getCollection( m_pLFile->uiLfNum, + &pCollection))) + { + goto Exit; + } + } + + rc = pDatabase->lFileWrite( m_pDb, pCollection, m_pLFile); + goto Exit; + } + + // Move up one level to the parent entry. + + m_pStack++; + flmAssert( m_pStack->pSCache); + + // Log that we are making a change to the block. + + if( RC_BAD( rc = m_pDb->m_pDatabase->logPhysBlk( + m_pDb, &m_pStack->pSCache))) + { + goto Exit; + } + + m_pStack->pBlkHdr = (F_BTREE_BLK_HDR *)m_pStack->pSCache->m_pBlkHdr; + + // 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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + 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_XFLM_OK; + F_BLK_HDR * pBlkHdr = NULL; + F_BLK_HDR * pNewBlkHdr = NULL; + FLMBYTE * pucEntry; + FLMBYTE * pucKey = NULL; + FLMBYTE * pucSrc; + FLMBYTE * pucDest; + F_CachedBlock * pSCache = NULL; + F_CachedBlock * pPrevSCache = NULL; + F_CachedBlock * pNextSCache = NULL; + FLMUINT uiKeyLen; + FLMUINT uiOADataLen; + const FLMBYTE * pucData; + FLMUINT32 ui32DOBlkAddr; + FLMUINT uiDataLen; + FLMBYTE ucDataBuffer[ sizeof(FLMUINT32)]; + FLMUINT uiBlkHdrSize; + + // m_pSCache has already been retrieved. + + flmAssert( m_pSCache); + + // Log that we are changing this block. + + if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &m_pSCache))) + { + goto Exit; + } + pBlkHdr = m_pSCache->m_pBlkHdr; + + // Get the new block and verify that it is a free block. + + if( RC_BAD( m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32ToBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + if( getBlkType( (FLMBYTE *)pSCache->m_pBlkHdr) != BT_FREE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + // Update the header of the new block to point to the prev and next + // blocks etc.. + + if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pSCache))) + { + goto Exit; + } + + pNewBlkHdr = pSCache->m_pBlkHdr; + 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) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pBlkHdr->ui32PrevBlkInChain, NULL, &pPrevSCache))) + { + goto Exit; + } + + if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pPrevSCache))) + { + goto Exit; + } + + flmAssert( pPrevSCache->m_pBlkHdr->ui32NextBlkInChain == ui32FromBlkAddr); + pPrevSCache->m_pBlkHdr->ui32NextBlkInChain = ui32ToBlkAddr; + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = NULL; + } + + if( pBlkHdr->ui32NextBlkInChain) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + pBlkHdr->ui32NextBlkInChain, NULL, &pNextSCache))) + { + goto Exit; + } + + if( RC_BAD( m_pDb->m_pDatabase->logPhysBlk( m_pDb, &pNextSCache))) + { + goto Exit; + } + + flmAssert( pNextSCache->m_pBlkHdr->ui32PrevBlkInChain == ui32FromBlkAddr); + pNextSCache->m_pBlkHdr->ui32PrevBlkInChain = ui32ToBlkAddr; + ScaReleaseCache( pNextSCache, FALSE); + pNextSCache = 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, XFLM_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_XFLM_BTREE_ERROR); + goto Exit; + } + + uiDataLen = btGetEntryDataLength( pucEntry, &pucData, + &uiOADataLen, NULL); + + ui32DOBlkAddr = bteGetBlkAddr( pucData); + + if( ui32DOBlkAddr != ui32FromBlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + if( uiDataLen != sizeof( ucDataBuffer)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + if( pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + + 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_XFLM_OK; + FLMBYTE * pucEntry; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT32 ui32BlkAddr; + FLMBOOL bLastElement = FALSE; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_BTREE_ERROR); + goto Exit; + } + + // If the transaction Id or the Block Change Count has changed, + // we must re-sync ourselves. + + if( (m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt)) + { + + // Test to see if we really need to re-synch... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + ( m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // We must call btLocateEntry so we can re-initialize the read. + + if( !m_bFirstRead) + { + if( RC_BAD( rc = btLocateEntry( + pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) + { + goto Exit; + } + + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + else + { + rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); + 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_pSCache == NULL) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + } + + // The next case is when the new position is in a *previous* entry, possibly + // a previous block. + + while( uiPosition < m_uiOffsetAtStart) + { + pBlkHdr = m_pSCache->m_pBlkHdr; + + // Are we dealing with DataOnly blocks? + + if( m_bDataOnlyBlock) + { + ui32BlkAddr = pBlkHdr->ui32PrevBlkInChain; + flmAssert( ui32BlkAddr); + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + m_ui32CurBlkAddr = ui32BlkAddr; + pBlkHdr = m_pSCache->m_pBlkHdr; + + 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( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( + pucKey, uiKeyLen, &bLastElement, pucEntry, + getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr))) + { + // Should always match at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_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 = m_pSCache->m_pBlkHdr; + + // Are we dealing with DataOnly blocks? + + if( m_bDataOnlyBlock) + { + ui32BlkAddr = pBlkHdr->ui32NextBlkInChain; + flmAssert( ui32BlkAddr); + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32BlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + m_ui32CurBlkAddr = ui32BlkAddr; + pBlkHdr = m_pSCache->m_pBlkHdr; + + // 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( (FLMBYTE *)m_pSCache->m_pBlkHdr, m_uiCurOffset); + + // Make sure we are still looking at the same key etc. + + if( !checkContinuedEntry( + pucKey, uiKeyLen, &bLastElement, pucEntry, + getBlkType( (FLMBYTE *)m_pSCache->m_pBlkHdr))) + { + // Should always match at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_BTREE_ERROR); + goto Exit; + } + + m_uiDataRemaining = m_uiDataLength - (uiPosition - m_uiOffsetAtStart); + m_uiOADataRemaining = m_uiOADataLength - uiPosition; + updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, + m_pSCache->m_ui64HighTransID); + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + + releaseBlocks( FALSE); + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Btree::btGetReadPosition( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT * puiPosition) +{ + RCODE rc = NE_XFLM_OK; + + if( !m_bOpened || !m_bSetupForRead) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + + flmAssert( puiPosition); + + // If the transaction ID or the block change count has changed, + // we must re-sync ourselves. + + if( !m_bTempDb && + ((m_ui64CurrTransID != m_pDb->m_ui64CurrTransID) || + (m_uiBlkChangeCnt != m_pDb->m_uiBlkChangeCnt))) + { + // Test to see if we really need to re-sync ... + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + m_ui32CurBlkAddr, NULL, &m_pSCache))) + { + goto Exit; + } + + if( m_pSCache->m_pBlkHdr->ui64TransID != m_ui64LastBlkTransId || + (m_pDb->m_eTransType == XFLM_UPDATE_TRANS && + m_pDb->m_ui64CurrTransID == m_pSCache->m_pBlkHdr->ui64TransID)) + { + // We must call btLocateEntry so we can re-initialize the read + + if( !m_bFirstRead) + { + if( RC_BAD( rc = btLocateEntry( + pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) + { + goto Exit; + } + + // Will need a new version of this block. + + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = NULL; + } + else + { + rc = RC_SET(NE_XFLM_BTREE_BAD_STATE); + goto Exit; + } + } + } + + *puiPosition = m_uiOffsetAtStart + (m_uiDataLength - m_uiDataRemaining); + + if( m_pSCache) + { + updateTransInfo( m_pSCache->m_pBlkHdr->ui64TransID, + m_pSCache->m_ui64HighTransID); + } + +Exit: + + if( m_pSCache) + { + ScaReleaseCache( m_pSCache, FALSE); + m_pSCache = 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_XFLM_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; + F_CachedBlock * pCurrentBlk = NULL; + F_CachedBlock * pPrevSCache = NULL; + FLMBYTE * pBlk = NULL; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucPrevEntry = NULL; + F_CachedBlock * pChildBlk = NULL; + FLMUINT16 * puiOffsetArray; + BTREE_ERR_STRUCT localErrStruct; + FLMINT iCmpResult; + FLMUINT uiOADataLength = 0; + + // Verify the Txn type + + if( m_pDb->m_eTransType == XFLM_NO_TRANS && !m_bTempDb) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // Initial setup... + + ui32NextLevelBlkAddr = (FLMUINT32)m_pLFile->uiRootBlk; + 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &pCurrentBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, + "Failed to get block at %X", ui32NextBlkAddr); + goto Exit; + } + + pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; + 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( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = 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; + + // VISIT: Verify the block header fields + /* + ui32PrevBlkInChain = + ui32BlkCRC = + ui16BlkBytesAvail < ? + ui8BlkLevel?? + ui8BlkIsRoot?? + */ + + // Verify that the keys are in order... + // Make sure that we check the keys between blocks as well. + + if( pPrevSCache) + { + pucEntry = BtLastEntry( (FLMBYTE *)pPrevSCache->m_pBlkHdr); + uiPrevKeySize = getEntryKeyLength( pucEntry, getBlkType( + (FLMBYTE *)pPrevSCache->m_pBlkHdr), &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 = (pPrevSCache ? 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_XFLM_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_XFLM_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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32ChildBlkAddr, NULL, &pChildBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, "Failed to get block at %X", + ui32ChildBlkAddr); + goto Exit; + } + + ScaReleaseCache( pChildBlk, FALSE); + } + } + + // Release the current block and get the next one + + ui32NextBlkAddr = ((F_BLK_HDR *)pBlk)->ui32NextBlkInChain; + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + pPrevSCache = NULL; + } + + pPrevSCache = pCurrentBlk; + pCurrentBlk = NULL; + + if( ui32NextBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextBlkAddr, NULL, &pCurrentBlk))) + { + localErrStruct.type = SCA_GET_BLOCK_FAILED; + f_sprintf( localErrStruct.szMsg, + "Failed to get block at %X", ui32ChildBlkAddr); + goto Exit; + } + + pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; + } + } + } + + if( m_bCounts) + { + if( RC_BAD( rc = verifyCounts( &localErrStruct))) + { + goto Exit; + } + } + +Exit: + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + if( pCurrentBlk) + { + ScaReleaseCache( pCurrentBlk, FALSE); + } + + 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_XFLM_OK; + FLMUINT uiRunningLength = 0; // A running total of the DataLength fields + // for all of the blocks in this chain + F_CachedBlock * 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_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + ui32NextAddr, NULL, &pCurrentBlk))) + { + errStruct->type = SCA_GET_BLOCK_FAILED; + f_sprintf( errStruct->szMsg, "Failed to get block at %X", uiDOAddr); + goto Exit; + } + + pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; + + // Verify that it's really a DO Block + + if( getBlkType( pBlk) != BT_DATA_ONLY) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + errStruct->type = NOT_DATA_ONLY_BLOCK; + goto Exit; + } + + // Update counts info in errStruct + + errStruct->LevelStats[ errStruct->uiLevels - 1].uiDOBytesUsed += + m_uiBlockSize - pCurrentBlk->m_pBlkHdr->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. + + ScaReleaseCache( pCurrentBlk, FALSE); + pCurrentBlk = NULL; + } + + // Check the calculated overall length vs. uiDataLength + + if( uiRunningLength != uiDataLength) + { + errStruct->type = BAD_DO_BLOCK_LENGTHS; + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + +Exit: + + if( pCurrentBlk) + { + ScaReleaseCache( pCurrentBlk, FALSE); + } + + if( rc == NE_XFLM_BTREE_ERROR) + { + f_sprintf( errStruct->szMsg, "Corrupt DO chain starting at %X", uiDOAddr); + } + + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Method to check the counts in a database with counts. +****************************************************************************/ +RCODE F_Btree::verifyCounts( + BTREE_ERR_STRUCT * pErrStruct) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNextLevelBlkAddr; + FLMUINT uiNextBlkAddr; + FLMUINT uiChildBlkAddr; + F_CachedBlock * pCurrentBlk = NULL; + F_CachedBlock * 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_pLFile->uiRootBlk; + + while( uiNextLevelBlkAddr) + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, uiNextLevelBlkAddr, NULL, &pCurrentBlk))) + { + goto Exit; + } + + if( pCurrentBlk->m_pBlkHdr->ui8BlkType != BT_NON_LEAF_COUNTS) + { + ScaReleaseCache( pCurrentBlk, FALSE); + pCurrentBlk = NULL; + break; + } + + pucEntry = BtEntry( (FLMBYTE *)pCurrentBlk->m_pBlkHdr, 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) + { + uiNumKeys = ((F_BTREE_BLK_HDR *)pCurrentBlk->m_pBlkHdr)->ui16NumKeys; + pBlk = (FLMBYTE *)pCurrentBlk->m_pBlkHdr; + + // Now check every entry in this block. + + for( uiEntryNum = 0; uiEntryNum < uiNumKeys; uiEntryNum++) + { + pucEntry = BtEntry( pBlk, uiEntryNum); + uiChildBlkAddr = bteGetBlkAddr( pucEntry); + + pucEntry += 4; + uiParentCounts = FB2UD( pucEntry); + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, m_pLFile, + uiChildBlkAddr, NULL, &pChildBlk))) + { + goto Exit; + } + + uiChildCounts = countKeys( (FLMBYTE *)pChildBlk->m_pBlkHdr); + + if( uiChildCounts != uiParentCounts) + { + pErrStruct->type = BAD_COUNTS; + pErrStruct->uiBlkAddr = pChildBlk->m_pBlkHdr->ui32BlkAddr; + f_sprintf( + pErrStruct->szMsg, + "Counts do not match. Expected %d, got %d", + uiParentCounts, uiChildCounts); + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + ScaReleaseCache( pChildBlk, FALSE); + pChildBlk = NULL; + } + + // Now get the next block at this level. + + uiNextBlkAddr = pCurrentBlk->m_pBlkHdr->ui32NextBlkInChain; + ScaReleaseCache( pCurrentBlk, FALSE); + pCurrentBlk = NULL; + + if( uiNextBlkAddr == 0) + { + bDone = TRUE; + } + else + { + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( + m_pDb, m_pLFile, uiNextBlkAddr, NULL, &pCurrentBlk))) + { + goto Exit; + } + } + } + } + +Exit: + + if( pCurrentBlk) + { + ScaReleaseCache( pCurrentBlk, FALSE); + } + + if( pChildBlk) + { + ScaReleaseCache( pChildBlk, FALSE); + } + + return( rc); +} diff --git a/version5/src/f_btree.h b/version5/src/f_btree.h new file mode 100644 index 0000000..9bc8434 --- /dev/null +++ b/version5/src/f_btree.h @@ -0,0 +1,1062 @@ +//------------------------------------------------------------------------------ +// Desc: Header file for the B-Tree class definitions +// +// 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.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef F_BTREE_H +#define F_BTREE_H + +// 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])); +} + +typedef struct +{ + F_BTREE_BLK_HDR * pBlkHdr; + F_CachedBlock * pSCache; + const FLMBYTE * pucKeyBuf; + FLMUINT uiKeyBufSize; + FLMUINT uiKeyLen; + FLMUINT uiCurOffset; + FLMUINT uiLevel; + FLMUINT16 * pui16OffsetArray; + FLMUINT32 ui32BlkAddr; +} F_BTSK, * F_BTSK_p; + +typedef enum +{ + ELM_INSERT_DO, + ELM_INSERT, + ELM_REPLACE_DO, + ELM_REPLACE, + ELM_REMOVE, + ELM_BLK_MERGE, + ELM_DONE +} F_ELM_UPD_ACTION; + + +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)); +} + +FINLINE FLMBYTE * BtLastEntry( + FLMBYTE * pBlk + ) +{ + return BtEntry( pBlk, ((F_BTREE_BLK_HDR *)pBlk)->ui16NumKeys - 1); +} + +FINLINE FLMUINT getBlkType( + FLMBYTE * pBlk) +{ + return (FLMUINT)((F_BLK_HDR *)pBlk)->ui8BlkType; +} + +FINLINE FLMUINT16 * BtOffsetArray( + FLMBYTE * pBlk, + FLMUINT uiIndex) +{ + return (FLMUINT16 *) + (pBlk + sizeofBTreeBlkHdr( (F_BTREE_BLK_HDR *)pBlk) + (uiIndex * sizeof( FLMUINT16))); +} + +// Returns the address of the first entry in the block. i.e. the first non-blank +// address after the offset array. + +FINLINE FLMBYTE * getBlockEnd( + F_BTREE_BLK_HDR * pBlkHdr) +{ + return ((FLMBYTE *)pBlkHdr + sizeofBTreeBlkHdr( pBlkHdr) + + (pBlkHdr->ui16NumKeys * 2) + + pBlkHdr->ui16HeapSize); +} + +// This inline function takes the parameter returned from getEntrySize which +// adds 2 for the offset. + +FINLINE FLMUINT actualEntrySize( + FLMUINT uiEntrySize) +{ + return uiEntrySize - 2; +} + +// Error information returned by btCheck() + +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[ MAX_KEY_SIZ]; + FLMBYTE pucNewKey[ MAX_KEY_SIZ]; +} BTREE_REPLACE_STRUCT; + +class F_BtPool; + +class F_Btree : public XF_RefCount, public XF_Base +{ +public: + + + F_Btree( void); + ~F_Btree( void); + + RCODE btCreate( + F_Db * pDb, + LFILE * pLFile, + FLMBOOL bCounts, + FLMBOOL bData); + + RCODE btOpen( + F_Db * pDb, + LFILE * pLFile, + 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 uiKeyBufSize, + 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( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT * puiPosition); + + RCODE btPositionTo( + FLMUINT uiPosition, + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen); + + RCODE btGetPosition( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiPosition); + + RCODE btCheck( + BTREE_ERR_STRUCT * pErrStruct); + + RCODE btRewind( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen); + + FINLINE void btGetTransInfo( + FLMUINT64 * pui64LowTransId, + FLMBOOL * pbMostCurrent) + { + *pui64LowTransId = m_ui64LowTransId; + *pbMostCurrent = m_bMostCurrent; + } + + 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_ui64CurrTransID = 0; + m_ui64LastBlkTransId = 0; + m_ui64PrimaryBlkTransId = 0; + m_uiBlkChangeCnt = 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: + + 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_p pFromStack, + F_BTSK_p pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness); + + RCODE blockCounts( + F_BTSK_p pStack, + FLMUINT uiFirstOffset, + FLMUINT uiLastOffset, + FLMUINT * puiKeyCount, + FLMUINT * puiElementCount); + + RCODE getStoredCounts( + F_BTSK_p pFromStack, + F_BTSK_p pUntilStack, + FLMUINT * puiBlockCount, + FLMUINT * puiKeyCount, + FLMBOOL * pbTotalsEstimated, + FLMUINT uiAvgBlkFullness); + + RCODE getCacheBlocks( + F_BTSK_p pStack1, + F_BTSK_p pStack2); + + FINLINE FLMUINT getAvgKeyCount( + F_BTSK_p pFromStack, + F_BTSK_p pUntilStack, + FLMUINT uiAvgBlkFullness); + + FINLINE void updateTransInfo( + FLMUINT64 ui64LowTransID, + FLMUINT64 ui64HighTransID + ) + { + if (m_ui64LowTransId > ui64LowTransID) + { + m_ui64LowTransId = ui64LowTransID; + } + + if (!m_bMostCurrent) + { + m_bMostCurrent = (ui64HighTransID == FLM_MAX_UINT64) + ? TRUE + : FALSE; + } + } + + 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_p 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_XFLM_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( + F_CachedBlock ** ppSCache); + + 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, + F_CachedBlock ** ppPrevSCache, + 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, + F_CachedBlock ** ppPrevSCache); + + RCODE moveToNext( + FLMUINT uiStart, + FLMUINT uiFinish, + F_CachedBlock ** ppNextSCache); + + RCODE updateParentCounts( + F_CachedBlock * pChildSCache, + F_CachedBlock ** ppParentSCache, + FLMUINT uiParentElm); + + FLMUINT countKeys( + FLMBYTE * pBlk); + + FLMUINT countRangeOfKeys( + F_BTSK_p pFromStack, + FLMUINT uiFromOffset, + FLMUINT uiUntilOffset); + + RCODE moveStackToPrev( + F_CachedBlock * pPrevSCache); + + RCODE moveStackToNext( + F_CachedBlock * pSCache, + 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( + F_CachedBlock ** ppSCache); + + FINLINE RCODE getPrevBlock( + F_CachedBlock ** ppSCache); + + 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( + F_CachedBlock ** ppFromSCache, + F_CachedBlock ** ppToSCache); + + RCODE checkDownLinks( void); + + RCODE verifyChildLinks( + F_CachedBlock * pParentSCache); + + RCODE combineEntries( + F_BTREE_BLK_HDR * pSrcBlkHdr, + FLMUINT uiSrcOffset, + F_BTREE_BLK_HDR * pDstBlkHdr, + FLMUINT uiDstOffset, + FLMBOOL * pbEntriesCombined, + FLMUINT * puiEntrySize); + + RCODE moveBtreeBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr); + + RCODE moveDOBlock( + FLMUINT32 ui32FromBlkAddr, + FLMUINT32 ui32ToBlkAddr); + +// Member variables + FLMBOOL m_bCounts; // BT_NON_LEAF_COUNTS + FLMBOOL m_bData; // BT_LEAF_DATA + FLMBOOL m_bSetupForRead; + FLMBOOL m_bSetupForWrite; + FLMBOOL m_bSetupForReplace; + FLMBOOL m_bOpened; + FLMBOOL m_bMostCurrent; + FLMBOOL m_bDataOnlyBlock; + FLMBOOL m_bOrigInDOBlocks; + FLMBOOL m_bFirstRead; + FLMBOOL m_bStackSetup; + LFILE * m_pLFile; + F_Db * m_pDb; + FLMBOOL m_bTempDb; + F_BTSK_p m_pStack; // Used for traversing the B-Tree + FLMBYTE * m_pucTempBlk; + FLMBYTE * m_pucTempDefragBlk; + BTREE_REPLACE_STRUCT * m_pReplaceInfo; + BTREE_REPLACE_STRUCT * m_pReplaceStruct; + const FLMBYTE * m_pucDataPtr; + F_CachedBlock * m_pSCache; + F_BTREE_BLK_HDR * m_pBlkHdr; + FLMBYTE * m_pucBuffer; // Buffer used during moves + FLMUINT m_uiBufferSize; // Size of the buffer + FLMUINT m_uiBlockSize; + FLMUINT m_uiDefragThreshold; + FLMUINT m_uiOverflowThreshold; + FLMUINT m_uiStackLevels; + FLMUINT m_uiRootLevel; + FLMUINT m_uiReplaceLevels; + FLMUINT m_uiBlkChangeCnt; + FLMUINT m_uiDataLength; + FLMUINT m_uiPrimaryDataLen; + FLMUINT m_uiOADataLength; + FLMUINT m_uiDataRemaining; + FLMUINT m_uiOADataRemaining; + FLMUINT m_uiPrimaryOffset; // Offset into primary block + FLMUINT m_uiCurOffset; // Offset into current block + FLMUINT m_uiSearchLevel; + FLMUINT m_uiOffsetAtStart; // Offset into the current + // element at the beginning of + // the entry. An element may + // span multiple entries. + FLMUINT32 m_ui32PrimaryBlkAddr;// Primary block address + FLMUINT32 m_ui32DOBlkAddr; // Address of first DO Block + FLMUINT32 m_ui32CurBlkAddr; // Current block being read + FLMUINT64 m_ui64LowTransId; + FLMUINT64 m_ui64LastBlkTransId; + FLMUINT64 m_ui64PrimaryBlkTransId; + FLMUINT64 m_ui64CurrTransID; + F_BTSK m_Stack[ BH_MAX_LEVELS]; + // The m_pNext field is only used by the btPool object. + F_Btree * m_pNext; + IF_ResultSetCompare * m_pCompare; + +friend class F_BtPool; +friend class F_Db; +friend class F_Rfl; +}; + +RCODE btFreeBlockChain( + F_Db * pDb, + LFILE * pLFile, + FLMUINT uiStartAddr, + FLMUINT uiBlocksToFree, + FLMUINT * puiBlocksFreed, + FLMUINT * puiEndAddr, + IF_DeleteStatus * ifpDeleteStatus); + +#endif diff --git a/version5/src/f_nici.cpp b/version5/src/f_nici.cpp new file mode 100644 index 0000000..0e7693e --- /dev/null +++ b/version5/src/f_nici.cpp @@ -0,0 +1,2991 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the functions needed for the NICI interface +// functions. Adapted from ss_crypto.c written by Cameron Mashayekhi. +// +// 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: f_nici.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#ifdef FLM_USE_NICI +FSTATIC void GetIV( + FLMBYTE * pucIV, + FLMUINT uiLen); +#endif + +/*----------------------------------------------------------------------------- + * Desc: DTOR - Destroy an F_CCS object. + *---------------------------------------------------------------------------*/ +F_CCS::~F_CCS() +{ +#ifdef FLM_USE_NICI + if( m_keyHandle) + { + if( !m_hContext) + { + if( RC_BAD( CCS_CreateContext(0, &m_hContext))) + { + flmAssert( 0); + } + } + + // Get rid of the key handle. + + if ( m_hContext) + { + CCS_DestroyObject( m_hContext, + m_keyHandle); + + CCS_DestroyContext( m_hContext); + } + } + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } +#endif +} + +/*----------------------------------------------------------------------------- + * Desc: wrapNiciKey - Save the wrapped key in m_pKey. NOTE: Make sure + * there is a buffer allocated for the wrapped key (m_pucWrappedKey). + *---------------------------------------------------------------------------*/ +RCODE F_CCS::wrapKey( + FLMBYTE ** ppucWrappedKey, + FLMUINT32 * pui32Length, + NICI_OBJECT_HANDLE masterWrappingKey) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucWrappedKey); + F_UNREFERENCED_PARM( pui32Length); + F_UNREFERENCED_PARM( masterWrappingKey); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE wKey[2]; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes128[] = {IDV_NOV_AES128CBCPad}; + FLMBYTE oid_aes192[] = {IDV_NOV_AES192CBCPad}; + FLMBYTE oid_aes256[] = {IDV_NOV_AES256CBCPad}; + FLMBYTE oid_3des[] = {IDV_DES_EDE3_CBCPadIV8}; + NICI_OBJECT_HANDLE wrappingKeyHandle; + FLMBOOL bLocked = FALSE; + + if (masterWrappingKey) + { + wrappingKeyHandle = masterWrappingKey; + } + else + { + if (RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) + { + goto Exit; + } + } + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + /* Create NICI Context */ + if ( !m_hContext) + { + + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + f_memset( &wKey, 0, sizeof(NICI_ATTRIBUTE) * 2); + + wKey[0].type = NICI_A_KEY_TYPE; + wKey[1].type = NICI_A_KEY_SIZE; + + if (RC_BAD( rc = CCS_GetAttributeValue( + m_hContext, + wrappingKeyHandle, + &wKey[0], + 2))) + { + rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (!wKey[0].u.f.hasValue || !wKey[1].u.f.hasValue) + { + rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); + goto Exit; + } + + switch (wKey[0].u.f.value) + { + case NICI_K_AES: + { + switch (wKey[1].u.f.value) + { + case XFLM_NICI_AES128: + { + algorithm.algorithm = (nuint8 *)oid_aes128; + break; + } + case XFLM_NICI_AES192: + { + algorithm.algorithm = (nuint8 *)oid_aes192; + break; + } + case XFLM_NICI_AES256: + { + algorithm.algorithm = (nuint8 *)oid_aes256; + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + } + algorithm.parameter = parm; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + break; + } + + case NICI_K_DES3X: + { + algorithm.algorithm = (nuint8 *)oid_3des; + algorithm.parameter = parm; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + break; + } + + default: + { + rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); + goto Exit; + } + } + + // We should be able to call this with NULL for the wrapped key, to get the length. + + if (RC_BAD( rc = CCS_WrapKey( + m_hContext, + &algorithm, + NICI_KM_UNSPECIFIED, + 0, + wrappingKeyHandle, + m_keyHandle, + (nuint8 *)NULL, + (pnuint32)pui32Length))) + { + rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = f_calloc( *pui32Length, ppucWrappedKey))) + { + goto Exit; + } + + + if (RC_BAD( rc = CCS_WrapKey( + m_hContext, + &algorithm, + NICI_KM_UNSPECIFIED, + 0, + wrappingKeyHandle, + m_keyHandle, + (nuint8 *)*ppucWrappedKey, + (pnuint32)pui32Length))) + { + rc = RC_SET( NE_XFLM_NICI_WRAPKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } + +#endif + + return(rc); +} + + +/*----------------------------------------------------------------------------- + * Desc: - unwrapKey + *---------------------------------------------------------------------------*/ +RCODE F_CCS::unwrapKey( + FLMBYTE * pucWrappedKey, + FLMUINT32 ui32WrappedKeyLength, + NICI_OBJECT_HANDLE masterWrappingKey) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucWrappedKey); + F_UNREFERENCED_PARM( ui32WrappedKeyLength); + F_UNREFERENCED_PARM( masterWrappingKey); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE wKey; + NICI_OBJECT_HANDLE wrappingKeyHandle; + FLMBOOL bLocked = FALSE; + + if (masterWrappingKey) + { + wrappingKeyHandle = masterWrappingKey; + } + else + { + if (RC_BAD( rc = getWrappingKey( &wrappingKeyHandle))) + { + goto Exit; + } + } + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + if (RC_BAD( rc = CCS_UnwrapKey( + m_hContext, + wrappingKeyHandle, + (nuint8 *)pucWrappedKey, + ui32WrappedKeyLength, + &m_keyHandle))) + { + rc = RC_SET( NE_XFLM_NICI_UNWRAPKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // We need to get the key size... + + f_memset( &wKey, 0, sizeof(NICI_ATTRIBUTE)); + + wKey.type = NICI_A_KEY_SIZE; + + if (RC_BAD( rc = CCS_GetAttributeValue( + m_hContext, + m_keyHandle, + &wKey, + 1))) + { + rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (!wKey.u.f.hasValue) + { + rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); + goto Exit; + } + + m_uiEncKeySize = wKey.u.f.value; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } + +#endif + + return(rc); +} + +/*----------------------------------------------------------------------------- + * Desc: generateEncryptionKey + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateEncryptionKey( + FLMUINT uiEncKeySize + ) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch( m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = generateEncryptionKeyAES( uiEncKeySize); + break; + } + case FLM_NICI_DES3: + { + rc = generateEncryptionKeyDES3( uiEncKeySize); + break; + } + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return rc; + +} + +/*----------------------------------------------------------------------------- + * Desc: generateEncryptionKey + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateEncryptionKeyAES( + FLMUINT uiEncKeySize + ) +{ + RCODE rc = NE_XFLM_OK; + + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[3]; + nbool8 keySizeChanged; + FLMBYTE oid_aes128[] = {IDV_AES128CBC}; + FLMBYTE oid_aes192[] = {IDV_AES192CBC}; + FLMBYTE oid_aes256[] = {IDV_AES256CBC}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + /* Set up AES Algorithm*/ + switch (uiEncKeySize) + { + case XFLM_NICI_AES128: + { + algorithm.algorithm = (nuint8 *)oid_aes128; + break; + } + case XFLM_NICI_AES192: + { + algorithm.algorithm = (nuint8 *)oid_aes192; + break; + } + case XFLM_NICI_AES256: + { + algorithm.algorithm = (nuint8 *)oid_aes256; + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + } + algorithm.parameterLen = 0; + algorithm.parameter = NULL; + + /* Set up key attributes */ + keyAttr[0].type = NICI_A_KEY_USAGE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_SIZE; + keyAttr[1].u.f.hasValue = 1; + keyAttr[1].u.f.value = uiEncKeySize; + keyAttr[1].u.f.valueInfo = 0; + + keyAttr[2].type = NICI_A_GLOBAL; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = N_TRUE; + keyAttr[2].u.f.valueInfo = 0; + + /*Generate a AES key */ + + if (RC_BAD( rc = CCS_GenerateKey( + m_hContext, + &algorithm, + keyAttr, + 3, + &keySizeChanged, + &m_keyHandle, + NICI_H_INVALID))) + { + rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate some IV to use with this key. + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)m_ucIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + m_uiEncKeySize = uiEncKeySize; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + +/*----------------------------------------------------------------------------- + * Desc: generateEncryptionKey - DES3 + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateEncryptionKeyDES3( + FLMUINT uiEncKeySize + ) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[3]; + nbool8 keySizeChanged; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + f_mutexLock( m_hMutex); + + // Only one DES3 key size supported. + + if (uiEncKeySize != XFLM_NICI_DES3X) + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + + /* Create NICI Context */ + if ( !m_hContext) + { + + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + /* Set up AES Algorithm*/ + algorithm.algorithm = (nuint8 *)oid_des3; + algorithm.parameterLen = 0; + algorithm.parameter = NULL; + + /* Set up key attributes */ + keyAttr[0].type = NICI_A_KEY_USAGE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT | NICI_F_EXTRACT; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_SIZE; + keyAttr[1].u.f.hasValue = 1; + keyAttr[1].u.f.value = uiEncKeySize; + keyAttr[1].u.f.valueInfo = 0; + + keyAttr[2].type = NICI_A_GLOBAL; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = N_TRUE; + keyAttr[2].u.f.valueInfo = 0; + + /*Generate a AES key */ + + if (RC_BAD( rc = CCS_GenerateKey( + m_hContext, + &algorithm, + keyAttr, + 3, + &keySizeChanged, + &m_keyHandle, + NICI_H_INVALID))) + { + rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate some IV to use with this key. + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)m_ucIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + m_uiEncKeySize = uiEncKeySize; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + +/*----------------------------------------------------------------------------- + * Desc: generateWrappingKey + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateWrappingKey( + FLMUINT uiEncKeySize + ) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch( m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = generateWrappingKeyAES( uiEncKeySize); + break; + } + case FLM_NICI_DES3: + { + rc = generateWrappingKeyDES3( uiEncKeySize); + break; + } + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return rc; + +} + +/*----------------------------------------------------------------------------- + * Desc: generateWrappingKeyAES - generates an AES wrapping key + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateWrappingKeyAES( + FLMUINT uiEncKeySize) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[6]; + nbool8 keySizeChanged; + FLMBYTE oid_aes128[] = {IDV_AES128CBC}; + FLMBYTE oid_aes192[] = {IDV_AES192CBC}; + FLMBYTE oid_aes256[] = {IDV_AES256CBC}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + /* Set up AES Algorithm*/ + switch (uiEncKeySize) + { + case XFLM_NICI_AES128: + { + algorithm.algorithm = (nuint8 *)oid_aes128; + keyAttr[1].u.v.valuePtr = oid_aes128; + keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes128); + break; + } + case XFLM_NICI_AES192: + { + algorithm.algorithm = (nuint8 *)oid_aes192; + keyAttr[1].u.v.valuePtr = oid_aes192; + keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes192); + break; + } + case XFLM_NICI_AES256: + { + algorithm.algorithm = (nuint8 *)oid_aes256; + keyAttr[1].u.v.valuePtr = oid_aes256; + keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_aes256); + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + } + algorithm.parameterLen = 0; + algorithm.parameter = NULL; + + /* Set up key attributes */ + keyAttr[0].type = NICI_A_KEY_TYPE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_K_AES; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_FORMAT; + keyAttr[1].u.v.valueInfo = 0; + + keyAttr[2].type = NICI_A_KEY_USAGE; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + keyAttr[2].u.f.valueInfo = 0; + + keyAttr[3].type = NICI_A_KEY_SIZE; + keyAttr[3].u.f.hasValue = 1; + keyAttr[3].u.f.value = uiEncKeySize; + keyAttr[3].u.f.valueInfo = 0; + + keyAttr[4].type = NICI_A_GLOBAL; + keyAttr[4].u.f.hasValue = 1; + keyAttr[4].u.f.value = N_TRUE; + keyAttr[4].u.f.valueInfo = 0; + + keyAttr[5].type = NICI_A_CLASS; + keyAttr[5].u.f.hasValue = 1; + keyAttr[5].u.f.value = NICI_O_SECRET_KEY; + keyAttr[5].u.f.valueInfo = 0; + + /*Generate an AES wrapping key */ + + if (RC_BAD( rc = CCS_GenerateKey( + m_hContext, + &algorithm, + keyAttr, + 6, + &keySizeChanged, + &m_keyHandle, + NICI_H_INVALID))) + { + rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate some IV to use with this key. + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)m_ucIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // If we generated a wrapping key, then this object's key handle is actually a + // wrapping key. This means that we will use it to wrap the other keys in the + // system. + + m_bKeyIsWrappingKey = TRUE; + m_uiEncKeySize = uiEncKeySize; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + + +/*----------------------------------------------------------------------------- + * Desc: generateWrappingKeyDES3 - generates a triple DES (DES3) wrapping key + *---------------------------------------------------------------------------*/ +RCODE F_CCS::generateWrappingKeyDES3( + FLMUINT uiEncKeySize) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiEncKeySize); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[6]; + nbool8 keySizeChanged; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + f_mutexLock( m_hMutex); + + if (uiEncKeySize != XFLM_NICI_DES3X) + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + + /* Create NICI Context */ + if ( !m_hContext) + { + + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + /* Set up AES Algorithm*/ + algorithm.algorithm = (nuint8 *)oid_des3; + algorithm.parameterLen = 0; + algorithm.parameter = NULL; + + /* Set up key attributes */ + + keyAttr[0].type = NICI_A_KEY_TYPE; + keyAttr[0].u.f.hasValue = 1; + keyAttr[0].u.f.value = NICI_K_DES3X; + keyAttr[0].u.f.valueInfo = 0; + + keyAttr[1].type = NICI_A_KEY_FORMAT; + keyAttr[1].u.v.valuePtr = oid_des3; + keyAttr[1].u.v.valueLen = (nuint32)sizeof( oid_des3); + keyAttr[1].u.v.valueInfo = 0; + + keyAttr[2].type = NICI_A_KEY_USAGE; + keyAttr[2].u.f.hasValue = 1; + keyAttr[2].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + keyAttr[2].u.f.valueInfo = 0; + + keyAttr[3].type = NICI_A_KEY_SIZE; + keyAttr[3].u.f.hasValue = 1; + keyAttr[3].u.f.value = uiEncKeySize; + keyAttr[3].u.f.valueInfo = 0; + + keyAttr[4].type = NICI_A_GLOBAL; + keyAttr[4].u.f.hasValue = 1; + keyAttr[4].u.f.value = N_TRUE; + keyAttr[4].u.f.valueInfo = 0; + + keyAttr[5].type = NICI_A_CLASS; + keyAttr[5].u.f.hasValue = 1; + keyAttr[5].u.f.value = NICI_O_SECRET_KEY; + keyAttr[5].u.f.valueInfo = 0; + + /*Generate an AES wrapping key */ + + if (RC_BAD( rc = CCS_GenerateKey( + m_hContext, + &algorithm, + keyAttr, + 6, + &keySizeChanged, + &m_keyHandle, + NICI_H_INVALID))) + { + rc = RC_SET( NE_XFLM_NICI_GENKEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate some IV to use with this key. + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)m_ucIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // If we generated a wrapping key, then this object's key handle is actually a + // wrapping key. This means that we will use it to wrap the other keys in the + // system. + m_bKeyIsWrappingKey = TRUE; + m_uiEncKeySize = uiEncKeySize; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + +/*----------------------------------------------------------------------------- + * Desc: encryptToStore (public) + *---------------------------------------------------------------------------*/ +RCODE F_CCS::encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch (m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = encryptToStoreAES( + pucIn, + uiInLen, + pucOut, + puiOutLen, + pucIV); + break; + } + case FLM_NICI_DES3: + { + rc = encryptToStoreDES3( + pucIn, + uiInLen, + pucOut, + puiOutLen, + pucIV); + break; + } + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return rc; + +} + +/*----------------------------------------------------------------------------- + * Desc: decryptFromStore (public) + *---------------------------------------------------------------------------*/ +RCODE F_CCS::decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + + switch( m_uiAlgType) + { + case FLM_NICI_AES: + { + rc = decryptFromStoreAES( + pucIn, + uiInLen, + pucOut, + puiOutLen, + pucIV); + break; + } + case FLM_NICI_DES3: + { + rc = decryptFromStoreDES3( + pucIn, + uiInLen, + pucOut, + puiOutLen, + pucIV); + break; + } + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + +#endif + +Exit: + + return rc; + +} + +/*----------------------------------------------------------------------------- + * Desc: encryptToStore - Using AES + *---------------------------------------------------------------------------*/ +RCODE F_CCS::encryptToStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes128[] = {IDV_AES128CBC}; + FLMBYTE oid_aes192[] = {IDV_AES192CBC}; + FLMBYTE oid_aes256[] = {IDV_AES256CBC}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context*/ + if ( !m_hContext) + { + + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + switch (m_uiEncKeySize) + { + case XFLM_NICI_AES128: + { + algorithm.algorithm = (nuint8 *)oid_aes128; + break; + } + case XFLM_NICI_AES192: + { + algorithm.algorithm = (nuint8 *)oid_aes192; + break; + } + case XFLM_NICI_AES256: + { + algorithm.algorithm = (nuint8 *)oid_aes256; + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + } + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + + if (pucIV) + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + } + else + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + } + + algorithm.parameter->parms[0].u.b.len = IV_SZ; + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataEncryptInit( m_hContext, &algorithm, m_keyHandle))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_ENC_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Encrypt( + m_hContext, + (nuint8 *)pucIn, + uiInLen, + (nuint8 *)pucOut, + puiOutLen))) + { + rc = RC_SET( NE_XFLM_NICI_ENCRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + + +/*----------------------------------------------------------------------------- + * Desc: decryptFromStore - using the AES algorithm + *---------------------------------------------------------------------------*/ +RCODE F_CCS::decryptFromStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes128[] = {IDV_AES128CBC}; + FLMBYTE oid_aes192[] = {IDV_AES192CBC}; + FLMBYTE oid_aes256[] = {IDV_AES256CBC}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + switch (m_uiEncKeySize) + { + case XFLM_NICI_AES128: + { + algorithm.algorithm = (nuint8 *)oid_aes128; + break; + } + case XFLM_NICI_AES192: + { + algorithm.algorithm = (nuint8 *)oid_aes192; + break; + } + case XFLM_NICI_AES256: + { + algorithm.algorithm = (nuint8 *)oid_aes256; + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + } + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + + if (pucIV) + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + } + else + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + } + + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataDecryptInit( + m_hContext, + &algorithm, + m_keyHandle))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Decrypt( + m_hContext, + (nuint8 *)pucIn, + uiInLen, + (nuint8 *)pucOut, + puiOutLen))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + + +/*----------------------------------------------------------------------------- + * Desc: encryptToStore - Using DES3 + *---------------------------------------------------------------------------*/ +RCODE F_CCS::encryptToStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context*/ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + algorithm.algorithm = (nuint8 *)oid_des3; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + + if (pucIV) + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + } + else + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + } + + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataEncryptInit(m_hContext, &algorithm, m_keyHandle))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_ENC_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Encrypt( + m_hContext, + (nuint8 *)pucIn, + uiInLen, + (nuint8 *)pucOut, + puiOutLen))) + { + rc = RC_SET( NE_XFLM_NICI_ENCRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + + +/*----------------------------------------------------------------------------- + * Desc: decryptFromStore - using the Triple DES (DES3) algorithm + *---------------------------------------------------------------------------*/ +RCODE F_CCS::decryptFromStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucIn); + F_UNREFERENCED_PARM( uiInLen); + F_UNREFERENCED_PARM( pucOut); + F_UNREFERENCED_PARM( puiOutLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_des3[] = {IDV_DES_EDE3_CBC_IV8}; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + /*Set up alogrithm now to do triple des decryption */ + algorithm.algorithm = (nuint8 *)oid_des3; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + + if (pucIV) + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + } + else + { + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)m_ucIV; + } + + algorithm.parameter->parms[0].u.b.len = IV_SZ8; /* 8-byte IV */ + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataDecryptInit( + m_hContext, + &algorithm, + m_keyHandle))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Decrypt( + m_hContext, + (nuint8 *)pucIn, + uiInLen, + (nuint8 *)pucOut, + puiOutLen))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); + +} + +/*----------------------------------------------------------------------------- + * Desc: init - Initialize the context. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::init( + FLMBOOL bKeyIsWrappingKey, + FLMUINT uiAlgType) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( bKeyIsWrappingKey); + F_UNREFERENCED_PARM( uiAlgType); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMBOOL bLocked = FALSE; + + if (m_bInitCalled) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + m_bKeyIsWrappingKey = bKeyIsWrappingKey; + + if (uiAlgType != FLM_NICI_AES && + uiAlgType != FLM_NICI_DES3) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); + goto Exit; + } + + m_uiAlgType = uiAlgType; + + // Create a mutex to control access to the nici operations. + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + // Create NICI Context + + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + else + { + flmAssert( 0); // Should not have a context yet! + } + + // Generate the Random IV + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)&m_ucRndIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate an adjustment factor for the IV + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)&m_uiIVFactor, + sizeof(FLMUINT)))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + + m_bInitCalled = TRUE; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } +#endif + + return rc; + +} + +/*----------------------------------------------------------------------------- + * Desc: selectWrappingKey - pick a wrapping key that we can use to wrap & + * unwrap the encryption key with. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::getWrappingKey( + NICI_OBJECT_HANDLE * pWrappingKeyHandle) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pWrappingKeyHandle); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ATTRIBUTE find[2]; + FLMUINT uiCount; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_AV_STORAGE; + find[1].u.f.valueInfo = 0; + + if (RC_BAD( rc = CCS_FindObjectsInit(m_hContext, find, 2))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_INIT); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + uiCount = 1; + + if (RC_BAD( rc = CCS_FindObjects( + m_hContext, + pWrappingKeyHandle, + &uiCount))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_OBJECT); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( NE_XFLM_NICI_WRAPKEY_NOT_FOUND); + goto Exit; + } + +#endif + +Exit: + +#ifdef FLM_USE_NICI + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); +} + + +/*----------------------------------------------------------------------------- + * Desc: getKeyToStore - Function used to obtain the key information in the + * format that will be stored on disk. A buffer will be allocated by this + * function that **MUST** be freed when no longer needed. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::getKeyToStore( + FLMBYTE ** ppucKeyInfo, + FLMUINT32 * pui32BufLen, + FLMBYTE * pszEncKeyPasswd, + F_CCS * pWrappingCcs) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucKeyInfo); + F_UNREFERENCED_PARM( pui32BufLen); + F_UNREFERENCED_PARM( pszEncKeyPasswd); + F_UNREFERENCED_PARM( pWrappingCcs); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMBYTE * pucTmp = NULL; + FLMBYTE * pucPtr = NULL; + FLMUINT32 ui32PaddedLength; + FLMBYTE * pucWrappedKey = NULL; + FLMUINT32 ui32WrappedKeyLen = 0; + FLMBYTE * pszFormattedEncKeyPasswd = NULL; + NICI_OBJECT_HANDLE wrappingKeyHandle = 0; + + *ppucKeyInfo = NULL; + + *pui32BufLen = 0; + + if (pWrappingCcs) + { + flmAssert(m_bKeyIsWrappingKey == FALSE); + wrappingKeyHandle = pWrappingCcs->m_keyHandle; + } + else if (!pszEncKeyPasswd) + { + flmAssert( m_bKeyIsWrappingKey); + } + + // Either extract the key or wrap the key. + if (pszEncKeyPasswd && pszEncKeyPasswd[0]) + { + // The password that is passed in to CCS_pbeEncrypt is NOT actually + // unicode. It must be treated as a sequence of bytes that that is + // terminated with 2 nulls and has an even length. If we treat it + // as unicode, then we'll have endian issues if we move the database + // to machines with different byte ordering. + if (RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + + (f_strlen(pszEncKeyPasswd) % 2) + 2, + &pszFormattedEncKeyPasswd))) + { + goto Exit; + } + f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); + + if (RC_BAD( rc = extractKey( &pucWrappedKey, + &ui32WrappedKeyLen, + (FLMUNICODE *)pszFormattedEncKeyPasswd))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = wrapKey( &pucWrappedKey, + &ui32WrappedKeyLen, + wrappingKeyHandle))) + { + goto Exit; + } + } + + // The shrouded or wrapped key will be stored in m_pKey. + ui32PaddedLength = (ui32WrappedKeyLen + + sizeof( FLMBOOL) + + sizeof (FLMUINT32) + + IV_SZ ); + + // Make sure our buffer size is padded to a 16 byte boundary. + if ((ui32PaddedLength % 16) != 0) + { + ui32PaddedLength += (16 - (ui32PaddedLength % 16)); + } + + // Add one extra byte for a NULL terminator + if (RC_BAD(rc = f_alloc( ui32PaddedLength + 1, &pucTmp))) + { + goto Exit; + } + + if ( !m_hContext) + { + if (CCS_CreateContext( 0, &m_hContext)) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + pucPtr = pucTmp; + + // Save a flag indicating whether the key is wrapped or encoded in + // a password. + UD2FBA( (pszEncKeyPasswd && pszEncKeyPasswd[0]) ? (FLMUINT)TRUE : (FLMUINT)FALSE, pucPtr); + pucPtr += sizeof(FLMBOOL); + + // Copy the key length. + UD2FBA(ui32WrappedKeyLen, pucPtr); + pucPtr += sizeof(FLMUINT32); + + // Copy the IV too. + f_memcpy( pucPtr, m_ucIV, IV_SZ); + pucPtr += IV_SZ; + + // Copy the wrapped key value + f_memcpy( pucPtr, pucWrappedKey, ui32WrappedKeyLen); + pucPtr += ui32WrappedKeyLen; + + // Fill the remainder of the buffer with random data. + if (CCS_GetRandom(m_hContext, + (nuint8 *)pucPtr, + ((FLMUINT)pucTmp + ui32PaddedLength) - (FLMUINT)pucPtr)) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + pucTmp[ ui32PaddedLength] = '\0'; + *ppucKeyInfo = pucTmp; + *pui32BufLen = ui32PaddedLength; + pucTmp = NULL; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (pucTmp) + { + f_free(&pucTmp); + } + + if (pucWrappedKey) + { + f_free( &pucWrappedKey); + } + + if (pszFormattedEncKeyPasswd) + { + f_free( &pszFormattedEncKeyPasswd); + } +#endif + + return rc; +} + +/*----------------------------------------------------------------------------- + * Desc: setKeyFromStore - Function used to set the key info using the binary + * key stored on the disk. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::setKeyFromStore( + FLMBYTE * pucKeyInfo, + FLMBYTE * pszEncKeyPasswd, + F_CCS * pWrappingCcs) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucKeyInfo); + F_UNREFERENCED_PARM( pszEncKeyPasswd); + F_UNREFERENCED_PARM( pWrappingCcs); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMBYTE * pucTmp = pucKeyInfo; + FLMBYTE * pucBuffer = NULL; + FLMBOOL bShrouded = FALSE; + FLMUINT32 ui32Length; + FLMBYTE * pucKeyBuf = NULL; + FLMBYTE * pszFormattedEncKeyPasswd = NULL; + NICI_OBJECT_HANDLE wrappingKeyHandle = 0; + + if (pWrappingCcs) + { + flmAssert(m_bKeyIsWrappingKey == FALSE); + wrappingKeyHandle = pWrappingCcs->m_keyHandle; + } + + // Extract the fields from the buffer + // Is the key shrouded? + bShrouded = FB2UD( pucTmp); + pucTmp += sizeof(FLMUINT); + + // Actual length - note that the passed buffer is padded to 16 byte boundary. + ui32Length = FB2UD( pucTmp); + pucTmp += sizeof(FLMUINT32); + + // Get the IV + f_memcpy( m_ucIV, pucTmp, IV_SZ); + pucTmp += IV_SZ; + + // Need another temporary buffer to hold the encrypted / shrouded key. + if (RC_BAD( rc = f_alloc( ui32Length, &pucBuffer))) + { + goto Exit; + } + + f_memcpy( pucBuffer, pucTmp, ui32Length); + + if (bShrouded) + { + if (pszEncKeyPasswd == NULL || pszEncKeyPasswd[0] == '\0') + { + rc = RC_SET( NE_XFLM_EXPECTING_PASSWORD); + goto Exit; + } + + // The password that is passed in to CCS_pbeDecrypt is NOT actually + // unicode. It must be treated as a sequence of bytes that that is + // terminated with 2 nulls and has an even length. If we treat it + // as unicode, then we'll have endian issues if we move the database + // to machines with different byte ordering. + if (RC_BAD( rc = f_calloc( f_strlen(pszEncKeyPasswd) + + (f_strlen(pszEncKeyPasswd) % 2) + 2, + &pszFormattedEncKeyPasswd))) + { + goto Exit; + } + f_strcpy( pszFormattedEncKeyPasswd, pszEncKeyPasswd); + + // Unshroud the key using the password. + // Key handle is always kept in m_keyHandle. + if (RC_BAD( rc = injectKey( pucBuffer, + ui32Length, + (FLMUNICODE *)pszFormattedEncKeyPasswd))) + { + goto Exit; + } + } + else + { + if (pszEncKeyPasswd) + { + if ( pszEncKeyPasswd[0] != '\0') + { + rc = RC_SET( NE_XFLM_NOT_EXPECTING_PASSWORD); + goto Exit; + } + } + + // Unwrap the key. The Key handle is always store in m_keyHandle. + if (RC_BAD( rc = unwrapKey( pucBuffer, + ui32Length, + wrappingKeyHandle))) + { + goto Exit; + } + } + + m_bKeyVerified = TRUE; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (pucBuffer) + { + f_free( &pucBuffer); + } + + if (pucKeyBuf) + { + f_free( &pucKeyBuf); + } + + if (pszFormattedEncKeyPasswd) + { + f_free( &pszFormattedEncKeyPasswd); + } + +#endif + + return rc; + +} + +typedef struct +{ + FLMUINT uiKeyType; + FLMUINT uiFormatLen; + FLMUINT uiKeyLen; + FLMUINT uiKeySize; +} EXTRACTED_KEY; + +/*----------------------------------------------------------------------------- + * Desc: extractKey - Extract the key by encrypting it in a supplied password. The + * buffer ppucExtractedKey buffer is allocated and returned, thus *MUST* be released + * after it is no longer needed. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::extractKey( + FLMBYTE ** ppucExtractedKey, + FLMUINT32 * pui32Length, + FLMUNICODE * puzEncKeyPasswd) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( ppucExtractedKey); + F_UNREFERENCED_PARM( pui32Length); + F_UNREFERENCED_PARM( puzEncKeyPasswd); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[2]; + NICI_ATTRIBUTE attr[2]; + FLMBYTE oid_sha1[] = {IDV_SHA1}; + FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; + FLMBYTE ucDigest[ 20]; + FLMUINT uiDigestLen = sizeof(ucDigest); + FLMUINT uiBufferSize; + FLMBYTE * pucKey = NULL; + FLMBYTE * pucFormat = NULL; + EXTRACTED_KEY * pExtractedKey = NULL; + FLMUINT uiEncLen; + FLMBYTE * pTemp = NULL; + NICI_PARAMETER_INFO * pParmInfo; + FLMBYTE * pucSalt; + FLMUINT uiAllocSize; + FLMUINT uiIndx; + FLMBYTE * pucTempPtr; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + f_memset( &attr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); + attr[0].type = NICI_A_KEY_TYPE; + attr[1].type = NICI_A_KEY_FORMAT; + + if (RC_BAD( rc = CCS_GetAttributeValue( + m_hContext, + m_keyHandle, + &attr[0], + 2))) + { + rc = RC_SET( NE_XFLM_NICI_ATTRIBUTE_VALUE); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (!attr[0].u.f.hasValue) + { + rc = RC_SET( NE_XFLM_NICI_BAD_ATTRIBUTE); + goto Exit; + } + + f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 2); + + switch (attr[0].u.f.value) + { + case NICI_K_AES: + { + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + switch (m_uiEncKeySize) + { + case XFLM_NICI_AES128: + { + keyAttr[uiIndx].u.v.valueLen = 16; + break; + } + case XFLM_NICI_AES192: + { + keyAttr[uiIndx].u.v.valueLen = 24; + break; + } + case XFLM_NICI_AES256: + { + keyAttr[uiIndx].u.v.valueLen = 32; + break; + } + } + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; + + break; + } + case NICI_K_DES3X: + { + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valueLen = 24; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; + break; + + } + case NICI_K_DES: + { + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valueLen = 8; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valueLen = attr[1].u.v.valueLen; + break; + + } + + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + + // Make one allocation that we can then use to hold several different things. + + uiBufferSize = sizeof( EXTRACTED_KEY) + // pExtractedKey + attr[1].u.v.valueLen + // pucFormat + keyAttr[0].u.v.valueLen + // pucKey + sizeof (ucDigest); // pucDigest + uiAllocSize = uiBufferSize + + SALT_SZ + // Salt (not encrypted) + (sizeof(NICI_PARAMETER_DATA) * 2) + sizeof(FLMUINT32); // Parameter data (not encrypted) + + // Make sure the allocation size is on a 8 byte boundary + + if( (uiAllocSize % 8) != 0) + { + uiAllocSize += (8 - (uiAllocSize % 8)); + } + + if (RC_BAD( rc = f_calloc( uiAllocSize, &pExtractedKey))) + { + goto Exit; + } + + keyAttr[1].u.v.valuePtr = &pExtractedKey[1]; + pucFormat = (FLMBYTE *)keyAttr[1].u.v.valuePtr; + keyAttr[0].u.v.valuePtr = pucFormat + attr[1].u.v.valueLen; + pucKey = (FLMBYTE *)keyAttr[0].u.v.valuePtr; + + pucSalt = (FLMBYTE *)pExtractedKey + uiBufferSize; + + pParmInfo = (NICI_PARAMETER_INFO *)(pucSalt + SALT_SZ); + + // Make sure that pParmInfo is 8 byte alligned. + + if ((FLMUINT)pParmInfo % 8) + { + FLMBYTE * pucTemp = (FLMBYTE *)pParmInfo + + (8 - ((FLMUINT)pParmInfo % 8)); + pParmInfo = (NICI_PARAMETER_INFO *)pucTemp; + } + + // Extracted the key value now + + if (RC_BAD( rc = CCS_ExtractKey( + m_hContext, + m_keyHandle, + &keyAttr[0], + 2))) + { + rc = RC_SET( NE_XFLM_EXTRACT_KEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Calculate a SHA1 checksum. + + algorithm.algorithm = (nuint8 *)oid_sha1; + algorithm.parameter = NULL; + algorithm.parameterLen = 0; + + if (RC_BAD( rc = CCS_DigestInit( + m_hContext, + &algorithm))) + { + rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Digest( + m_hContext, + (nuint8 *)pucFormat, + keyAttr[0].u.v.valueLen + attr[1].u.v.valueLen, + (nuint8 *)ucDigest, + &uiDigestLen))) + { + rc = RC_SET( NE_XFLM_DIGEST_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + flmAssert( uiDigestLen == sizeof( ucDigest)); + + pucTempPtr = (FLMBYTE *)pExtractedKey; + + UD2FBA( attr[0].u.f.value, pucTempPtr); //pExtractedKey->uiKeyType = attr[0].u.f.value; + pucTempPtr += 4; + + UD2FBA( attr[1].u.v.valueLen, pucTempPtr); //pExtractedKey->uiFormatLen = attr[1].u.v.valueLen; + pucTempPtr += 4; + + UD2FBA( keyAttr[0].u.v.valueLen, pucTempPtr); //pExtractedKey->uiKeyLen = keyAttr[0].u.v.valueLen; + pucTempPtr += 4; + + UD2FBA( m_uiEncKeySize, pucTempPtr); // pEncKey->uiKeySize = m_uiEncKeySize; + + // Point to the Digest... + + pTemp = (FLMBYTE *)&pExtractedKey[1] + + attr[1].u.v.valueLen + // Format length + keyAttr[0].u.v.valueLen; // Key length + f_memcpy( pTemp, ucDigest, uiDigestLen); + + // Generate some salt. + + if (RC_BAD( rc = CCS_GetRandom( m_hContext, + (nuint8 *)pucSalt, + SALT_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + pTemp = NULL; // don't want this to be freed + goto Exit; + } + + // This buffer needs to be a separate allocation because it is returned to the caller. We will + // be returning the value of the SALT with the encrypted key. The call to CCS_pbeEncrypt + // may return an extra 8 bytes. + + if (RC_BAD( rc = f_alloc( uiBufferSize + SALT_SZ + 8, &pTemp))) + { + goto Exit; + } + + // Now to encrypt the buffer. + + algorithm.algorithm = (nuint8 *)oid_pbe; + + pParmInfo->count = 2; // Two parameters + + pParmInfo->parms[0].parmType = NICI_P_SALT; + pParmInfo->parms[0].u.b.len = SALT_SZ; + pParmInfo->parms[0].u.b.ptr = (nuint8 *)pucSalt; + + pParmInfo->parms[1].parmType = NICI_P_COUNT; + pParmInfo->parms[1].u.value = SALT_COUNT; + + algorithm.parameter = pParmInfo; + algorithm.parameterLen = sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + uiEncLen = uiBufferSize + 8; + + if (RC_BAD( rc = CCS_pbeEncrypt( + m_hContext, + &algorithm, + puzEncKeyPasswd, + (nuint8 *)pExtractedKey, + uiBufferSize, + (nuint8 *)pTemp, + &uiEncLen))) + { + rc = RC_SET( NE_XFLM_PBE_ENCRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + *ppucExtractedKey = pTemp; + + // Now add the salt to the end of the buffer. + + pTemp += uiEncLen; + + f_memcpy( pTemp, pucSalt, SALT_SZ); + + pTemp = NULL; + + *pui32Length = uiEncLen + SALT_SZ; + +#endif + +Exit: +#ifdef FLM_USE_NICI + if (pTemp) + { + f_free( &pTemp); + } + + if (pucKey) + { + f_free( &pExtractedKey); + } + + f_mutexUnlock( m_hMutex); + +#endif + return(rc); +} + + +/*----------------------------------------------------------------------------- + * Desc: injectKey - Inject the encrypting key using the supplied password. + *---------------------------------------------------------------------------*/ +RCODE F_CCS::injectKey( + FLMBYTE * pszExtractedKey, + FLMUINT32 ui32Length, + FLMUNICODE * puzEncKeyPasswd) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pszExtractedKey); + F_UNREFERENCED_PARM( ui32Length); + F_UNREFERENCED_PARM( puzEncKeyPasswd); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_ALGORITHM algorithm; + NICI_ATTRIBUTE keyAttr[7]; + FLMBYTE oid_sha1[] = {IDV_SHA1}; + FLMBYTE oid_pbe[] = {IDV_pbeWithSHA1And3Key3xDES_CBC}; + FLMUINT uiIndx; + FLMBYTE ucDigest[ 20]; + FLMUINT uiDigestLen = sizeof(ucDigest); + FLMBYTE * pKey; + FLMBYTE * pucFormat; + EXTRACTED_KEY * pExtractedKey; + FLMUINT uiEncLen; + FLMBYTE * pTemp; + FLMBYTE * pucBuffer = NULL; + FLMBYTE * pucSalt; + FLMUINT uiAllocSize; + NICI_PARAMETER_INFO * pParmInfo = NULL; + FLMBYTE * pucTempPtr; + + f_mutexLock( m_hMutex); + + /* Create NICI Context */ + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + // Extract the SALT from the key buffer. + pucSalt = pszExtractedKey + (ui32Length - SALT_SZ); + ui32Length -= SALT_SZ; + + // Make one allocation and point into it for the different buffers we need. + + uiAllocSize = ui32Length + + sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + if (RC_BAD( rc = f_calloc( uiAllocSize, &pucBuffer))) + { + goto Exit; + } + + pParmInfo = (NICI_PARAMETER_INFO *)(pucBuffer + ui32Length); + + + // Now to decrypt the buffer. + + algorithm.algorithm = (nuint8 *)oid_pbe; + + pParmInfo->count = 2; // Two parameters + + pParmInfo->parms[0].parmType = NICI_P_SALT; + pParmInfo->parms[0].u.b.len = SALT_SZ; + pParmInfo->parms[0].u.b.ptr = (nuint8 *)pucSalt; + + pParmInfo->parms[1].parmType = NICI_P_COUNT; + pParmInfo->parms[1].u.value = SALT_COUNT; + + algorithm.parameter = pParmInfo; + algorithm.parameterLen = sizeof(NICI_PARAMETER_DATA) * 2 + sizeof(FLMUINT32); + + uiEncLen = ui32Length; + + if (RC_BAD( rc = CCS_pbeDecrypt( + m_hContext, + &algorithm, + puzEncKeyPasswd, + (nuint8 *)pszExtractedKey, + ui32Length, + (nuint8 *)pucBuffer, + &uiEncLen))) + { + rc = RC_SET( NE_XFLM_PBE_DECRYPT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // For cross platform compatibility, we need to first extract the KeyType, + // FormatLen and KeyLen values then we will set them back again. They are + // stored in a specific byte order, which may not match the native order for + // referencing integers on the local platform. + + pExtractedKey = (EXTRACTED_KEY *)pucBuffer; + pucTempPtr = pucBuffer; + + pExtractedKey->uiKeyType = FB2UD( pucTempPtr); + pucTempPtr += 4; + + pExtractedKey->uiFormatLen = FB2UD( pucTempPtr); + pucTempPtr += 4; + + pExtractedKey->uiKeyLen = FB2UD( pucTempPtr); + pucTempPtr += 4; + + m_uiEncKeySize = FB2UD( pucTempPtr); + + // Calculate a SHA1 checksum. + + algorithm.algorithm = (nuint8 *)oid_sha1; + algorithm.parameter = NULL; + algorithm.parameterLen = 0; + + if (RC_BAD( rc = CCS_DigestInit( + m_hContext, + &algorithm))) + { + rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + pTemp = (FLMBYTE *)&pExtractedKey[ 1]; + + if (RC_BAD( rc = CCS_Digest( + m_hContext, + (nuint8 *)pTemp, + pExtractedKey->uiFormatLen + + pExtractedKey->uiKeyLen, + (nuint8 *)ucDigest, + &uiDigestLen))) + { + rc = RC_SET( NE_XFLM_DIGEST_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + flmAssert( uiDigestLen == sizeof( ucDigest)); + + // Now compare the two digests. They must be equal! + pTemp += pExtractedKey->uiKeyLen + pExtractedKey->uiFormatLen; + + if (f_memcmp( pTemp, ucDigest, uiDigestLen)) + { + rc = RC_SET( NE_XFLM_INVALID_ENCKEY_CRC); + goto Exit; + } + + pucFormat = (FLMBYTE *)&pExtractedKey[1]; // Point to the format + pKey = pucFormat + pExtractedKey->uiFormatLen; // Point to the key. + + uiIndx = 0; + f_memset( &keyAttr[0], 0, sizeof(NICI_ATTRIBUTE) * 7); + + switch (pExtractedKey->uiKeyType) + { + case NICI_K_AES: + { + /* Set key attributes */ + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_TYPE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_K_AES; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valuePtr = pucFormat; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_USAGE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_SIZE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = m_uiEncKeySize; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valuePtr = pKey; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_CLASS; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_GLOBAL; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = N_TRUE; + keyAttr[uiIndx].u.f.valueInfo = 0; + break; + } + case NICI_K_DES3X: + { + /* Set key attributes */ + uiIndx = 0; + keyAttr[uiIndx].type = NICI_A_KEY_TYPE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_K_DES3X; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_FORMAT; + keyAttr[uiIndx].u.v.valuePtr = pucFormat; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiFormatLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_USAGE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_F_WRAP | NICI_F_UNWRAP | NICI_F_KM_ENCRYPT | NICI_F_KM_DECRYPT | NICI_F_EXTRACT | NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_SIZE; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = m_uiEncKeySize; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_KEY_VALUE; + keyAttr[uiIndx].u.v.valuePtr = pKey; + keyAttr[uiIndx].u.v.valueLen = pExtractedKey->uiKeyLen; + keyAttr[uiIndx].u.v.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_CLASS; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = NICI_O_SECRET_KEY; + keyAttr[uiIndx].u.f.valueInfo = 0; + + uiIndx++; + keyAttr[uiIndx].type = NICI_A_GLOBAL; + keyAttr[uiIndx].u.f.hasValue = 1; + keyAttr[uiIndx].u.f.value = N_TRUE; + keyAttr[uiIndx].u.f.valueInfo = 0; + break; + } + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_NICI_INVALID_ALGORITHM); + goto Exit; + } + } + + if (RC_BAD( rc = CCS_InjectKey( + m_hContext, + &keyAttr[0], + 7, + &m_keyHandle))) + { + rc = RC_SET( NE_XFLM_INJECT_KEY_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + +#endif + +Exit: +#ifdef FLM_USE_NICI + if (pucBuffer) + { + f_free( &pucBuffer); + } + + f_mutexUnlock( m_hMutex); + +#endif + + return(rc); +} + + +/**************************************************************************** +Desc: getIVLen returns the correct length of the IV for the type of + algorithm. +****************************************************************************/ +FLMUINT F_CCS::getIVLen() +{ + +#ifndef FLM_USE_NICI + return 0; +#else + switch (m_uiAlgType) + { + case FLM_NICI_AES: + return IV_SZ; + case FLM_NICI_DES3: + return IV_SZ8; + default: + return 0; + } +#endif +} + +/**************************************************************************** +Desc: generateIV will generate a random set of bytes to be used as IV. +****************************************************************************/ +RCODE F_CCS::generateIV( + FLMUINT uiIVLen, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( uiIVLen); + F_UNREFERENCED_PARM( pucIV); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + FLMUINT uiLoop; + NICI_ALGORITHM algorithm; + FLMBYTE oid_sha1[] = {IDV_SHA1}; + FLMBOOL bLocked = FALSE; + FLMBYTE * pucIVPtr = m_ucRndIV; + FLMBYTE ucIVBuffer[ IV_SZ * 2]; + FLMUINT uiIVBufferLen = sizeof(ucIVBuffer); + + if (!uiIVLen) + { + goto Exit; + } + + f_mutexLock( m_hMutex); + bLocked = TRUE; + + /* Create NICI Context */ + + if ( !m_hContext) + { + if (RC_BAD( rc = CCS_CreateContext(0, &m_hContext))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + m_hContext = 0; + goto Exit; + } + } + + // See if it is time to reinitialize the Random IV. + + if ((m_uiIVFactor & 0x07FF) == 0) + { + // Generate the Random IV + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)&m_ucRndIV, + IV_SZ))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Generate an adjustment factor for the IV + + if (RC_BAD( rc = CCS_GetRandom( + m_hContext, + (nuint8 *)&m_uiIVFactor, + sizeof(FLMUINT)))) + { + rc = RC_SET( NE_XFLM_NICI_BAD_RANDOM); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + } + + + // Increment each byte of the IV by the IV Factor + + for( uiLoop = 0; uiLoop < IV_SZ; uiLoop++) + { + (*pucIVPtr) += (FLMBYTE)m_uiIVFactor; + pucIVPtr++; + } + + // Now run the resulting IV through a SHA1 digest. + + algorithm.algorithm = (nuint8 *)oid_sha1; + algorithm.parameter = NULL; + algorithm.parameterLen = 0; + + if (RC_BAD( rc = CCS_DigestInit( + m_hContext, + &algorithm))) + { + rc = RC_SET( NE_XFLM_DIGEST_INIT_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + if (RC_BAD( rc = CCS_Digest( + m_hContext, + (nuint8 *)m_ucRndIV, + uiIVLen, + (nuint8 *)ucIVBuffer, + &uiIVBufferLen))) + { + rc = RC_SET( NE_XFLM_DIGEST_FAILED); + m_hContext = 0; // Context has been destroyed + goto Exit; + } + + // Return the new IV! + + f_memcpy( pucIV, ucIVBuffer, uiIVLen); + + m_uiIVFactor++; + +#endif + +Exit: + +#ifdef FLM_USE_NICI + if (bLocked) + { + f_mutexUnlock( m_hMutex); + } +#endif + + return rc; +} + + +/**************************************************************************** +Desc: flmDecryptBuffer - assumes aes +****************************************************************************/ +RCODE flmDecryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucBuffer); + F_UNREFERENCED_PARM( puiBufLen); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ATTRIBUTE find[2]; + NICI_OBJECT_HANDLE serverKeyHdl = 0; + FLMUINT uiCount; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + FLMBYTE pucIV[ IV_SZ]; + + /* Create NICI Context */ + + if (RC_BAD( rc = CCS_CreateContext(0, &context))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + goto Exit; + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + find[1].u.f.valueInfo = 0; + + if (RC_BAD( rc = CCS_FindObjectsInit(context, find, 2))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_INIT); + goto Exit; + } + + uiCount = 1; + + if (RC_BAD( rc = CCS_FindObjects( + context, + &serverKeyHdl, + &uiCount))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_OBJECT); + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( NE_XFLM_NICI_KEY_NOT_FOUND); + goto ExitCtx; + } + + /*Set up alogrithm now to do AES and pading for encryption */ + algorithm.algorithm = (nuint8 *)oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataDecryptInit( + context, + &algorithm, + serverKeyHdl))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_INIT_FAILED); + goto Exit; + } + + if (RC_BAD( rc = CCS_Decrypt( + context, + (nuint8 *)pucBuffer, + *puiBufLen, + (nuint8 *)pucBuffer, + puiBufLen))) + { + rc = RC_SET( NE_XFLM_NICI_DECRYPT_FAILED); + goto Exit; + } + +ExitCtx: + + CCS_DestroyContext( context); + +#endif + +Exit: + + return rc; + +} + +/**************************************************************************** +Desc: flmEncryptBuffer - assumes aes +****************************************************************************/ +RCODE flmEncryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + +#ifndef FLM_USE_NICI + F_UNREFERENCED_PARM( pucBuffer); + F_UNREFERENCED_PARM( puiBufLen); + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; +#else + NICI_CC_HANDLE context = 0; + NICI_ATTRIBUTE find[2]; + NICI_OBJECT_HANDLE serverKeyHdl = 0; + FLMUINT uiCount; + NICI_ALGORITHM algorithm; + NICI_PARAMETER_INFO parm[1]; + FLMBYTE oid_aes[] = {IDV_AES128CBC}; + FLMBYTE pucIV[ IV_SZ]; + + /* Create NICI Context */ + if (RC_BAD( rc = CCS_CreateContext(0, &context))) + { + rc = RC_SET( NE_XFLM_NICI_CONTEXT); + goto Exit; + } + + find[0].type = NICI_A_GLOBAL; + find[0].u.f.hasValue = 1; + find[0].u.f.value = 1; + find[0].u.f.valueInfo = 0; + + find[1].type = NICI_A_FEATURE; + find[1].u.f.hasValue = 1; + find[1].u.f.value = NICI_F_DATA_ENCRYPT | NICI_F_DATA_DECRYPT; + find[1].u.f.valueInfo = 0; + + if (RC_BAD( rc = CCS_FindObjectsInit(context, find, 2))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_INIT); + goto Exit; + } + + uiCount = 1; + + if (RC_BAD( rc = CCS_FindObjects( + context, + &serverKeyHdl, + &uiCount))) + { + rc = RC_SET( NE_XFLM_NICI_FIND_OBJECT); + goto Exit; + } + + if (uiCount < 1) + { + rc = RC_SET( NE_XFLM_NICI_KEY_NOT_FOUND); + goto ExitCtx; + } + + + algorithm.algorithm = (nuint8 *)oid_aes; + algorithm.parameterLen = sizeof(algorithm.parameter->parms[0])+ + sizeof(algorithm.parameter->count); + algorithm.parameter = parm; + algorithm.parameter->count = 1; + algorithm.parameter->parms[0].parmType = NICI_P_IV; + algorithm.parameter->parms[0].u.b.len = IV_SZ; /* 16-byte IV */ + algorithm.parameter->parms[0].u.b.ptr = (nuint8 *)pucIV; + + GetIV(pucIV, IV_SZ); + + /* init encryption */ + + if (RC_BAD( rc = CCS_DataEncryptInit( + context, + &algorithm, + serverKeyHdl))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NICI_ENC_INIT_FAILED); + goto Exit; + } + + if (RC_BAD( rc = CCS_Encrypt( + context, + (nuint8 *)pucBuffer, + *puiBufLen, + (nuint8 *)pucBuffer, + puiBufLen))) + { + rc = RC_SET( NE_XFLM_NICI_ENCRYPT_FAILED); + goto Exit; + } + +ExitCtx: + + CCS_DestroyContext( context); + +#endif + +Exit: + + return rc; + +} + +#ifdef FLM_USE_NICI +FSTATIC void GetIV( + FLMBYTE * pucIV, + FLMUINT //uiLen + ) +{ + FLMUINT uiLoop; + FLMUINT uiLoop2; + + f_sprintf( (char *)pucIV, "3587903781145935"); + + for (uiLoop = 0; uiLoop < 100; uiLoop++) + { + for ( uiLoop2 = 0; uiLoop2 < IV_SZ; uiLoop2++) + { + pucIV[IV_SZ - uiLoop2] ^= pucIV[ uiLoop2]; + pucIV[IV_SZ - uiLoop2] += pucIV[ uiLoop2]; + pucIV[IV_SZ - uiLoop2] ^= pucIV[ uiLoop2]; + } + + } + +} +#endif + + +#ifdef FLM_USE_NICI +#ifndef FLM_UNIX +int CCSX_SetNewIV( + int ,//MODULEID, + FLMUINT32 ,//hContext, + pnuint8 ,//IV, + nuint32 //IVLen + ) +{ + return(NICI_E_FUNCTION_NOT_SUPPORTED); +} +#endif +#endif + diff --git a/version5/src/f_nici.h b/version5/src/f_nici.h new file mode 100644 index 0000000..02ad546 --- /dev/null +++ b/version5/src/f_nici.h @@ -0,0 +1,275 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the definitions needed for the NICI interface +// functions. +// +// 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: f_nici.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef _F_NICI_HPP +#define _F_NICI_HPP + +#ifdef FLM_USE_NICI + #ifdef FLM_NLM + #define N_PLAT_NLM + #endif + + #include "nwccs.h" + + #ifndef IDV_NOV_AES128CBCPad + #define IDV_NOV_AES128CBCPad NICI_AlgorithmPrefix(1), 97 /* 0x61 */ + #endif +#else + #define NICI_OBJECT_HANDLE void * + #define NICI_CC_HANDLE FLMUINT32 +#endif + +/*-------------------------------------------------------------------------- + * Definitions + *------------------------------------------------------------------------*/ + +#define IV_SZ 16 +#define IV_SZ8 8 +#define SALT_SZ 8 +#define SALT_COUNT 895 + +// These values are used to identify the algorithm for encryption. They map to indexes in the DDEncOpts +// table defined in ddprep.c. +#define FLM_NICI_AES 0 +#define FLM_NICI_DES3 1 +#define FLM_NICI_UNDEFINED 0xFF + +#define XFLM_NICI_AES128 128 +#define XFLM_NICI_AES192 192 +#define XFLM_NICI_AES256 256 +#define XFLM_NICI_DES3X 168 + +/*----------------------------------------------------------------------- + * CCS Interface. + *-----------------------------------------------------------------------*/ +class IF_CCS +{ +public: + + virtual RCODE generateEncryptionKey( + FLMUINT uiEncKeySize) = 0; + + virtual RCODE generateWrappingKey( + FLMUINT uiEncKeySize) = 0; + + virtual RCODE encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV = NULL) = 0; + + virtual RCODE decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV = NULL) = 0; + +}; // IF_CCS + + + +class F_CCS : public IF_CCS, public XF_Base, public XF_RefCount +{ +public: + + // Constructor & destructor + F_CCS() + { + m_bInitCalled = FALSE; + m_bKeyVerified = FALSE; + f_memset( m_ucIV, 0, IV_SZ); + //m_bKeyIsWrappingKey = FALSE; + //m_uiAlgType = FLM_NICI_UNDEFINED; + m_keyHandle = 0; + m_hContext = 0; + m_uiEncKeySize = 0; + m_hMutex = F_MUTEX_NULL; + + } + + ~F_CCS(); + + RCODE init( + FLMBOOL bKeyIsWrappingKey, + FLMUINT uiAlgType); + + RCODE generateEncryptionKey( + FLMUINT uiEncKeySize); + + RCODE generateWrappingKey( + FLMUINT uiEncKeySize); + + RCODE encryptToStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV = NULL); + + RCODE decryptFromStore( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV = NULL); + + RCODE getKeyToStore( + FLMBYTE ** ppucKeyInfo, + FLMUINT32 * pui32BufLen, + FLMBYTE * pzEncKeyPasswd = NULL, + F_CCS * pWrappingCcs = NULL); + + RCODE setKeyFromStore( + FLMBYTE * pucKeyInfo, + FLMBYTE * pszEncKeyPasswd = NULL, + F_CCS * pWrappingCcs = NULL); + + FINLINE FLMBOOL keyVerified() + { + return m_bKeyVerified; + } + + FINLINE FLMUINT getEncType( void) + { + return m_uiAlgType; + } + + FLMUINT getIVLen(); + + RCODE generateIV( + FLMUINT uiIVLen, + FLMBYTE * pucIV); + +private: + + RCODE getWrappingKey( + NICI_OBJECT_HANDLE * pWrappingKeyHandle); + + RCODE wrapKey( + FLMBYTE ** ppucWrappedKey, + FLMUINT32 * pui32Length, + NICI_OBJECT_HANDLE masterWrappingKey = 0 ); + + RCODE unwrapKey( + FLMBYTE * pucWrappedKey, + FLMUINT32 ui32WrappedKeyLength, + NICI_OBJECT_HANDLE masterWrappingKey = 0); + + RCODE extractKey( + FLMBYTE ** ppucShroudedKey, + FLMUINT32 * pui32Length, + FLMUNICODE * puzEncKeyPasswd ); + + RCODE injectKey( + FLMBYTE * pucBuffer, + FLMUINT32 ui32Length, + FLMUNICODE * puzEncKeyPasswd ); + + RCODE encryptToStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE encryptToStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE encryptToStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE decryptFromStoreAES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE decryptFromStoreDES3( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE decryptFromStoreDES( + FLMBYTE * pucIn, + FLMUINT uiInLen, + FLMBYTE * pucOut, + FLMUINT * puiOutLen, + FLMBYTE * pucIV); + + RCODE generateEncryptionKeyAES( + FLMUINT uiEncKeySize); + + RCODE generateEncryptionKeyDES3( + FLMUINT uiEncKeySize); + + RCODE generateEncryptionKeyDES( + FLMUINT uiEncKeySize); + + RCODE generateWrappingKeyAES( + FLMUINT uiEncKeySize); + + RCODE generateWrappingKeyDES3( + FLMUINT uiEncKeySize); + + RCODE generateWrappingKeyDES( + FLMUINT uiEncKeySize); + + FLMUINT m_uiAlgType; + FLMBOOL m_bInitCalled; + FLMBOOL m_bKeyIsWrappingKey; + FLMBOOL m_bKeyVerified; + NICI_OBJECT_HANDLE m_keyHandle; // Handle to the clear key - we don't ever get the actual key. + FLMBYTE m_ucIV[ IV_SZ]; // Used when the algorithm type is DES, 3DES or AES + FLMBYTE m_ucRndIV[ IV_SZ]; // Used when the IV is stored with the data. + FLMUINT m_uiIVFactor; + NICI_CC_HANDLE m_hContext; + FLMUINT m_uiEncKeySize; + F_MUTEX m_hMutex; + +}; // F_CCS + +RCODE flmDecryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +RCODE flmEncryptBuffer( + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +#endif /* _F_NICI_HPP */ diff --git a/version5/src/fbtrset.cpp b/version5/src/fbtrset.cpp new file mode 100644 index 0000000..6396dcc --- /dev/null +++ b/version5/src/fbtrset.cpp @@ -0,0 +1,615 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains routines that implement a result set using +// a temporary XFLAIM database. +// +// 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: fbtrset.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_BtResultSet::~F_BtResultSet() +{ + // Free the collection table if it was ever created. + + if (m_ppCollectionTbl) + { + FLMUINT uiLoop; + + for (uiLoop = 0; uiLoop < BT_MAX_COLLECTION_TBL_SIZ; uiLoop++) + { + if (m_ppCollectionTbl[ uiLoop] != NULL) + { + BT_COLLECTION_XREF * pTmp; + + while (m_ppCollectionTbl[ uiLoop] != NULL) + { + pTmp = m_ppCollectionTbl[ uiLoop]; + m_ppCollectionTbl[ uiLoop] = pTmp->pNext; + if (pTmp && pTmp->pCompare) + { + pTmp->pCompare->Release(); + } + f_free( &pTmp); + } + } + } + f_free( &m_ppCollectionTbl); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getBTree( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree ** ppBTree) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollHash; + BT_COLLECTION_XREF * pCollPtr = NULL; + F_RandomGenerator * pRandGen = NULL; + F_Database * pDatabase; + FLMUINT uiCollection; + + if (RC_BAD( rc = m_pBtPool->btpReserveBtree( ppBTree))) + { + goto Exit; + } + + if (pSrcIxd) + { + if (!m_ppCollectionTbl) + { + if (RC_BAD( rc = f_calloc( + BT_MAX_COLLECTION_TBL_SIZ * sizeof(BT_COLLECTION_XREF), + &m_ppCollectionTbl))) + { + goto Exit; + } + } + + uiCollHash = pSrcIxd->uiIndexNum % BT_MAX_COLLECTION_TBL_SIZ; + + pCollPtr = m_ppCollectionTbl[ uiCollHash]; + + // Verify that we have the right collection + while (pCollPtr && pCollPtr->uiKeyNum != pSrcIxd->uiIndexNum) + { + pCollPtr = pCollPtr->pNext; + } + + if (!pCollPtr) + { + pDatabase = m_pResultSetDb->m_pDatabase; + + // Will need a random number generator. + + if (( pRandGen = f_new F_RandomGenerator) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pRandGen->randomSetSeed( (FLMINT32)pSrcIxd->uiIndexNum); + + // Allocate a new collection key context and create a new + // collection for it. + + if (RC_BAD( rc = f_calloc( sizeof(BT_COLLECTION_XREF), &pCollPtr))) + { + goto Exit; + } + + // Insert into the table at the head of the list. + + pCollPtr->pCompare = NULL; + pCollPtr->pNext = m_ppCollectionTbl[ uiCollHash]; + m_ppCollectionTbl[ uiCollHash] = pCollPtr; + +TryAgain: + + // Randomly select a collection number to use. + + uiCollection = pRandGen->randomChoice( 100, XFLM_MAX_COLLECTION_NUM); + + // Check to see if it already exists. + if (RC_BAD( rc = pDatabase->lFileCreate( m_pResultSetDb, + &pCollPtr->Collection.lfInfo, &pCollPtr->Collection, + uiCollection, XFLM_LF_COLLECTION, FALSE, TRUE, + pSrcIxd->lfInfo.uiEncId))) + { + if (rc != NE_XFLM_EXISTS) + { + goto Exit; + } + rc = NE_XFLM_OK; + goto TryAgain; + } + + pCollPtr->uiKeyNum = pSrcIxd->uiIndexNum; + pCollPtr->uiCollection = uiCollection; + + // Set up the comparison object. + + if ((pCollPtr->pCompare = f_new IXKeyCompare) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + pCollPtr->pCompare->setIxInfo( pSrcDb, pSrcIxd); + + // Open the btree and use the specified collection. + + if (RC_BAD( rc = (*ppBTree)->btOpen( m_pResultSetDb, + &pCollPtr->Collection.lfInfo, + FALSE, TRUE, pCollPtr->pCompare))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = (*ppBTree)->btOpen( m_pResultSetDb, + &m_Collection.lfInfo, + FALSE, TRUE, NULL))) + { + goto Exit; + } + } + +Exit: + + if (pRandGen) + { + pRandGen->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::addEntry( + F_Db * pSrcDb, + IXD * pSrcIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyLength, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + + flmAssert( uiKeyLength <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btInsertEntry( pucKey, uiKeyLength, + uiKeyLength, pucEntry, uiEntryLength, TRUE, TRUE))) + { + if (rc == NE_XFLM_NOT_UNIQUE) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + if (pBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::modifyEntry( + F_Db * pSrcDb, + IXD * pSrcIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyLength, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + + if (RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + + flmAssert( uiKeyLength <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btReplaceEntry( pucKey, uiKeyLength, + uiKeyLength, pucEntry, uiEntryLength, TRUE, TRUE))) + { + goto Exit; + } + +Exit: + + if( pBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::deleteEntry( + F_Db * pSrcDb, + IXD * pSrcIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyLength) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + + if (RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + + flmAssert( uiKeyLength <= MAX_KEY_SIZ); + + if (RC_BAD( rc = pBTree->btRemoveEntry( pucKey, + uiKeyLength, + uiKeyLength))) + { + goto Exit; + } + +Exit: + + if (pBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::findEntry( + F_Db * pSrcDb, + IXD * pSrcIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeyLen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLengthRV; + F_Btree * pBTree = NULL; + + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + + flmAssert( uiKeyBufLen <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btLocateEntry( pucKey, uiKeyBufLen, puiKeyLen, + XFLM_EXACT, NULL, &uiLengthRV))) + { + goto Exit; + } + + if( pucBuffer) + { + // Get the entry ... + + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, uiKeyBufLen, *puiKeyLen, + pucBuffer, uiBufferLength, puiReturnLength))) + { + goto Exit; + } + } + else if( puiReturnLength) + { + *puiReturnLength = uiLengthRV; + } + +Exit: + + if( pBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getCurrent( + F_Db * pSrcDb, + IXD * pSrcIxd, + FLMBYTE * pucKey, + FLMUINT uiKeyLength, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + + flmAssert( uiKeyLength <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, uiKeyLength, uiKeyLength, + pucEntry, uiEntryLength, puiReturnLength))) + { + goto Exit; + } + +Exit: + + if( pBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getNext( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree * pBTree, + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeyLen, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bFreeBTree = FALSE; + + if( !pBTree) + { + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + bFreeBTree = TRUE; + } + + flmAssert( uiKeyBufLen <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btNextEntry( pucKey, uiKeyBufLen, puiKeyLen, + puiReturnLength))) + { + goto Exit; + } + + if( pucEntry) + { + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, + pucEntry, uiEntryLength, puiReturnLength))) + { + goto Exit; + } + } + +Exit: + + if( bFreeBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getPrev( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree * pBTree, + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeyLen, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bFreeBTree = FALSE; + + if( !pBTree) + { + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + bFreeBTree = TRUE; + } + + flmAssert( uiKeyBufLen <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btPrevEntry( pucKey, uiKeyBufLen, puiKeyLen, + puiReturnLength))) + { + goto Exit; + } + + if( pucEntry) + { + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, + pucEntry, uiEntryLength, puiReturnLength))) + { + goto Exit; + } + } + +Exit: + + if( bFreeBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getFirst( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree * pBTree, + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeyLen, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bFreeBTree = FALSE; + + if( !pBTree) + { + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + bFreeBTree = TRUE; + } + + flmAssert( uiKeyBufLen <= MAX_KEY_SIZ); + + pBTree->btResetBtree(); + + if( RC_BAD( rc = pBTree->btFirstEntry( pucKey, uiKeyBufLen, puiKeyLen, + puiReturnLength))) + { + goto Exit; + } + + if( pucEntry) + { + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, + pucEntry, uiEntryLength, puiReturnLength))) + { + goto Exit; + } + } + +Exit: + + if( bFreeBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_BtResultSet::getLast( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree * pBTree, + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeyLen, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bFreeBTree = FALSE; + + if( !pBTree) + { + if( RC_BAD( rc = getBTree( pSrcDb, pSrcIxd, &pBTree))) + { + goto Exit; + } + bFreeBTree = TRUE; + } + + flmAssert( uiKeyBufLen <= MAX_KEY_SIZ); + + if( RC_BAD( rc = pBTree->btLastEntry( pucKey, uiKeyBufLen, puiKeyLen, + puiReturnLength))) + { + goto Exit; + } + + if( pucEntry) + { + if( RC_BAD( rc = pBTree->btGetEntry( pucKey, *puiKeyLen, *puiKeyLen, + pucEntry, uiEntryLength, puiReturnLength))) + { + goto Exit; + } + } + +Exit: + + if( bFreeBTree) + { + m_pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} diff --git a/version5/src/fbtrset.h b/version5/src/fbtrset.h new file mode 100644 index 0000000..7f4e867 --- /dev/null +++ b/version5/src/fbtrset.h @@ -0,0 +1,185 @@ +//------------------------------------------------------------------------------ +// Desc: This File contains routines which do certain types of verifications +// on objects in a FLAIM database. +// +// 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: fbtrset.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef BTRSET_H +#define BTRSET_H + +#include "f_btpool.h" +#include "f_btree.h" + +class IXKeyCompare; + +typedef struct BtCollXref +{ + FLMUINT uiKeyNum; + FLMUINT uiCollection; + F_COLLECTION Collection; + struct BtCollXref * pNext; + IXKeyCompare * pCompare; +} BT_COLLECTION_XREF; + +#define BT_MAX_COLLECTION_TBL_SIZ 256 + +/*============================================================================= +Desc: Result set class that uses an independant database. The name is randomly + generated. +=============================================================================*/ +class F_BtResultSet : public XF_RefCount, public XF_Base +{ +public: + + F_BtResultSet( + F_Db * pResultSetDb, + F_BtPool * pBtPool) + { + m_pBtPool = pBtPool; + m_pResultSetDb = pResultSetDb; + f_memset( &m_Collection, 0, sizeof( m_Collection)); + m_ppCollectionTbl = NULL; + } + + ~F_BtResultSet(); + + // Entry Add and Sort Methods + + RCODE addEntry( // Variable or fixed length entry coming in + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + FLMBYTE * pucKey, // key for sorting. + FLMUINT uiKeyLength, + FLMBYTE * pEntry, + FLMUINT uiEntryLength); // If length is zero then ignore entry. + + RCODE modifyEntry( // Modify current entry. + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + FLMBYTE * pucKey, + FLMUINT uiKeyLength, + FLMBYTE * pEntry, // Points to entry buffer + FLMUINT uiEntryLength); + + // Methods to read entries. + + RCODE getCurrent( // Return current entry + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + FLMBYTE * pucKey, + FLMUINT uiKeyLength, + FLMBYTE * pucEntry, + FLMUINT uiEntryLength, // Size of Entry buffer. + FLMUINT * puiReturnLength); + + RCODE getNext( + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + F_Btree * pBTree, // Preserves the context from one call to + // the next. May be null if not needed. + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeylen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE getPrev( // Position to previous entry and return + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + F_Btree * pBTree, // Preserves the context from one call to + // the next. May be null if not needed. + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeylen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE getFirst( // Position to the first entry and return + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + F_Btree * pBTree, // Preserves the context from one call to + // the next. May be null if not needed. + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeylen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE getLast( // Position to the last entry and return + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + F_Btree * pBTree, // Preserves the context from one call to + // the next. May be null if not needed. + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeylen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE findEntry( // Locate an entry + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + FLMBYTE * pucKey, + FLMUINT uiKeyBufLen, + FLMUINT * puiKeylen, + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE deleteEntry( + F_Db * pSrcDb, // Set for when we are keeping index keys + IXD * pSrcIxd, // Set for when we are keeping index keys + FLMBYTE * pucKey, + FLMUINT uiKeyLength); + + // Methods for managing context + + RCODE getBTree( + F_Db * pSrcDb, + IXD * pSrcIxd, + F_Btree ** ppBtree); + + FINLINE void freeBTree( + F_Btree ** ppBTree) + { + flmAssert( *ppBTree); + + m_pBtPool->btpReturnBtree( ppBTree); + + *ppBTree = NULL; + } + +private: + + F_BtPool * m_pBtPool; + F_Db * m_pResultSetDb; + F_COLLECTION m_Collection; + BT_COLLECTION_XREF ** m_ppCollectionTbl; + + friend class F_DbCheck; +}; +#endif diff --git a/version5/src/fbuff.cpp b/version5/src/fbuff.cpp new file mode 100644 index 0000000..b095317 --- /dev/null +++ b/version5/src/fbuff.cpp @@ -0,0 +1,557 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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_XFLM_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 F_IOBufferMgr::waitForAllPendingIO( void) +{ + RCODE rc = NE_XFLM_OK; + F_IOBuffer * pBuf; + + while( (pBuf = m_pFirstPending) != NULL) + { + (void)pBuf->waitToComplete(); + } + + rc = m_completionRc; + m_completionRc = NE_XFLM_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 F_IOBufferMgr::getBuffer( + F_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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 + m_pDbStats = NULL; +} + +/**************************************************************************** +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 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 F_IOBuffer::setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize) +{ + RCODE rc = NE_XFLM_OK; + +#if defined( FLM_WIN) + if( (m_Overlapped.hEvent = CreateEvent( NULL, TRUE, + FALSE, NULL)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_MEM); + goto Exit; + } +#elif defined( FLM_LINUX) + if( posix_memalign( (void **)&m_pucBuffer, + sysconf( _SC_PAGESIZE), uiBufferSize) != 0) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_MEM); + goto Exit; + } +#elif defined( FLM_SOLARIS) + if( (m_pucBuffer = (FLMBYTE *)memalign( sysconf( _SC_PAGESIZE), + uiBufferSize)) == NULL) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_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 F_IOBuffer::notifyComplete( + RCODE rc) +{ + flmAssert( m_eList == MGR_LIST_PENDING || + m_eList == MGR_LIST_USED); + + m_completionRc = rc; + endTimer(); + 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_XFLM_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 = MapWinErrorToFlaim( GetLastError(), + NE_XFLM_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 = MapErrnoToFlaimErr( errno, NE_XFLM_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: +****************************************************************************/ +void F_IOBuffer::endTimer( void) +{ + if (m_pDbStats) + { + flmAddElapTime( &m_StartTime, &m_ui64ElapMilli); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_NLM +void F_IOBuffer::signalComplete( + RCODE rc) +{ + m_completionRc = rc; + flmAssert( kSemaphoreExamineCount( (SEMAPHORE)m_hSem) == 0); + kSemaphoreSignal( (SEMAPHORE)m_hSem); +} +#endif diff --git a/version5/src/fbuff.h b/version5/src/fbuff.h new file mode 100644 index 0000000..de5b64e --- /dev/null +++ b/version5/src/fbuff.h @@ -0,0 +1,276 @@ +//------------------------------------------------------------------------------ +// 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.h 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#ifndef FBUFF_H +#define FBUFF_H + +class F_IOBuffer; +class F_IOBufferMgr; + +typedef void (* WRITE_COMPLETION_CB)( + F_IOBuffer * pWriteBuffer); + +/*=========================================================================== +Class: F_IOBufferMgr +===========================================================================*/ +class F_IOBufferMgr : public XF_RefCount, public XF_Base +{ +public: + + // Constructor + + F_IOBufferMgr(); + + // Destructor + + virtual ~F_IOBufferMgr(); + + RCODE waitForAllPendingIO( void); + + FINLINE void setMaxBuffers( + FLMUINT uiMaxBuffers) + { + m_uiMaxBuffers = uiMaxBuffers; + } + + FINLINE void setMaxBytes( + FLMUINT uiMaxBytes) + { + m_uiMaxBufferBytesToUse = uiMaxBytes; + } + + FINLINE void enableKeepBuffer( void) + { + m_bKeepBuffers = TRUE; + } + + RCODE getBuffer( + F_IOBuffer ** ppIOBuffer, + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMBOOL havePendingIO( void) + { + return( m_pFirstPending ? TRUE : FALSE); + } + + FINLINE FLMBOOL 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; + +}; + +/*=========================================================================== +Class: F_IOBuffer +===========================================================================*/ +class F_IOBuffer : public XF_RefCount, public XF_Base +{ +#define MAX_BUFFER_BLOCKS 16 +public: + + typedef enum + { + MGR_LIST_NONE, + MGR_LIST_AVAIL, + MGR_LIST_PENDING, + MGR_LIST_USED + } eBufferMgrList; + + // Constructor + + F_IOBuffer(); + + // Destructor + + ~F_IOBuffer(); + + RCODE setupBuffer( + FLMUINT uiBufferSize, + FLMUINT uiBlockSize); + + FINLINE FLMBYTE * getBuffer( void) + { + return( m_pucBuffer); + } + + FINLINE FLMUINT getBufferSize( void) + { + return( m_uiBufferSize); + } + + FINLINE FLMUINT getBlockSize( void) + { + return( m_uiBlockSize); + } + + void notifyComplete( + RCODE rc); + + FINLINE void setCompletionCallback( + WRITE_COMPLETION_CB fnCompletion) + { + m_fnCompletion = fnCompletion; + } + + FINLINE void startTimer( + XFLM_DB_STATS * pDbStats) + { + if ((m_pDbStats = pDbStats) != NULL) + { + m_ui64ElapMilli = 0; + f_timeGetTimeStamp( &m_StartTime); + } + } + + void endTimer( void); + + FINLINE FLMUINT64 getElapTime( void) + { + return( m_ui64ElapMilli); + } + + FINLINE XFLM_DB_STATS * getDbStats( void) + { + return( m_pDbStats); + } + + FINLINE void setCompletionCallbackData( + FLMUINT uiBlockNumber, + void * pvData) + { + flmAssert( uiBlockNumber < MAX_BUFFER_BLOCKS); + m_UserData [uiBlockNumber] = pvData; + } + + FINLINE void * getCompletionCallbackData( + FLMUINT uiBlockNumber) + { + flmAssert( uiBlockNumber < MAX_BUFFER_BLOCKS); + return( m_UserData [uiBlockNumber]); + } + + FINLINE RCODE getCompletionCode( void) + { + return( m_completionRc); + } + + FINLINE eBufferMgrList getList( void) + { + return( m_eList); + } + + void 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; + XFLM_DB_STATS * m_pDbStats; + RCODE m_completionRc; + F_TMSTAMP m_StartTime; + FLMUINT64 m_ui64ElapMilli; + + friend class F_IOBufferMgr; +}; + +#endif // #ifndef FBUFF_H diff --git a/version5/src/fcache.h b/version5/src/fcache.h new file mode 100644 index 0000000..d9d39fa --- /dev/null +++ b/version5/src/fcache.h @@ -0,0 +1,4282 @@ +//------------------------------------------------------------------------------ +// Desc: Various classes used to manage cache. +// +// 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: fcache.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FCACHE_H +#define FCACHE_H + +#include "flfixed.h" + +class F_Rfl; +class F_DOMNode; +class F_Db; +class F_DbSystem; +class F_Database; +class F_MultiAlloc; +class F_LocalNodeCache; +class F_CachedNode; +class F_CachedBlock; +class F_BlockCacheMgr; +class F_NodeCacheMgr; +class F_GlobalCacheMgr; +class F_CacheList; +class F_CachedItem; +class F_Btree; +class F_BTreeIStream; +class F_NodeInfo; +class F_BTreeInfo; +class F_DynaBuf; +class F_AttrItem; +class F_NodeRelocator; +class F_NodeDataRelocator; +class F_NodeListRelocator; +class F_AttrListRelocator; +class F_AttrItemRelocator; +class F_AttrBufferRelocator; +class F_BlockRelocator; + +#define MIN_HASH_BUCKETS 0x10000 // 65536 buckets - multiple of 2. +#define MAX_HASH_BUCKETS 0x20000000 // roughly 500,000,000 buckets. + +FLMUINT caGetBestHashTblSize( // scache.cpp + FLMUINT uiCurrItemCount); + +FINLINE FLMUINT minItemCount( + FLMUINT uiNumHashBuckets) +{ + return( uiNumHashBuckets / 4); +} + +FINLINE FLMUINT maxItemCount( + FLMUINT uiNumHashBuckets) +{ + return( uiNumHashBuckets * 4); +} + +FINLINE FLMBOOL shouldRehash( + FLMUINT uiItemCount, + FLMUINT uiNumBuckets) +{ + return( ((uiItemCount > maxItemCount( uiNumBuckets) && + uiNumBuckets < MAX_HASH_BUCKETS) || + (uiItemCount < minItemCount( uiNumBuckets) && + uiNumBuckets > MIN_HASH_BUCKETS)) + ? TRUE + : FALSE); +} + +/*************************************************************************** +Desc: See if enough time has passed since we last tried to rehash. We + don't want to be continually trying to rehash. +***************************************************************************/ +FINLINE FLMBOOL checkHashFailTime( + FLMUINT * puiHashFailTime) +{ + if (*puiHashFailTime) + { + FLMUINT uiCurrTime = FLM_GET_TIMER(); + + if (FLM_ELAPSED_TIME( uiCurrTime, (*puiHashFailTime)) >= + gv_XFlmSysData.uiRehashAfterFailureBackoffTime) + { + *puiHashFailTime = 0; + return( TRUE); + } + else + { + return( FALSE); + } + } + else + { + return( TRUE); + } +} + +/***************************************************************************** +Desc: Cached item +******************************************************************************/ +class F_CachedItem +{ +public: + + F_CachedItem() + { + m_pNextInGlobal = NULL; + m_pPrevInGlobal = NULL; + } + + virtual ~F_CachedItem() + { + } + +#ifdef FLM_CACHE_PROTECT + virtual void protectCachedItem( void) = 0; + + virtual void unprotectCachedItem( void) = 0; +#endif + +private: + + F_CachedItem * m_pPrevInGlobal; + F_CachedItem * m_pNextInGlobal; + +friend class F_CacheList; +friend class F_CachedBlock; +friend class F_CachedNode; +friend class F_GlobalCacheMgr; +friend class F_BlockCacheMgr; +friend class F_NodeCacheMgr; +friend class F_Database; +friend class F_Db; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +friend class F_BlockRelocator; +}; + +/*************************************************************************** +Desc: Object for keeping track of an MRU/LRU list of cached items (nodes + or blocks) +***************************************************************************/ +class F_CacheList +{ +public: + + F_CacheList() + { + m_pMRUItem = NULL; + m_pLRUItem = NULL; + m_pLastMRUItem = NULL; + } + + ~F_CacheList() + { + flmAssert( !m_pMRUItem); + flmAssert( !m_pLRUItem); + flmAssert( !m_pLastMRUItem); + } + + // Link a cached item into the global list as the MRU item. This routine + // assumes that the cache mutex for managing this list + // has already been locked. + + FINLINE void linkGlobalAsMRU( + F_CachedItem * pItem) + { +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + + if ((pItem->m_pNextInGlobal = m_pMRUItem) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->unprotectCachedItem(); +#endif + pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem; +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->protectCachedItem(); +#endif + } + else + { + m_pLRUItem = pItem; + m_pLastMRUItem = pItem; + } + + pItem->m_pPrevInGlobal = NULL; +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif + m_pMRUItem = pItem; + flmAssert( pItem != pItem->m_pPrevInGlobal); + flmAssert( pItem != pItem->m_pNextInGlobal); + } + + // Link a cached item into the global list as the last MRU item. + // This routine assumes that the cache mutex for managing this list + // has already been locked. + + FINLINE void linkGlobalAsLastMRU( + F_CachedItem * pItem) + { + if( !m_pLastMRUItem) + { + flmAssert( !m_pMRUItem); + linkGlobalAsMRU( pItem); + return; + } + + flmAssert( m_pLastMRUItem); + +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + + if( m_pLastMRUItem->m_pNextInGlobal) + { +#ifdef FLM_CACHE_PROTECT + m_pLastMRUItem->m_pNextInGlobal->unprotectCachedItem(); +#endif + m_pLastMRUItem->m_pNextInGlobal->m_pPrevInGlobal = pItem; +#ifdef FLM_CACHE_PROTECT + m_pLastMRUItem->m_pNextInGlobal->protectCachedItem(); +#endif + pItem->m_pNextInGlobal = m_pLastMRUItem->m_pNextInGlobal; + } + else + { + flmAssert( m_pLRUItem == m_pLastMRUItem); + m_pLRUItem = pItem; + } + +#ifdef FLM_CACHE_PROTECT + m_pLastMRUItem->unprotectCachedItem(); +#endif + m_pLastMRUItem->m_pNextInGlobal = pItem; +#ifdef FLM_CACHE_PROTECT + m_pLastMRUItem->protectCachedItem(); +#endif + + pItem->m_pPrevInGlobal = m_pLastMRUItem; + m_pLastMRUItem = pItem; + +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif + + flmAssert( pItem != pItem->m_pPrevInGlobal); + flmAssert( pItem != pItem->m_pNextInGlobal); + } + + // Link a cached item into the global list as the LRU item. This routine + // assumes that the cache mutex for managing this list + // has already been locked. + + FINLINE void linkGlobalAsLRU( + F_CachedItem * pItem) + { +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + + if ((pItem->m_pPrevInGlobal = m_pLRUItem) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pItem->m_pPrevInGlobal->unprotectCachedItem(); +#endif + pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem; +#ifdef FLM_CACHE_PROTECT + pItem->m_pPrevInGlobal->protectCachedItem(); +#endif + } + else + { + flmAssert( !m_pMRUItem); + flmAssert( !m_pLastMRUItem); + + m_pMRUItem = pItem; + m_pLastMRUItem = pItem; + } + + pItem->m_pNextInGlobal = NULL; +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif + m_pLRUItem = pItem; + + flmAssert( pItem != pItem->m_pPrevInGlobal); + flmAssert( pItem != pItem->m_pNextInGlobal); + } + + // Unlink a cached item from the global list. This routine + // assumes that the cache mutex for managing this list + // has already been locked. + + FINLINE void unlinkGlobal( + F_CachedItem * pItem) + { + if( pItem == m_pLastMRUItem) + { + if( m_pLastMRUItem->m_pPrevInGlobal) + { + m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal; + } + else + { + m_pLastMRUItem = m_pLastMRUItem->m_pNextInGlobal; + } + } + + if (pItem->m_pNextInGlobal) + { + flmAssert( pItem != m_pLRUItem); + +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->unprotectCachedItem(); +#endif + pItem->m_pNextInGlobal->m_pPrevInGlobal = pItem->m_pPrevInGlobal; +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->protectCachedItem(); +#endif + } + else + { + m_pLRUItem = pItem->m_pPrevInGlobal; + } + + if (pItem->m_pPrevInGlobal) + { + flmAssert( pItem != m_pMRUItem); + +#ifdef FLM_CACHE_PROTECT + pItem->m_pPrevInGlobal->unprotectCachedItem(); +#endif + pItem->m_pPrevInGlobal->m_pNextInGlobal = pItem->m_pNextInGlobal; +#ifdef FLM_CACHE_PROTECT + pItem->m_pPrevInGlobal->protectCachedItem(); +#endif + } + else + { + m_pMRUItem = pItem->m_pNextInGlobal; + } + +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + pItem->m_pNextInGlobal = NULL; + pItem->m_pPrevInGlobal = NULL; +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif + } + + // Moves a cached item one step closer to the MRU slot in the global list. + // This routine assumes that the cache mutex for managing this list + // has already been locked. + + FINLINE void stepUpInGlobal( + F_CachedItem * pItem) + { + F_CachedItem * pPrevItem; + + if ((pPrevItem = pItem->m_pPrevInGlobal) != NULL) + { + if( pItem == m_pLastMRUItem) + { + m_pLastMRUItem = m_pLastMRUItem->m_pPrevInGlobal; + } + + if (pPrevItem->m_pPrevInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pPrevItem->m_pPrevInGlobal->unprotectCachedItem(); +#endif + pPrevItem->m_pPrevInGlobal->m_pNextInGlobal = pItem; +#ifdef FLM_CACHE_PROTECT + pPrevItem->m_pPrevInGlobal->protectCachedItem(); +#endif + } + else + { + m_pMRUItem = pItem; + } + +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + pItem->m_pPrevInGlobal = pPrevItem->m_pPrevInGlobal; +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif +#ifdef FLM_CACHE_PROTECT + pPrevItem->unprotectCachedItem(); +#endif + pPrevItem->m_pPrevInGlobal = pItem; + pPrevItem->m_pNextInGlobal = pItem->m_pNextInGlobal; +#ifdef FLM_CACHE_PROTECT + pPrevItem->protectCachedItem(); +#endif + + if (pItem->m_pNextInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->unprotectCachedItem(); +#endif + pItem->m_pNextInGlobal->m_pPrevInGlobal = pPrevItem; +#ifdef FLM_CACHE_PROTECT + pItem->m_pNextInGlobal->protectCachedItem(); +#endif + } + else + { + m_pLRUItem = pPrevItem; + } + +#ifdef FLM_CACHE_PROTECT + pItem->unprotectCachedItem(); +#endif + pItem->m_pNextInGlobal = pPrevItem; +#ifdef FLM_CACHE_PROTECT + pItem->protectCachedItem(); +#endif + } + } + +private: + + F_CachedItem * m_pMRUItem; + F_CachedItem * m_pLRUItem; + F_CachedItem * m_pLastMRUItem; + +friend class F_CachedItem; +friend class F_NodeCacheMgr; +friend class F_BlockCacheMgr; +friend class F_CachedBlock; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +friend class F_BlockRelocator; +}; + +/*************************************************************************** +Desc: Global cache manager for XFLAIM. +***************************************************************************/ +class F_GlobalCacheMgr : public XF_RefCount, public XF_Base +{ +public: + F_GlobalCacheMgr(); + + ~F_GlobalCacheMgr(); + + RCODE setup( void); + + FINLINE void incrTotalBytes( + FLMUINT uiIncrAmount) + { + m_pSlabManager->incrementTotalBytesAllocated( uiIncrAmount, FALSE); + } + + FINLINE void decrTotalBytes( + FLMUINT uiDecrAmount) + { + m_pSlabManager->decrementTotalBytesAllocated( uiDecrAmount, FALSE); + } + + FINLINE FLMUINT totalBytes( void) + { + return( m_pSlabManager->totalBytesAllocated()); + } + + FINLINE FLMUINT availSlabs( void) + { + return( m_pSlabManager->availSlabs()); + } + + FINLINE FLMUINT allocatedSlabs( void) + { + return( m_pSlabManager->getTotalSlabs()); + } + + FINLINE FLMBOOL cacheOverLimit( void) + { + if( allocatedSlabs() > m_uiMaxSlabs) + { + return( TRUE); + } + + return( FALSE); + } + + RCODE setCacheLimit( + FLMUINT uiMaxCache, + FLMBOOL bPreallocateCache); + + RCODE setDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, + FLMUINT uiCacheAdjustMin, + FLMUINT uiCacheAdjustMax, + FLMUINT uiCacheAdjustMinToLeave); + + RCODE setHardMemoryLimit( + FLMUINT uiPercent, + FLMBOOL bPercentOfAvail, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bPreallocate); + + void getCacheInfo( + XFLM_CACHE_INFO * pMemInfo); + + RCODE adjustCache( + FLMUINT * puiCurrTime, + FLMUINT * puiLastCacheAdjustTime); + + RCODE clearCache( + IF_Db * pDb); + + FINLINE void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + FINLINE void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + +private: + + F_SlabManager * m_pSlabManager; + FLMUINT m_uiMaxBytes; + FLMUINT m_uiMaxSlabs; + FLMBOOL m_bCachePreallocated; + FLMBOOL m_bDynamicCacheAdjust; + // Is cache to be dynamically adjusted? + FLMUINT m_uiCacheAdjustPercent; + // Percent of available memory to adjust to. + FLMUINT m_uiCacheAdjustMin; + // Minimum limit to adjust cache to. + FLMUINT m_uiCacheAdjustMax; + // Maximum limit to adjust cache to. + FLMUINT m_uiCacheAdjustMinToLeave; + // Minimum bytes to leave when adjusting cache. + FLMUINT m_uiCacheAdjustInterval; + // Interval for adjusting cache limit. + FLMUINT m_uiCacheCleanupInterval; + // Interval for cleaning up old things out of + // cache. + FLMUINT m_uiUnusedCleanupInterval; + // Interval for cleaning up unused structures + F_MUTEX m_hMutex; // Mutex to control access to global cache + // manager object. + +friend class F_CachedItem; +friend class F_CachedNode; +friend class F_CachedBlock; +friend class F_BlockCacheMgr; +friend class F_NodeCacheMgr; +friend class F_Database; +friend class F_DbSystem; +}; + +/**************************************************************************** +Desc: Class for moving cache blocks in cache. +****************************************************************************/ +class F_BlockRelocator : public IF_Relocator +{ +public: + + F_BlockRelocator() + { + } + + virtual ~F_BlockRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: This class manages block cache. +****************************************************************************/ +class F_BlockCacheMgr : public XF_RefCount, public XF_Base +{ +public: + F_BlockCacheMgr(); + + ~F_BlockCacheMgr(); + + RCODE initCache( void); + + void cleanupLRUCache( void); + + void cleanupReplaceList( void); + + void cleanupFreeCache( void); + + void reduceReuseList( void); + + RCODE reduceCache( + F_Db * pDb); + + RCODE rehash( void); + + RCODE allocBlock( + F_Db * pDb, + F_CachedBlock ** ppSCache); + + // Returns a pointer to the correct entry in the block cache hash table for + // the given block address + + FINLINE F_CachedBlock ** blockHash( + FLMUINT uiSigBitsInBlkSize, + FLMUINT uiBlkAddress + ) + { + return( &m_ppHashBuckets[ (uiBlkAddress >> + uiSigBitsInBlkSize) & m_uiHashMask]); + } + + FINLINE void defragmentMemory( + FLMBOOL bMutexLocked = FALSE) + { + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + } + + m_blockAllocator.defragmentMemory(); + + if( !bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + } + + FINLINE void getUsageStats( + XFLM_SLAB_USAGE * pUsage) + { + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->lockMutex(); + f_memcpy( pUsage, &m_Usage, sizeof( XFLM_SLAB_USAGE)); + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->unlockMutex(); + } + +private: + + RCODE initHashTbl( void); + + F_CacheList m_MRUList; // List of all block objects in MRU order. + F_CachedBlock * m_pMRUReplace; // Pointer to the MRU end of the list + // of cache items with no flags set. + F_CachedBlock * m_pLRUReplace; // Pointer to the LRU end of the list + // of cache items with no flags set. + F_CachedBlock * m_pFirstFree; + // Pointer to a linked list of cache + // blocks that need to be freed. + // Cache blocks in this list are no + // longer associated with a file and can + // be freed or re-used as needed. They + // are linked using the pNextInFile and + // pPrevInFile pointers. + F_CachedBlock * m_pLastFree; + // Pointer to a linked list of cache + // blocks that need to be freed. + // Cache blocks in this list are no + // longer associated with a file and can + // be freed or re-used as needed. They + // are linked using the pNextInFile and + // pPrevInFile pointers. + XFLM_CACHE_USAGE m_Usage; // Contains usage information. + FLMUINT m_uiFreeBytes; // Number of free bytes + FLMUINT m_uiFreeCount; // Number of free blocks + FLMUINT m_uiReplaceableCount; + // Number of blocks whose flags are 0 + FLMUINT m_uiReplaceableBytes; + // Number of bytes belonging to blocks whose + // flags are 0 + FLMBOOL m_bAutoCalcMaxDirty; + // Flag indicating we should automatically + // calculate maximum dirty cache. + FLMUINT m_uiMaxDirtyCache; + // Maximum cache that can be dirty. + FLMUINT m_uiLowDirtyCache; + // When maximum dirty cache is exceeded, + // threshhold it should be brought back + // under + FLMUINT m_uiTotalUses; // Total number of uses currently held + // on blocks in cache. + FLMUINT m_uiBlocksUsed;// Total number of blocks in cache that + // are being used. + FLMUINT m_uiPendingReads; + // Total reads currently pending. + FLMUINT m_uiIoWaits; // Number of times multiple threads + // were reading the same block from + // disk at the same time. + F_CachedBlock ** m_ppHashBuckets;// This is a pointer to a hash table that + // is used to find cache blocks. Each + // element in the table points to a + // linked list of F_CachedBlock objects that + // all hash to the same hash bucket. + FLMUINT m_uiNumBuckets;// This contains the number of buckets + // in the hash table. + FLMUINT m_uiHashFailTime; + // Last time we tried to rehash and + // failed. Want to wait before we + // retry again. + FLMUINT m_uiHashMask; // Bits that are significant + // for the number of hash buckets. + F_MultiAlloc m_blockAllocator; + // Fixed size allocators for cache blocks + F_BlockRelocator m_blockRelocator; + // Relocator for cache blocks + FLMBOOL m_bReduceInProgress; +#ifdef FLM_DEBUG + FLMBOOL m_bDebug; // Enables checksumming and cache use + // monitoring. Only available when + // debug is compiled in. +#endif + +friend class F_CachedBlock; +friend class F_GlobalCacheMgr; +friend class F_Database; +friend class F_Db; +friend class F_DbSystem; +friend class F_BlockRelocator; +}; + +#ifdef FLM_DEBUG +/**************************************************************************** +Struct: SCACHE_USE (Cache Block Use) +Desc: This is a debug only structure that is used to keep track of the + threads that are currently using a block. +****************************************************************************/ +typedef struct SCache_Use +{ + SCache_Use * pNext; // Pointer to next SCACHE_USE structure in + // the list. + FLMUINT uiThreadId; // Thread ID of thread using the block. + FLMUINT uiUseCount; // Use count for this particular thread. +} SCACHE_USE; +#endif + +// Flags for m_ui16Flags field in F_CachedBlock + +#define CA_DIRTY 0x0001 + // This bit indicates that the block is + // dirty and needs to be flushed to disk. + // NOTE: For 3.x files, this bit may remain + // set on prior versions of blocks until the + // current transaction commits. +#define CA_WRITE_INHIBIT 0x0002 + // Must not write block until use count + // goes to zero. NOTE: Can ignore when + // in the checkpoint thread. +#define CA_READ_PENDING 0x0004 + // This bit indicates that the block is + // currently being read in from disk. +#define CA_WRITE_TO_LOG 0x0008 + // This bit indicates that this version of + // the block should be written to the + // rollback log before being replaced. + // During an update transaction, the first + // time a block is updated, FLAIM will + // create a new version of the block and + // insert it into cache. The prior version + // of the block is marked with this flag + // to indicate that it needs to be written + // to the log before it can be replaced. +#define CA_LOG_FOR_CP 0x0010 + // This bit indicates that this version of + // the block needs to be logged to the + // physical rollback in order to restore + // the last checkpoint. This is only + // applicable to 3.x files. +#define CA_WAS_DIRTY 0x0020 + // This bit indicates that this version of + // the block was dirty before the newer + // version of the block was created. + // Its dirty state should be restored if + // the current transaction aborts. This + // flag is only used for 3.x files. +#define CA_WRITE_PENDING 0x0040 + // This bit indicates that a block is in + // the process of being written out to + // disk. +#define CA_IN_WRITE_PENDING_LIST 0x0080 + // This bit indicates that a block is in + // the write pending list. +#define CA_FREE 0x0100 + // The block has been linked to the free + // list (and unlinked from all other lists) +#define CA_IN_FILE_LOG_LIST 0x0200 + // Block is in the list of blocks that may + // have one or more versions that need to + // be logged +#define CA_IN_NEW_LIST 0x0400 + // Dirty block that is beyond the last CP EOF +#define CA_DUMMY_FLAG 0x0800 + // Used to prevent blocks from being linked + // into the replace list in cases where + // they will be removed immediately (because + // a bit is going to being set) + +/**************************************************************************** +Desc: This is the header structure for a cached data block. +****************************************************************************/ +class F_CachedBlock : public F_CachedItem +{ +public: + F_CachedBlock( + FLMUINT uiBlockSize); + + ~F_CachedBlock(); + +#ifdef FLM_CACHE_PROTECT + FINLINE void protectCachedItem( void) + { + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.protectBuffer( this); + } +#endif + +#ifdef FLM_CACHE_PROTECT + FINLINE void unprotectCachedItem( void) + { + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.unprotectBuffer( this); + } +#endif + + FINLINE FLMUINT memSize( void) + { + return( gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.getTrueSize( + (FLMBYTE *)this)); + } + + FINLINE FLMUINT blkAddress( void) + { + return( m_uiBlkAddress); + } + + FINLINE F_BLK_HDR * getBlockPtr( void) + { + return( m_pBlkHdr); + } + + FINLINE F_Database * getDatabase( void) + { + return( m_pDatabase); + } + + FINLINE FLMUINT16 getModeFlags( void) + { + return( m_ui16Flags); + } + + FINLINE FLMUINT getUseCount( void) + { + return( m_uiUseCount); + } + + // Gets the prior image block address from the block header. + // NOTE: This function assumes that the block cache mutex is locked. + + FINLINE FLMUINT getPriorImageAddress( void) + { + return( (FLMUINT)m_pBlkHdr->ui32PriorBlkImgAddr); + } + + // Gets the transaction ID from the block header. NOTE: This function + // assumes that the block cache mutex is locked. + + FINLINE FLMUINT64 getLowTransID( void) + { + return( m_pBlkHdr->ui64TransID); + } + + // Set the high transaction ID for a cache block. + // NOTE: This function assumes that the block cache mutex is locked. + + FINLINE void setTransID( + FLMUINT64 ui64NewTransID) + { + if (m_ui64HighTransID == FLM_MAX_UINT64 && ui64NewTransID != FLM_MAX_UINT64) + { + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes += memSize(); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount++; + } + else if (m_ui64HighTransID != FLM_MAX_UINT64 && ui64NewTransID == FLM_MAX_UINT64) + { + FLMUINT uiSize = memSize(); + + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes >= uiSize); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount--; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_ui64HighTransID = ui64NewTransID; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Determines if a cache block is needed by a read transaction. + FINLINE FLMBOOL neededByReadTrans( void) + { + return( m_pDatabase->neededByReadTrans( getLowTransID(), + m_ui64HighTransID)); + } + + // Link a cache block into the replace list as the MRU item. This routine + // assumes that the block cache mutex has already been locked. + FINLINE void linkToReplaceListAsMRU( void) + { + flmAssert( !m_ui16Flags); + + if ((m_pNextInReplaceList = + gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = this; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pPrevInReplaceList = NULL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++; + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize(); + } + + // Link a cache block into the replace list as the LRU item. This routine + // assumes that the block cache mutex has already been locked. + FINLINE void linkToReplaceListAsLRU( void) + { + flmAssert( !m_ui16Flags); + + if ((m_pPrevInReplaceList = gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->unprotectCachedItem(); +#endif + m_pPrevInReplaceList->m_pNextInReplaceList = this; +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInReplaceList = NULL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = this; + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount++; + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes += memSize(); + } + + // Moves a block one step closer to the MRU slot in the replace list. + // This routine assumes that the block cache mutex has already been locked. + FINLINE void stepUpInReplaceList( void) + { + F_CachedBlock * pPrevSCache; + + flmAssert( !m_ui16Flags); + + if( (pPrevSCache = m_pPrevInReplaceList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if( pPrevSCache->m_pPrevInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + pPrevSCache->m_pPrevInReplaceList->unprotectCachedItem(); +#endif + pPrevSCache->m_pPrevInReplaceList->m_pNextInReplaceList = this; +#ifdef FLM_CACHE_PROTECT + pPrevSCache->m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = this; + } + + m_pPrevInReplaceList = pPrevSCache->m_pPrevInReplaceList; + +#ifdef FLM_CACHE_PROTECT + pPrevSCache->unprotectCachedItem(); +#endif + pPrevSCache->m_pPrevInReplaceList = this; + pPrevSCache->m_pNextInReplaceList = m_pNextInReplaceList; +#ifdef FLM_CACHE_PROTECT + pPrevSCache->protectCachedItem(); +#endif + + if( m_pNextInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = pPrevSCache; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = pPrevSCache; + } + + m_pNextInReplaceList = pPrevSCache; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + } + + // Clears the passed-in flags from the F_CachedBlock object + // This routine assumes that the block cache mutex is locked. + FINLINE void clearFlags( + FLMUINT16 ui16FlagsToClear) + { + if( m_ui16Flags) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + if( (m_ui16Flags &= ~ui16FlagsToClear) == 0) + { + if( !m_pPrevInGlobal || + m_ui64HighTransID == ~((FLMUINT64)0) || + neededByReadTrans()) + { + linkToReplaceListAsMRU(); + } + else + { + linkToReplaceListAsLRU(); + } + } +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + } + + // Sets the passed-in flags on the object + // This routine assumes that the block cache mutex is locked. + + FINLINE void setFlags( + FLMUINT16 ui16FlagsToSet) + { + flmAssert( ui16FlagsToSet); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + if( !m_ui16Flags) + { + unlinkFromReplaceList(); + } + + m_ui16Flags |= ui16FlagsToSet; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Set the dirty flag on a cache block. + // This routine assumes that the block cache mutex is locked. + + FINLINE void setDirtyFlag( + F_Database * pDatabase) + { + flmAssert( !(m_ui16Flags & + (CA_DIRTY | CA_WRITE_PENDING | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + setFlags( CA_DIRTY); + pDatabase->incrementDirtyCacheCount(); + } + + // Unset the dirty flag on a cache block. + // This routine assumes that the block cache mutex is locked. + FINLINE void unsetDirtyFlag( void) + { + flmAssert( m_ui16Flags & CA_DIRTY); + flmAssert( m_pDatabase->getDirtyCacheCount()); + + if (m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + unlinkFromLogList(); + } + else if (m_ui16Flags & CA_IN_NEW_LIST) + { + unlinkFromNewList(); + } + + clearFlags( CA_DIRTY); + m_pDatabase->decrementDirtyCacheCount(); + } + + FINLINE FLMUINT getBlkSize( void) + { + return( (FLMUINT)m_ui16BlkSize); + } + +#ifdef FLM_DBG_LOG + void logFlgChange( + FLMUINT16 ui16OldFlags, + char cPlace); +#endif + + void linkToLogList( void); + + void unlinkFromLogList( void); + + void linkToNewList( void); + + void unlinkFromNewList( void); + + void linkToDatabase( + F_Database * pDatabase); + + void unlinkFromDatabase( void); + + void unlinkFromTransLogList( void); + + // Increment the use count on a cache block for a particular + // thread. NOTE: This routine assumes that the block cache mutex + // is locked. + FINLINE void useForThread( + #ifdef FLM_DEBUG + FLMUINT uiThreadId) + #else + FLMUINT) // uiThreadId) + #endif + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + #ifdef FLM_DEBUG + if (m_pUseList || + (gv_XFlmSysData.pBlockCacheMgr->m_bDebug && !m_uiUseCount)) + { + dbgUseForThread( uiThreadId); + } + else + #endif + { + if (!m_uiUseCount) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed++; + } + m_uiUseCount++; + gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses++; + } + } + + // Decrement the use count on a cache block for a particular + // thread. NOTE: This routine assumes that the block cache mutex + // is locked. + FINLINE void releaseForThread( void) + { + if (!m_uiUseCount) + { + return; + } + + #ifdef FLM_DEBUG + if (m_pUseList) + { + dbgReleaseForThread(); + } + else + #endif + { + + #ifdef FLM_DEBUG + + // If count is one, it will be decremented to zero. + + if (m_uiUseCount == 1) + { + m_uiChecksum = computeChecksum(); + } + #endif + + m_uiUseCount--; + gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses--; + if (!m_uiUseCount) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed--; + } + } + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Tests if a block can be freed from cache. + // NOTE: This routine assumes that the block cache mutex is locked. + FINLINE FLMBOOL canBeFreed( void) + { + if (!m_uiUseCount && !m_ui16Flags) + { + F_CachedBlock * pNewerSCache = m_pPrevInVersionList; + + // The following code is attempting to ensure that newer + // versions of the block have had the prior block address + // properly transferred to them from an older version of + // the block. If not, we cannot remove the current version + // of the block (pointed to by pSCache), because it is + // the older version that needs to be logged in order for + // the prior block address to be properly transferred to + // the newer version of the block. + + // If there is no newer version of the block, we can remove + // this block, because it means that there was at one point + // in time a newer version, the prior block address was + // safely transferred - otherwise, the newer version would + // still be in cache. + + // If there is a newer version of the block, but it is in the + // process of being read in from disk (CA_READ_PENDING bit is + // set), we can know that the prior block address has been + // properly transferred to the block being read in. + // Explanation: If the CA_READ_PENDING bit it set, the block + // had to have been written out to disk at some prior time. The + // rules for writing out a block to disk are such that it is + // impossible for a block to be written out without having a + // pointer to some prior version of the block. The only + // exception to this is a newly created block - but in that + // case, the block does not need to have a prior version pointer + // - because there are none! + // This assertion is obvious for a version of a block that is + // being read from the rollback log - it would be impossible + // to be reading such a block from the rollback log if it hadn't + // been part of a version chain! As for the current version of a + // block, it cannot be written out and removed from cache without + // having a pointer to the chain of older versions that may still + // be needed (by a read transactions, for rollback, or to recover + // a checkpoint). + + // NOTE: Although we know that a block being read in from disk + // has to already have a prior block address, we cannot just + // look at the block header, because it is being read in from + // disk and the prior block address is not yet there. Usually, + // it will still be zeroes - making it look as though the block + // does not have a prior block address when, in fact, it does. + // Thus, we look at the CA_READ_PENDING bit first. If that + // is not set, we can safely look at the prior block address. + + // Note also that even if there is a newer block that doesn't + // have a prior block address, we may still be able to remove + // the current block (pSCache) if it is not needed by any + // read transactions. + + if (!pNewerSCache || + (pNewerSCache->m_ui16Flags & CA_READ_PENDING) || + pNewerSCache->getPriorImageAddress() != 0 || + !m_pDatabase || + !neededByReadTrans()) + { + return( TRUE); + } + } + return( FALSE); + } + + void linkToFreeList( + FLMUINT uiFreeTime); + + void unlinkFromFreeList( void); + + void * operator new( + FLMSIZET uiSize, + FLMUINT uiBlockSize, + FLMBOOL bAllocMutexLocked = FALSE) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void operator delete( + void * ptr); + +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + FLMSIZET uiSize, + const char * pszFileName, + int iLine); +#endif + + void operator delete[]( + void * ptr); + +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + FLMUINT uiBlockSize, + FLMBOOL bAllocMutexLocked); +#endif + + +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + const char * file, + int line); +#endif + +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete[]( + void * ptr, + const char * pszFileName, + int iLineNum); +#endif + +private: + + void unlinkFromReplaceList( void); + +#ifdef FLM_DEBUG + void dbgUseForThread( + FLMUINT uiThreadId); + + void dbgReleaseForThread( void); + + FLMUINT computeChecksum( void); +#endif + +#ifdef SCACHE_LINK_CHECKING + void verifyCache( + int iPlace); +#else + FINLINE void verifyCache( + int) + { + } +#endif + + // Link a cached block into the global list as the MRU item. This routine + // assumes that the block cache mutex has already been locked. + + FINLINE void linkToGlobalListAsMRU( void) + { + if( (m_pBlkHdr->ui8BlkType & BT_FREE) || + (m_pBlkHdr->ui8BlkType & BT_LEAF) || + (m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) || + (m_pBlkHdr->ui8BlkType & BT_DATA_ONLY)) + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU( + (F_CachedItem *)this); + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsMRU( + (F_CachedItem *)this); + } + + if( !m_ui16Flags) + { + linkToReplaceListAsMRU(); + } + } + + // Link a cached block into the global list as the LRU item. This routine + // assumes that the block cache mutex has already been locked. + + FINLINE void linkToGlobalListAsLRU( void) + { + if( (m_pBlkHdr->ui8BlkType & BT_FREE) || + (m_pBlkHdr->ui8BlkType & BT_LEAF) || + (m_pBlkHdr->ui8BlkType & BT_LEAF_DATA) || + (m_pBlkHdr->ui8BlkType & BT_DATA_ONLY)) + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLRU( + (F_CachedItem *)this); + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.linkGlobalAsLastMRU( + (F_CachedItem *)this); + } + + if( !m_ui16Flags) + { + linkToReplaceListAsLRU(); + } + } + + // Unlink a cache block from the global list. This routine + // assumes that the block cache mutex has already been locked. + + FINLINE void unlinkFromGlobalList( void) + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.unlinkGlobal( + (F_CachedItem *)this); + if( !m_ui16Flags) + { + unlinkFromReplaceList(); + } + } + + // Moves a block one step closer to the MRU slot in the global list. This + // routine assumes that the block cache mutex has already been locked. + + FINLINE void stepUpInGlobalList( void) + { + gv_XFlmSysData.pBlockCacheMgr->m_MRUList.stepUpInGlobal( + (F_CachedItem *)this); + if( !m_ui16Flags) + { + stepUpInReplaceList(); + } + } + + #ifdef SCACHE_LINK_CHECKING + void checkHashLinks( + F_CachedBlock ** ppSCacheBucket); + + void checkHashUnlinks( + F_CachedBlock ** ppSCacheBucket); + #endif + + // Link a cache block to its hash bucket. This routine assumes + // that the block cache mutex has already been locked. + FINLINE void linkToHashBucket( + F_CachedBlock ** ppSCacheBucket) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + #ifdef SCACHE_LINK_CHECKING + checkHashLinks( ppSCacheBucket); + #endif + m_pPrevInHashBucket = NULL; + if ((m_pNextInHashBucket = *ppSCacheBucket) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->unprotectCachedItem(); +#endif + m_pNextInHashBucket->m_pPrevInHashBucket = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->protectCachedItem(); +#endif + } + *ppSCacheBucket = this; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Unlink a cache block from its hash bucket. This routine assumes + // that the block cache mutex has already been locked. + FINLINE void unlinkFromHashBucket( + F_CachedBlock ** ppSCacheBucket) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + #ifdef SCACHE_LINK_CHECKING + checkHashUnlinks( ppSCacheBucket); + #endif + + // Make sure it is not in the list of log blocks. + + flmAssert( !(m_ui16Flags & CA_WRITE_TO_LOG)); + + if (m_pNextInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->unprotectCachedItem(); +#endif + m_pNextInHashBucket->m_pPrevInHashBucket = m_pPrevInHashBucket; +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->protectCachedItem(); +#endif + } + + if (m_pPrevInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInHashBucket->unprotectCachedItem(); +#endif + m_pPrevInHashBucket->m_pNextInHashBucket = m_pNextInHashBucket; +#ifdef FLM_CACHE_PROTECT + m_pPrevInHashBucket->protectCachedItem(); +#endif + } + else + { + *ppSCacheBucket = m_pNextInHashBucket; + } + + m_pNextInHashBucket = NULL; + m_pPrevInHashBucket = NULL; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + void unlinkCache( + FLMBOOL bFreeIt, + RCODE NotifyRc); + + void savePrevBlkAddress( void); + + F_CachedBlock * m_pPrevInDatabase; // This is a pointer to the previous block + // in the linked list of blocks that are + // in the same database. + F_CachedBlock * m_pNextInDatabase; // This is a pointer to the next block in + // the linked list of blocks that are in + // the same database. + F_BLK_HDR * m_pBlkHdr; // Pointer to this block's header and data. + F_Database * m_pDatabase; // Pointer to the database this data block + // belongs to. + FLMUINT m_uiBlkAddress; // Block address. + F_CachedBlock * m_pNextInReplaceList;// This is a pointer to the next block in + // the global linked list of cache blocks + // that have a flags value of zero. + F_CachedBlock * m_pPrevInReplaceList;// This is a pointer to the previous block in + // the global linked list of cache blocks + // that have a flags value of zero. + F_CachedBlock * m_pPrevInHashBucket; // This is a pointer to the previous block + // in the linked list of blocks that are + // in the same hash bucket. + F_CachedBlock * m_pNextInHashBucket; // This is a pointer to the next block in + // the linked list of blocks that are in + // the same hash bucket. + F_CachedBlock * m_pPrevInVersionList;// This is a pointer to the previous block + // in the linked list of blocks that are + // all just different versions of the + // same block. The previous block is a + // more recent version of the block. + F_CachedBlock * m_pNextInVersionList;// This is a pointer to the next block in + // the linked list of blocks that are all + // just different versions of the same + // block. The next block is an older + // version of the block. + FNOTIFY * m_pNotifyList; // This is a pointer to a list of threads + // that want to be notified when a pending + // I/O is complete. This pointer is only + // non-null if the block is currently being + // read from disk and there are multiple + // threads all waiting for the block to + // be read in. + FLMUINT64 m_ui64HighTransID; // This indicates the highest known moment + // in the file's update history when this + // version of the block was the active + // block. + // A block's low transaction ID and high + // transaction ID indicate a span of + // transactions where this version of the + // block was the active version of the + // block. + FLMUINT m_uiUseCount; // Number of times this block has been + // retrieved for use by threads. A use + // count of zero indicates that no thread + // is currently using the block. Note that + // a block cannot be replaced when its use + // count is non-zero. + FLMUINT16 m_ui16Flags; // This is a set of flags for the block + // that indicate various things about the + // block's current state. + FLMUINT16 m_ui16BlkSize; // Block size + +// NOTE: Keep debug items at the END of the structure. + +#ifdef FLM_DEBUG + FLMUINT m_uiChecksum; // Checksum for the block and header. + SCACHE_USE * m_pUseList; // This is a pointer to a list of threads + // that are currently using the block. +#endif +friend class F_BlockCacheMgr; +friend class F_GlobalCacheMgr; +friend class F_Database; +friend class F_DbSystem; +friend class F_Db; +friend class F_Btree; +friend class F_BTreeInfo; +friend class F_BlockRelocator; +}; + +/**************************************************************************** +Desc: Class for moving nodes in cache. +****************************************************************************/ +class F_NodeRelocator : public IF_Relocator +{ +public: + + F_NodeRelocator() + { + } + + virtual ~F_NodeRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: Class for moving node data buffers in cache. +****************************************************************************/ +class F_NodeDataRelocator : public IF_Relocator +{ +public: + + F_NodeDataRelocator() + { + } + + virtual ~F_NodeDataRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: Class for moving node lists in cache. +****************************************************************************/ +class F_NodeListRelocator : public IF_Relocator +{ +public: + + F_NodeListRelocator() + { + } + + virtual ~F_NodeListRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: Class for moving attr lists in cache. +****************************************************************************/ +class F_AttrListRelocator : public IF_Relocator +{ +public: + + F_AttrListRelocator() + { + } + + virtual ~F_AttrListRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: Class for moving attr items in cache. +****************************************************************************/ +class F_AttrItemRelocator : public IF_Relocator +{ +public: + + F_AttrItemRelocator() + { + } + + virtual ~F_AttrItemRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Desc: Class for moving attr buffers in cache. +****************************************************************************/ +class F_AttrBufferRelocator : public IF_Relocator +{ +public: + + F_AttrBufferRelocator() + { + } + + virtual ~F_AttrBufferRelocator() + { + } + + void relocate( + void * pvOldAlloc, + void * pvNewAlloc); + + FLMBOOL canRelocate( + void * pvOldAlloc); +}; + +/**************************************************************************** +Struct: NODE_CACHE_MGR (XFLAIM Node Cache Manager) +Desc: This structure defines the header information that is used to + control the FLAIM record cache. This structure will be embedded + in the FLMSYSDATA structure. +****************************************************************************/ +class F_NodeCacheMgr : public XF_RefCount, public XF_Base +{ +public: + F_NodeCacheMgr(); + + ~F_NodeCacheMgr(); + + void insertDOMNode( + F_DOMNode * pNode); + + RCODE initCache( void); + + void cleanupOldCache( void); + + void cleanupPurgedCache( void); + + void reduceCache( void); + + FINLINE void setDebugMode( + FLMBOOL bDebug) + { +#ifdef FLM_DEBUG + m_bDebug = bDebug; +#else + (void)bDebug; +#endif + } + + RCODE allocNode( + F_CachedNode ** ppNode, + FLMBOOL bMutexLocked); + + RCODE allocDOMNode( + F_DOMNode ** ppDOMNode); + + RCODE retrieveNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppDOMNode); + + RCODE createNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppDOMNode); + + RCODE makeWriteCopy( + F_Db * pDb, + F_CachedNode ** ppCachedNode); + + void removeNode( + F_Db * pDb, + F_CachedNode * pNode, + FLMBOOL bDecrementUseCount, + FLMBOOL bMutexLocked = FALSE); + + void removeNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId); + + FINLINE void defragmentMemory( + FLMBOOL bMutexLocked = FALSE) + { + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + + m_nodeAllocator.defragmentMemory(); + m_bufAllocator.defragmentMemory(); + m_attrItemAllocator.defragmentMemory(); + + if( !bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + } + + FINLINE void getUsageStats( + XFLM_SLAB_USAGE * pUsage) + { + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->lockMutex(); + f_memcpy( pUsage, &m_Usage, sizeof( XFLM_SLAB_USAGE)); + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->unlockMutex(); + } + +private: + + // Hash function for hashing to nodes in node cache. + // Assumes that the node cache mutex has already been locked. + FINLINE F_CachedNode ** nodeHash( + FLMUINT64 ui64NodeId) + { + return( &m_ppHashBuckets[(FLMUINT)ui64NodeId & m_uiHashMask]); + } + + RCODE rehash( void); + + RCODE waitNotify( + F_Db * pDb, + F_CachedNode ** ppNode); + + void notifyWaiters( + FNOTIFY * pNotify, + F_CachedNode * pUseNode, + RCODE NotifyRc); + + void linkIntoNodeCache( + F_CachedNode * pNewerNode, + F_CachedNode * pOlderNode, + F_CachedNode * pNode, + FLMBOOL bLinkAsMRU); + + void findNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64VersionNeeded, + FLMBOOL bDontPoisonCache, + FLMUINT * puiNumLooks, + F_CachedNode ** ppNode, + F_CachedNode ** ppNewerNode, + F_CachedNode ** ppOlderNode); + + RCODE readNodeFromDisk( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_CachedNode * pNode, + FLMUINT64 * pui64LowTransId, + FLMBOOL * pbMostCurrent); + + RCODE _makeWriteCopy( + F_Db * pDb, + F_CachedNode ** ppCachedNode); + + // Private Data + + F_CacheList m_MRUList; // List of all node objects in MRU order. + F_CachedNode * m_pPurgeList; // List of F_CachedNode objects that + // should be deleted when the use count + // goes to zero. + F_CachedNode * m_pHeapList; // List of nodes with heap allocations + F_CachedNode * m_pOldList; // List of old versions + XFLM_CACHE_USAGE m_Usage; // Contains maximum, bytes used, etc. + F_CachedNode ** m_ppHashBuckets; // Array of hash buckets. + FLMUINT m_uiNumBuckets; // Total number of hash buckets. + // must be an exponent of 2. + FLMUINT m_uiHashFailTime; + // Last time we tried to rehash and + // failed. Want to wait before we + // retry again. + FLMUINT m_uiHashMask; // Hash mask mask for hashing a + // node id to a hash bucket. + FLMUINT m_uiPendingReads; // Total reads currently pending. + FLMUINT m_uiIoWaits; // Number of times multiple threads + // were reading the same node from + // disk at the same time. + F_FixedAlloc m_nodeAllocator; // Fixed size allocator for F_CachedNode + // objects + F_BufferAlloc m_bufAllocator; // Buffer allocator for buffers in + // F_CachedNode objects + F_FixedAlloc m_attrItemAllocator; + // Allocator for attribute list items + F_NodeRelocator m_nodeRelocator; + F_NodeDataRelocator m_nodeDataRelocator; + F_NodeListRelocator m_nodeListRelocator; + F_AttrListRelocator m_attrListRelocator; + F_AttrItemRelocator m_attrItemRelocator; + F_AttrBufferRelocator m_attrBufferRelocator; + + F_DOMNode * m_pFirstNode; // List of DOM Nodes in a pool + FLMBOOL m_bReduceInProgress; + +#ifdef FLM_DEBUG + FLMBOOL m_bDebug; // Debug mode? +#endif +friend class F_CachedNode; +friend class F_GlobalCacheMgr; +friend class F_Database; +friend class F_DbSystem; +friend class F_DOMNode; +friend class F_AttrItem; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +}; + +// Bits for m_uiCacheFlags field in F_CachedNode + +#define NCA_READING_IN 0x80000000 +#define NCA_UNCOMMITTED 0x40000000 +#define NCA_LATEST_VER 0x20000000 +#define NCA_PURGED 0x10000000 +#define NCA_LINKED_TO_DATABASE 0x08000000 + +#define NCA_COUNTER_BITS (~(NCA_READING_IN | NCA_UNCOMMITTED | \ + NCA_LATEST_VER | NCA_PURGED | \ + NCA_LINKED_TO_DATABASE)) + +// In-memory node flags. +// +// Flags that are not actually written to disk - only held in memory +// Reserve upper byte for these. These flags need to be here so that +// we don't have to worry about locking the mutex to access them. + +#define FDOM_READ_ONLY 0x00000001 +#define FDOM_CANNOT_DELETE 0x00000002 +#define FDOM_QUARANTINED 0x00000004 +#define FDOM_VALUE_ON_DISK 0x00000008 +#define FDOM_SIGNED_QUICK_VAL 0x00000010 +#define FDOM_UNSIGNED_QUICK_VAL 0x00000020 +#define FDOM_DIRTY 0x00000040 +#define FDOM_NEW 0x00000080 +#define FDOM_HEAP_ALLOC 0x00000100 +#define FDOM_HAVE_CELM_LIST 0x00000200 +#define FDOM_NAMESPACE_DECL 0x00000400 +#define FDOM_FIXED_SIZE_HEADER 0x00000800 + +#define FDOM_PERSISTENT_FLAGS (FDOM_READ_ONLY | \ + FDOM_CANNOT_DELETE | \ + FDOM_QUARANTINED | \ + FDOM_NAMESPACE_DECL) +/***************************************************************************** +Desc: Node storage flags +******************************************************************************/ +#define NSF_HAVE_BASE_ID_BIT 0x0001 +#define NSF_HAVE_META_VALUE_BIT 0x0002 +#define NSF_HAVE_SIBLINGS_BIT 0x0004 +#define NSF_HAVE_CHILDREN_BIT 0x0008 +#define NSF_HAVE_ATTR_LIST_BIT 0x0010 +#define NSF_HAVE_CELM_LIST_BIT 0x0020 +#define NSF_HAVE_DATA_LEN_BIT 0x0040 + +#define NSF_EXT_HAVE_DCHILD_COUNT_BIT 0x0080 +#define NSF_EXT_READ_ONLY_BIT 0x0100 +#define NSF_EXT_CANNOT_DELETE_BIT 0x0200 +#define NSF_EXT_HAVE_PREFIX_BIT 0x0400 +#define NSF_EXT_ENCRYPTED_BIT 0x0800 +#define NSF_EXT_NAMESPACE_DECL_BIT 0x1000 +#define NSF_EXT_ANNOTATION_BIT 0x2000 +#define NSF_EXT_QUARANTINED_BIT 0x4000 + +/***************************************************************************** +Desc: Attribute storage flags and masks +******************************************************************************/ +#define ASF_PAYLOAD_LEN_MASK 0x0000000F +#define ASF_MAX_EMBEDDED_PAYLOAD_LEN 0x0000000E +#define ASF_HAVE_PAYLOAD_LEN_SEN 0x0000000F + +#define ASF_HAVE_PREFIX_BIT 0x00000010 +#define ASF_READ_ONLY_BIT 0x00000020 +#define ASF_CANNOT_DELETE_BIT 0x00000040 +#define ASF_ENCRYPTED_BIT 0x00000080 + +/***************************************************************************** +Desc: +******************************************************************************/ +typedef struct NODE_ITEM +{ + FLMUINT uiNameId; + FLMUINT64 ui64NodeId; +} NODE_ITEM; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_AttrItem +{ +public: + + F_AttrItem() + { + m_pCachedNode = NULL; + m_pucPayload = NULL; + m_uiPayloadLen = 0; + m_uiDataType = 0; + m_uiNameId = 0; + m_uiFlags = 0; + m_uiPrefixId = 0; + m_ui64QuickVal = 0; + m_uiEncDefId = 0; + m_uiIVLen = 0; + m_uiDecryptedDataLen = 0; + } + + ~F_AttrItem(); + + RCODE resizePayloadBuffer( + FLMUINT uiTotalNeeded, + FLMBOOL bMutexAlreadyLocked); + + RCODE setupAttribute( + F_Db * pDb, + FLMUINT uiEncDefId, + FLMUINT uiSizeNeeded, + FLMBOOL bOkToGenerateIV, + FLMBOOL bMutexAlreadyLocked); + + void getAttrSizeNeeded( + FLMUINT uiBaseNameId, + XFLM_NODE_INFO * pNodeInfo, + FLMUINT * puiSaveStorageFlags, + FLMUINT * puiSizeNeeded); + + FINLINE void copyFrom( + F_AttrItem * pSrcAttrItem) + { + m_pCachedNode = pSrcAttrItem->m_pCachedNode; + m_pucPayload = pSrcAttrItem->m_pucPayload; + m_uiPayloadLen = pSrcAttrItem->m_uiPayloadLen; + m_uiDataType = pSrcAttrItem->m_uiDataType; + m_uiNameId = pSrcAttrItem->m_uiNameId; + m_uiFlags = pSrcAttrItem->m_uiFlags; + m_uiPrefixId = pSrcAttrItem->m_uiPrefixId; + m_ui64QuickVal = pSrcAttrItem->m_ui64QuickVal; + m_uiEncDefId = pSrcAttrItem->m_uiEncDefId; + m_uiIVLen = pSrcAttrItem->m_uiIVLen; + m_uiDecryptedDataLen = pSrcAttrItem->m_uiDecryptedDataLen; + if( m_uiPayloadLen > sizeof( FLMBYTE *)) + { + m_pucPayload = NULL; + m_uiPayloadLen = 0; + m_uiEncDefId = 0; + } + } + + FINLINE FLMBYTE * getAttrDataPtr( void) + { + if( m_uiPayloadLen <= sizeof( FLMBYTE *)) + { + return( (FLMBYTE *)&m_pucPayload); + } + + return( m_pucPayload + m_uiIVLen); + } + + FINLINE FLMUINT getAttrDataLength( void) + { + if( m_uiEncDefId) + { + return( m_uiDecryptedDataLen); + } + + return( m_uiPayloadLen - m_uiIVLen); + } + + FINLINE FLMUINT getAttrDataBufferSize( void) + { + return( m_uiPayloadLen - m_uiIVLen); + } + + FINLINE FLMBYTE * getAttrIVPtr( void) + { + if( m_uiPayloadLen <= sizeof( FLMBYTE *)) + { + flmAssert( !m_uiIVLen); + return( NULL); + } + + return( m_pucPayload); + } + + FINLINE FLMBYTE * getAttrPayloadPtr( void) + { + if( m_uiPayloadLen <= sizeof( FLMBYTE *)) + { + return( (FLMBYTE *)&m_pucPayload); + } + + return( m_pucPayload); + } + + FINLINE FLMUINT getAttrPayloadSize( void) + { + return( m_uiPayloadLen); + } + + FINLINE FLMUINT getAttrModeFlags( void) + { + return( m_uiFlags & FDOM_PERSISTENT_FLAGS); + } + + FINLINE FLMUINT getAttrEncDefId( void) + { + return( m_uiEncDefId); + } + + FINLINE FLMUINT memSize( void) + { + FLMUINT uiSize = gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.getCellSize(); + + if( m_uiPayloadLen > sizeof( FLMBYTE *)) + { + uiSize += gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.getTrueSize( + m_uiPayloadLen + sizeof( F_AttrItem *), + m_pucPayload - sizeof( F_AttrItem *)); + } + return( uiSize); + } + + FINLINE FLMUINT getAttrStorageFlags( void) + { + FLMUINT uiFlags = 0; + + if( m_uiPayloadLen <= ASF_MAX_EMBEDDED_PAYLOAD_LEN) + { + uiFlags |= (FLMBYTE)m_uiPayloadLen; + } + else + { + uiFlags |= ASF_HAVE_PAYLOAD_LEN_SEN; + } + + if( m_uiPayloadLen) + { + if( m_uiEncDefId) + { + uiFlags |= ASF_ENCRYPTED_BIT; + } + } + + if( m_uiPrefixId) + { + uiFlags |= ASF_HAVE_PREFIX_BIT; + } + + if( m_uiFlags & FDOM_READ_ONLY) + { + uiFlags |= ASF_READ_ONLY_BIT; + } + + if( m_uiFlags & FDOM_CANNOT_DELETE) + { + uiFlags |= ASF_CANNOT_DELETE_BIT; + } + + return( uiFlags); + } + + void * operator new( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void operator delete( + void * ptr); + + void operator delete[]( + void * ptr); + +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + const char * file, + int line); +#endif + +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete[]( + void * ptr, + const char * pszFileName, + int iLineNum); +#endif + +private: + + F_CachedNode * m_pCachedNode; + FLMBYTE * m_pucPayload; + FLMUINT m_uiPayloadLen; + FLMUINT m_uiDataType; + FLMUINT m_uiNameId; + FLMUINT m_uiFlags; + FLMUINT m_uiPrefixId; + FLMUINT64 m_ui64QuickVal; + FLMUINT m_uiEncDefId; + FLMUINT m_uiIVLen; + FLMUINT m_uiDecryptedDataLen; + +friend class F_DOMNode; +friend class F_Rfl; +friend class F_Db; +friend class F_DbRebuild; +friend class F_CachedNode; +friend class F_NodeCacheMgr; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +friend class F_NodeInfo; +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE FLMUINT allocOverhead( void) +{ + // Round sizeof( F_CachedNode *) + 1 to nearest 8 byte boundary. + + return( (sizeof( F_CachedNode *) + 9) & (~((FLMUINT)7))); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE FLMBYTE * getActualPointer( + void * pvPtr) +{ + if (pvPtr) + { + return( (FLMBYTE *)pvPtr - allocOverhead()); + } + else + { + return( NULL); + } +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE FLMUINT calcNodeListBufSize( + FLMUINT uiNodeCount) +{ + return( uiNodeCount * sizeof( NODE_ITEM) + allocOverhead()); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE FLMUINT calcAttrListBufSize( + FLMUINT uiAttrCount) +{ + return( uiAttrCount * sizeof( F_AttrItem *) + allocOverhead()); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE FLMUINT calcDataBufSize( + FLMUINT uiDataSize) +{ + // Leave room for in buffer for a pointer back to the F_CachedNode. + // We reserve enough so that the actual allocation will start on + // the next eight byte boundary. + + return( uiDataSize + allocOverhead()); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +typedef struct +{ + FLMUINT64 ui64NodeId; + FLMUINT64 ui64DocumentId; + FLMUINT64 ui64ParentId; + FLMUINT64 ui64MetaValue; + FLMUINT64 ui64FirstChildId; + FLMUINT64 ui64LastChildId; + FLMUINT64 ui64PrevSibId; + FLMUINT64 ui64NextSibId; + FLMUINT64 ui64AnnotationId; + + eDomNodeType eNodeType; + FLMUINT uiCollection; + FLMUINT uiChildElmCount; + FLMUINT uiDataLength; + FLMUINT uiDataType; + FLMUINT uiPrefixId; + FLMUINT uiNameId; + FLMUINT uiDataChildCount; + FLMUINT uiEncDefId; +} F_NODE_INFO; + +/***************************************************************************** +Desc: Cached DOM NODE +******************************************************************************/ +class F_CachedNode : public F_CachedItem +{ +public: + + F_CachedNode(); + + ~F_CachedNode(); + +#ifdef FLM_CACHE_PROTECT + FINLINE void protectCachedItem( void) + { + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.protectCell( this); + } + + FINLINE void unprotectCachedItem( void) + { + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.unprotectCell( this); + } +#endif + + // This method assumes that the node cache mutex has been locked. + + FINLINE FLMBOOL canBeFreed( void) + { + return( (!nodeInUse() && !readingInNode() && !nodeIsDirty()) ? TRUE : FALSE); + } + + // This method assumes that the node cache mutex has been locked. + + FINLINE void freeNode( void) + { + if (nodePurged()) + { + freePurged(); + } + else + { + freeCache( FALSE); + } + } + + FINLINE FLMUINT64 getLowTransId( void) + { + return( m_ui64LowTransId); + } + + FINLINE FLMUINT64 getHighTransId( void) + { + return( m_ui64HighTransId); + } + + RCODE getIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL); + + RCODE headerToBuf( + FLMBOOL bFixedSizeHeader, + FLMBYTE * pucBuf, + FLMUINT * puiHeaderSize, + XFLM_NODE_INFO * pNodeInfo, + F_Db * pDb); + + RCODE readNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_IStream * pIStream, + FLMUINT uiOverallLength, + FLMBYTE * pucIV); + + RCODE getRawIStream( + F_Db * pDb, + IF_PosIStream ** ppIStream); + + RCODE openPendingInput( + F_Db * pDb, + FLMUINT uiNewDataType); + + RCODE flushPendingInput( + F_Db * pDb, + FLMBOOL bLast); + + RCODE resizeChildElmList( + FLMUINT uiChildElmCount, + FLMBOOL bMutexAlreadyLocked); + + RCODE resizeAttrList( + FLMUINT uiAttrCount, + FLMBOOL bMutexAlreadyLocked); + + RCODE resizeDataBuffer( + FLMUINT uiSize, + FLMBOOL bMutexAlreadyLocked); + + FINLINE void setNodeAndDataPtr( + FLMBYTE * pucActualAlloc) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + *((F_CachedNode **)(pucActualAlloc)) = this; + m_pucData = pucActualAlloc + allocOverhead(); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setNodeListPtr( + FLMBYTE * pucActualAlloc) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + *((F_CachedNode **)(pucActualAlloc)) = this; + m_pNodeList = (NODE_ITEM *)(pucActualAlloc + allocOverhead()); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setAttrListPtr( + FLMBYTE * pucActualAlloc) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + *((F_CachedNode **)(pucActualAlloc)) = this; + m_ppAttrList = (F_AttrItem **)(pucActualAlloc + allocOverhead()); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMBYTE * getDataPtr( void) + { + return( m_pucData); + } + + FINLINE FLMUINT getDataBufSize( void) + { + return( m_uiDataBufSize); + } + + FINLINE FLMUINT getModeFlags( void) + { + return( m_uiFlags); + } + + FINLINE FLMUINT getPersistentFlags( void) + { + return( m_uiFlags & FDOM_PERSISTENT_FLAGS); + } + + FINLINE void setFlags( + FLMUINT uiFlags) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiFlags |= uiFlags; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void unsetFlags( + FLMUINT uiFlags) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiFlags &= ~uiFlags; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getNodeId( void) + { + return( m_nodeInfo.ui64NodeId); + } + + FINLINE F_Database * getDatabase( void) + { + return( m_pDatabase); + } + + FINLINE FLMUINT getCollection( void) + { + return( m_nodeInfo.uiCollection); + } + + FINLINE eDomNodeType getNodeType( void) + { + return( m_nodeInfo.eNodeType); + } + + FINLINE void setNodeType( + eDomNodeType eNodeType) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.eNodeType = eNodeType; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getDataType( void) + { + return( m_nodeInfo.uiDataType); + } + + FINLINE void setDataType( + FLMUINT uiDataType) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.uiDataType = uiDataType; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getNameId( void) + { + return( m_nodeInfo.uiNameId); + } + + FINLINE void setNameId( + FLMUINT uiNameId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.uiNameId = uiNameId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getPrefixId( void) + { + return( m_nodeInfo.uiPrefixId); + } + + FINLINE void setPrefixId( + FLMUINT uiPrefixId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.uiPrefixId = uiPrefixId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getDocumentId( void) + { + return( m_nodeInfo.ui64DocumentId); + } + + FINLINE FLMBOOL isRootNode( void) + { + return( m_nodeInfo.ui64NodeId == m_nodeInfo.ui64DocumentId + ? TRUE + : FALSE); + } + + FINLINE void setDocumentId( + FLMUINT64 ui64DocumentId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64DocumentId = ui64DocumentId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getParentId( void) + { + return( m_nodeInfo.ui64ParentId); + } + + FINLINE void setParentId( + FLMUINT64 ui64ParentId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64ParentId = ui64ParentId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getNextSibId( void) + { + return( m_nodeInfo.ui64NextSibId); + } + + FINLINE void setNextSibId( + FLMUINT64 ui64NextSibId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64NextSibId = ui64NextSibId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getPrevSibId( void) + { + return( m_nodeInfo.ui64PrevSibId); + } + + FINLINE void setPrevSibId( + FLMUINT64 ui64PrevSibId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64PrevSibId = ui64PrevSibId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setSibIds( + FLMUINT64 ui64PrevSibId, + FLMUINT64 ui64NextSibId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64NextSibId = ui64NextSibId; + m_nodeInfo.ui64PrevSibId = ui64PrevSibId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getFirstChildId( void) + { + return( m_nodeInfo.ui64FirstChildId); + } + + FINLINE void setFirstChildId( + FLMUINT64 ui64FirstChildId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64FirstChildId = ui64FirstChildId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getLastChildId( void) + { + return( m_nodeInfo.ui64LastChildId); + } + + FINLINE void setLastChildId( + FLMUINT64 ui64LastChildId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64LastChildId = ui64LastChildId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setChildIds( + FLMUINT64 ui64FirstChildId, + FLMUINT64 ui64LastChildId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64FirstChildId = ui64FirstChildId; + m_nodeInfo.ui64LastChildId = ui64LastChildId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getDataChildCount( void) + { + return( m_nodeInfo.uiDataChildCount); + } + + FINLINE void setDataChildCount( + FLMUINT uiDataChildCount) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + flmAssert( m_nodeInfo.ui64FirstChildId); + m_nodeInfo.uiDataChildCount = uiDataChildCount; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getAnnotationId( void) + { + return( m_nodeInfo.ui64AnnotationId); + } + + FINLINE void setAnnotationId( + FLMUINT64 ui64AnnotationId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64AnnotationId = ui64AnnotationId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMBOOL hasAttributes( void) + { + return( m_uiAttrCount ? TRUE : FALSE); + } + + FINLINE FLMUINT getChildElmCount( void) + { + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + return( m_nodeInfo.uiChildElmCount); + } + + FINLINE FLMUINT getChildElmNameId( + FLMUINT uiChildElmOffset) + { + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + return( m_pNodeList [ uiChildElmOffset].uiNameId); + } + + FINLINE FLMUINT64 getChildElmNodeId( + FLMUINT uiChildElmOffset) + { + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + return( m_pNodeList [ uiChildElmOffset].ui64NodeId); + } + + FLMBOOL findChildElm( + FLMUINT uiChildElmNameId, + FLMUINT * puiInsertPos); + + FINLINE RCODE removeChildElm( + FLMUINT uiChildElmOffset) + { + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + if (uiChildElmOffset < m_nodeInfo.uiChildElmCount - 1) + { + f_memmove( &m_pNodeList [ uiChildElmOffset], + &m_pNodeList [ uiChildElmOffset + 1], + sizeof( NODE_ITEM) * (m_nodeInfo.uiChildElmCount - + uiChildElmOffset - 1)); + } + return( resizeChildElmList( m_nodeInfo.uiChildElmCount - 1, FALSE)); + } + + RCODE insertChildElm( + FLMUINT uiChildElmOffset, + FLMUINT uiChildElmNameId, + FLMUINT64 ui64ChildElmNodeId); + + FINLINE FLMUINT getDataLength( void) + { + return( m_nodeInfo.uiDataLength); + } + + FINLINE void setDataLength( + FLMUINT uiDataLength) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.uiDataLength = uiDataLength; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getEncDefId( void) + { + return( m_nodeInfo.uiEncDefId); + } + + FINLINE void setEncDefId( + FLMUINT uiEncDefId) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.uiEncDefId = uiEncDefId; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMBOOL getQuickNumber64( + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg) + { + if (m_uiFlags & FDOM_UNSIGNED_QUICK_VAL) + { + *pui64Num = m_numberVal.ui64Val; + *pbNeg = FALSE; + return( TRUE); + } + + if (m_uiFlags & FDOM_SIGNED_QUICK_VAL) + { + if( m_numberVal.i64Val < 0) + { + *pui64Num = (FLMUINT64)(-m_numberVal.i64Val); + *pbNeg = TRUE; + } + else + { + *pui64Num = (FLMUINT64)m_numberVal.i64Val; + *pbNeg = FALSE; + } + + return( TRUE); + } + + *pui64Num = 0; + *pbNeg = FALSE; + + return( FALSE); + } + + FINLINE FLMINT64 getQuickINT64( void) + { + flmAssert( m_uiFlags & FDOM_SIGNED_QUICK_VAL); + return( m_numberVal.i64Val); + } + + FINLINE FLMUINT64 getQuickUINT64( void) + { + flmAssert( m_uiFlags & FDOM_UNSIGNED_QUICK_VAL); + return( m_numberVal.ui64Val); + } + + FINLINE void setUINT64( + FLMUINT64 ui64Value) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_numberVal.ui64Val = ui64Value; + m_uiFlags &= ~FDOM_SIGNED_QUICK_VAL; + m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setINT64( + FLMINT64 i64Value) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_numberVal.i64Val = i64Value; + m_uiFlags &= ~FDOM_UNSIGNED_QUICK_VAL; + m_uiFlags |= FDOM_SIGNED_QUICK_VAL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE void setMetaValue( + FLMUINT64 ui64Value) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_nodeInfo.ui64MetaValue = ui64Value; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT64 getMetaValue( void) + { + return( m_nodeInfo.ui64MetaValue); + } + + FINLINE FLMUINT getOffsetIndex( void) + { + return( m_uiOffsetIndex); + } + + FINLINE void setOffsetIndex( + FLMUINT uiOffsetIndex) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiOffsetIndex = uiOffsetIndex; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT32 getBlkAddr( void) + { + return( m_ui32BlkAddr); + } + + FINLINE void setBlkAddr( + FLMUINT32 ui32BlkAddr) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_ui32BlkAddr = ui32BlkAddr; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMBOOL isRightVersion( + FLMUINT64 ui64TransId) + { + return( (ui64TransId >= m_ui64LowTransId && + ui64TransId <= m_ui64HighTransId) + ? TRUE + : FALSE); + } + + // Generally, assumes that the node cache mutex has already been locked. + // There is one case where it is not locked, but it is not + // critical that it be locked - inside syncFromDb. + + FINLINE FLMBOOL nodePurged( void) + { + return( (m_uiCacheFlags & NCA_PURGED ) ? TRUE : FALSE); + } + +#ifdef FLM_DEBUG + void checkReadFromDisk( + F_Db * pDb); +#endif + + void * operator new( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void * operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) + #if !defined( FLM_NLM) + throw() + #endif + ; + + void operator delete( + void * ptr); + + void operator delete[]( + void * ptr); + +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete( + void * ptr, + const char * file, + int line); +#endif + +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) + void operator delete[]( + void * ptr, + const char * pszFileName, + int iLineNum); +#endif + + // Assumes that the node cache mutex has already been locked. + FINLINE void incrNodeUseCount( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) | + (((m_uiCacheFlags & NCA_COUNTER_BITS) + 1)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void decrNodeUseCount( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags = (m_uiCacheFlags & (~(NCA_COUNTER_BITS))) | + (((m_uiCacheFlags & NCA_COUNTER_BITS) - 1)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void incrStreamUseCount( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiStreamUseCount++; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void decrStreamUseCount( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + flmAssert( m_uiStreamUseCount); + m_uiStreamUseCount--; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT getStreamUseCount( void) + { + return( m_uiStreamUseCount); + } + + void setNodeDirty( + F_Db * pDb, + FLMBOOL bNew); + + void unsetNodeDirtyAndNew( + F_Db * pDb, + FLMBOOL bMutexAlreadyLocked = FALSE); + + FINLINE FLMBOOL nodeIsDirty( void) + { + return( (m_uiFlags & FDOM_DIRTY) ? TRUE : FALSE); + } + + FINLINE FLMBOOL nodeIsNew( void) + { + return( (m_uiFlags & FDOM_NEW) ? TRUE : FALSE); + } + + // Assumes that the node cache mutex has already been locked. + FINLINE FLMBOOL nodeUncommitted( void) + { + return( (m_uiCacheFlags & NCA_UNCOMMITTED ) ? TRUE : FALSE); + } + + void freeCache( + FLMBOOL bPutInPurgeList); + + // Attribute functions + + RCODE setNumber64( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 ui64Value, + FLMBOOL bNeg, + FLMUINT uiEncDefId); + + RCODE getNumber64( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Value, + FLMBOOL * pbNeg); + + RCODE setUTF8( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiNumBytesInValue, + FLMUINT uiNumCharsInValue, + FLMUINT uiEncDefId); + + RCODE setBinary( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiNumBytesInBuffer, + FLMUINT uiEncDefId); + + RCODE getBinary( + F_Db * pDb, + FLMUINT uiAttrNameId, + void * pvBuffer, + FLMUINT uiBufferLen, + FLMUINT * puiDataLen); + + RCODE setStorageValue( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiValueLen, + FLMUINT uiEncDefId); + + F_AttrItem * getAttribute( + FLMUINT uiAttrNameId, + FLMUINT * puiInsertPos); + + FINLINE F_AttrItem * getFirstAttribute( void) + { + return( m_uiAttrCount ? m_ppAttrList [0] : NULL); + } + + FINLINE F_AttrItem * getLastAttribute( void) + { + return( m_uiAttrCount ? m_ppAttrList [m_uiAttrCount - 1] : NULL); + } + + RCODE getPrevSiblingNode( + FLMUINT uiCurrentNameId, + IF_DOMNode ** ppSib); + + RCODE getNextSiblingNode( + FLMUINT uiCurrentNameId, + IF_DOMNode ** ppSib); + + RCODE allocAttribute( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_AttrItem * pSrcAttrItem, + FLMUINT uiInsertPos, + F_AttrItem ** ppAttrItem, + FLMBOOL bMutexAlreadyLocked); + + RCODE freeAttribute( + F_AttrItem * pAttrItem, + FLMUINT uiPos); + + RCODE createAttribute( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_AttrItem ** ppAttrItem); + + RCODE removeAttribute( + F_Db * pDb, + FLMUINT uiAttrNameId); + + RCODE exportAttributeList( + F_Db * pDb, + F_DynaBuf * pDynaBuf, + XFLM_NODE_INFO * pNodeInfo); + + RCODE addModeFlags( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiFlags); + + RCODE removeModeFlags( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiFlags); + + RCODE setPrefixId( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiPrefixId); + + FINLINE F_AttrItem * getPrevSibling( + FLMUINT uiAttrNameId) + { + F_AttrItem * pAttrItem; + FLMUINT uiPos; + + if ((pAttrItem = getAttribute( uiAttrNameId, &uiPos)) != NULL) + { + pAttrItem = uiPos ? m_ppAttrList [uiPos - 1] : NULL; + } + + return( pAttrItem); + } + + FINLINE F_AttrItem * getNextSibling( + FLMUINT uiAttrNameId) + { + F_AttrItem * pAttrItem; + FLMUINT uiPos; + + if( (pAttrItem = getAttribute( uiAttrNameId, &uiPos)) != NULL) + { + pAttrItem = uiPos < m_uiAttrCount - 1 ? m_ppAttrList [uiPos + 1] : NULL; + } + + return( pAttrItem); + } + + FINLINE FLMUINT getModeFlags( + FLMUINT uiAttrNameId) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + return( 0); + } + + return( pAttrItem->getAttrModeFlags()); + } + + FINLINE RCODE getPrefixId( + FLMUINT uiAttrNameId, + FLMUINT * puiPrefixId) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + + *puiPrefixId = pAttrItem->m_uiPrefixId; + return( NE_XFLM_OK); + } + + FINLINE RCODE getDataLength( + FLMUINT uiAttrNameId, + FLMUINT * puiDataLength) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + + *puiDataLength = pAttrItem->getAttrDataLength(); + return( NE_XFLM_OK); + } + + FINLINE RCODE getDataType( + FLMUINT uiAttrNameId, + FLMUINT * puiDataType) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + + *puiDataType = pAttrItem->m_uiDataType; + return( NE_XFLM_OK); + } + + FINLINE RCODE getEncDefId( + FLMUINT uiAttrNameId, + FLMUINT * puiEncDefId) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + return( RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + + *puiEncDefId = pAttrItem->m_uiEncDefId; + return( NE_XFLM_OK); + } + + RCODE getIStream( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL); + +private: + + // Assumes that the node cache mutex has already been locked. + FINLINE FLMBOOL readingInNode( void) + { + return( (m_uiCacheFlags & NCA_READING_IN ) ? TRUE : FALSE); + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void setReadingIn( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags |= NCA_READING_IN; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unsetReadingIn( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags &= (~(NCA_READING_IN)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void setUncommitted( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags |= NCA_UNCOMMITTED; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unsetUncommitted( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags &= (~(NCA_UNCOMMITTED)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE FLMBOOL nodeIsLatestVer( void) + { + return( (m_uiCacheFlags & NCA_LATEST_VER ) ? TRUE : FALSE); + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void setLatestVer( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags |= NCA_LATEST_VER; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unsetLatestVer( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags &= (~(NCA_LATEST_VER)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void setPurged( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags |= NCA_PURGED; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unsetPurged( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags &= (~(NCA_PURGED)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE FLMBOOL nodeLinkedToDatabase( void) + { + return( (m_uiCacheFlags & NCA_LINKED_TO_DATABASE ) ? TRUE : FALSE); + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void setLinkedToDatabase( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags |= NCA_LINKED_TO_DATABASE; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unsetLinkedToDatabase( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiCacheFlags &= (~(NCA_LINKED_TO_DATABASE)); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE FLMBOOL nodeInUse( void) + { + return( (m_uiCacheFlags & NCA_COUNTER_BITS ) ? TRUE : FALSE); + } + + // Unlink a node from the global purged list. + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromPurged( void) + { + if (m_pNextInGlobal) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInGlobal->unprotectCachedItem(); +#endif + m_pNextInGlobal->m_pPrevInGlobal = m_pPrevInGlobal; +#ifdef FLM_CACHE_PROTECT + m_pNextInGlobal->protectCachedItem(); +#endif + } + + if (m_pPrevInGlobal) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInGlobal->unprotectCachedItem(); +#endif + m_pPrevInGlobal->m_pNextInGlobal = m_pNextInGlobal; +#ifdef FLM_CACHE_PROTECT + m_pPrevInGlobal->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList = + (F_CachedNode *)m_pNextInGlobal; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pPrevInGlobal = NULL; + m_pNextInGlobal = NULL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Link a node to an F_Database list at the head of the list. + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToDatabaseAtHead( + F_Database * pDatabase) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (!pDatabase->m_pLastDirtyNode || nodeIsDirty()) + { + m_pPrevInDatabase = NULL; + if ((m_pNextInDatabase = pDatabase->m_pFirstNode) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pDatabase->m_pFirstNode->unprotectCachedItem(); +#endif + pDatabase->m_pFirstNode->m_pPrevInDatabase = this; +#ifdef FLM_CACHE_PROTECT + pDatabase->m_pFirstNode->protectCachedItem(); +#endif + } + else + { + pDatabase->m_pLastNode = this; + } + + pDatabase->m_pFirstNode = this; + if (nodeIsDirty() && !pDatabase->m_pLastDirtyNode) + { + pDatabase->m_pLastDirtyNode = this; + } + } + else + { + // pDatabase->m_pLastDirtyNode is guaranteed to be non-NULL, + // Hence, m_pPrevInDatabase will be non-NULL. + // We are also guaranteed that the node is not dirty. + + m_pPrevInDatabase = pDatabase->m_pLastDirtyNode; + m_pNextInDatabase = m_pPrevInDatabase->m_pNextInDatabase; + +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->unprotectCachedItem(); +#endif + m_pPrevInDatabase->m_pNextInDatabase = this; +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->protectCachedItem(); +#endif + if (m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + else + { + pDatabase->m_pLastNode = this; + } + } + + m_pDatabase = pDatabase; + setLinkedToDatabase(); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Link a node to an F_Database list at the end of the list. + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToDatabaseAtEnd( + F_Database * pDatabase) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + // Node cannot be a dirty node. + + flmAssert( !nodeIsDirty()); + m_pNextInDatabase = NULL; + if( (m_pPrevInDatabase = pDatabase->m_pLastNode) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pDatabase->m_pLastNode->unprotectCachedItem(); +#endif + pDatabase->m_pLastNode->m_pNextInDatabase = this; +#ifdef FLM_CACHE_PROTECT + pDatabase->m_pLastNode->protectCachedItem(); +#endif + } + else + { + pDatabase->m_pFirstNode = this; + } + + pDatabase->m_pLastNode = this; + m_pDatabase = pDatabase; + setLinkedToDatabase(); + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Unlink a node from its F_Database list. + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromDatabase( void) + { + if( nodeLinkedToDatabase()) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + // If this is the last dirty node, change the database's + // last dirty pointer to point to the previous node, if any. + + if (m_pDatabase->m_pLastDirtyNode == this) + { + flmAssert( nodeIsDirty()); + m_pDatabase->m_pLastDirtyNode = m_pPrevInDatabase; + } + + // Remove the node from the database's list. + + if( m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pLastNode = m_pPrevInDatabase; + } + + if( m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->unprotectCachedItem(); +#endif + m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pFirstNode = m_pNextInDatabase; + } + + m_pPrevInDatabase = NULL; + m_pNextInDatabase = NULL; + m_pDatabase = NULL; + unsetLinkedToDatabase(); + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + } + + // Link a node into its hash bucket. + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToHashBucket( void) + { + F_CachedNode ** ppHashBucket = gv_XFlmSysData.pNodeCacheMgr->nodeHash( + m_nodeInfo.ui64NodeId); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + flmAssert( m_pNewerVersion == NULL); + + m_pPrevInBucket = NULL; + + if ((m_pNextInBucket = *ppHashBucket) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInBucket->unprotectCachedItem(); +#endif + m_pNextInBucket->m_pPrevInBucket = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInBucket->protectCachedItem(); +#endif + } + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + *ppHashBucket = this; + } + + // Unlink a node from its hash bucket. + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromHashBucket( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + flmAssert( m_pNewerVersion == NULL); + + if (m_pNextInBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInBucket->unprotectCachedItem(); +#endif + m_pNextInBucket->m_pPrevInBucket = m_pPrevInBucket; +#ifdef FLM_CACHE_PROTECT + m_pNextInBucket->protectCachedItem(); +#endif + } + + if (m_pPrevInBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInBucket->unprotectCachedItem(); +#endif + m_pPrevInBucket->m_pNextInBucket = m_pNextInBucket; +#ifdef FLM_CACHE_PROTECT + m_pPrevInBucket->protectCachedItem(); +#endif + } + else + { + F_CachedNode ** ppHashBucket = + gv_XFlmSysData.pNodeCacheMgr->nodeHash( + m_nodeInfo.ui64NodeId); + + *ppHashBucket = m_pNextInBucket; + } + + m_pPrevInBucket = NULL; + m_pNextInBucket = NULL; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Unlink a node from its version list. + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToVerList( + F_CachedNode * pNewerVer, + F_CachedNode * pOlderVer) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if( (m_pNewerVersion = pNewerVer) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pNewerVer->unprotectCachedItem(); +#endif + pNewerVer->m_pOlderVersion = this; +#ifdef FLM_CACHE_PROTECT + pNewerVer->protectCachedItem(); +#endif + } + + if ((m_pOlderVersion = pOlderVer) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pOlderVer->unprotectCachedItem(); +#endif + pOlderVer->m_pNewerVersion = this; +#ifdef FLM_CACHE_PROTECT + pOlderVer->protectCachedItem(); +#endif + } + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Unlink a node from its version list. This routine + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromVerList( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_pNewerVersion) + { +#ifdef FLM_CACHE_PROTECT + m_pNewerVersion->unprotectCachedItem(); +#endif + m_pNewerVersion->m_pOlderVersion = m_pOlderVersion; +#ifdef FLM_CACHE_PROTECT + m_pNewerVersion->protectCachedItem(); +#endif + } + + if (m_pOlderVersion) + { +#ifdef FLM_CACHE_PROTECT + m_pOlderVersion->unprotectCachedItem(); +#endif + m_pOlderVersion->m_pNewerVersion = m_pNewerVersion; +#ifdef FLM_CACHE_PROTECT + m_pOlderVersion->protectCachedItem(); +#endif + } + + m_pNewerVersion = NULL; + m_pOlderVersion = NULL; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Link a node into the heap list + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToHeapList( void) + { + flmAssert( !m_pPrevInHeapList); + flmAssert( (m_uiFlags & FDOM_HEAP_ALLOC) == 0); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if( (m_pNextInHeapList = + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList->unprotectCachedItem(); +#endif + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList->m_pPrevInHeapList = this; +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList->protectCachedItem(); +#endif + } + + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList = this; + m_uiFlags |= FDOM_HEAP_ALLOC; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Unlink a node from the heap list + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromHeapList( void) + { + flmAssert( m_uiFlags & FDOM_HEAP_ALLOC); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_pNextInHeapList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInHeapList->unprotectCachedItem(); +#endif + m_pNextInHeapList->m_pPrevInHeapList = m_pPrevInHeapList; +#ifdef FLM_CACHE_PROTECT + m_pNextInHeapList->protectCachedItem(); +#endif + } + + if (m_pPrevInHeapList) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInHeapList->unprotectCachedItem(); +#endif + m_pPrevInHeapList->m_pNextInHeapList = m_pNextInHeapList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInHeapList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pNodeCacheMgr->m_pHeapList = m_pNextInHeapList; + } + + m_pPrevInHeapList = NULL; + m_pNextInHeapList = NULL; + m_uiFlags &= ~FDOM_HEAP_ALLOC; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void linkToOldList( void) + { + flmAssert( !m_pPrevInOldList); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if( (m_pNextInOldList = + gv_XFlmSysData.pNodeCacheMgr->m_pOldList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_pOldList->unprotectCachedItem(); +#endif + gv_XFlmSysData.pNodeCacheMgr->m_pOldList->m_pPrevInOldList = this; +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_pOldList->protectCachedItem(); +#endif + } + + gv_XFlmSysData.pNodeCacheMgr->m_pOldList = this; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + // Assumes that the node cache mutex has already been locked. + FINLINE void unlinkFromOldList( void) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_pNextInOldList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInOldList->unprotectCachedItem(); +#endif + m_pNextInOldList->m_pPrevInOldList = m_pPrevInOldList; +#ifdef FLM_CACHE_PROTECT + m_pNextInOldList->protectCachedItem(); +#endif + } + + if (m_pPrevInOldList) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInOldList->unprotectCachedItem(); +#endif + m_pPrevInOldList->m_pNextInOldList = m_pNextInOldList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInOldList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pNodeCacheMgr->m_pOldList = m_pNextInOldList; + } + + m_pPrevInOldList = NULL; + m_pNextInOldList = NULL; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + FINLINE FLMUINT memSize( void) + { + FLMUINT uiSize = gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.getCellSize(); + + if (m_pucData) + { + uiSize += gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.getTrueSize( + m_uiDataBufSize, getActualPointer( m_pucData)); + } + + if( m_pNodeList) + { + uiSize += gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.getTrueSize( + calcNodeListBufSize( m_nodeInfo.uiChildElmCount), + getActualPointer( m_pNodeList)); + } + + if( m_ppAttrList) + { + uiSize += gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.getTrueSize( + calcAttrListBufSize( m_uiAttrCount), + getActualPointer( m_ppAttrList)); + } + + return( uiSize + m_uiTotalAttrSize); + } + + // Assumes that the node cache mutex is locked, because + // it potentially updates the cache usage statistics. + + FINLINE void setTransID( + FLMUINT64 ui64NewTransID) + { + FLMUINT uiSize; + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_ui64HighTransId == FLM_MAX_UINT64 && + ui64NewTransID != FLM_MAX_UINT64) + { + uiSize = memSize(); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount++; + linkToOldList(); + } + else if (m_ui64HighTransId != FLM_MAX_UINT64 && + ui64NewTransID == FLM_MAX_UINT64) + { + uiSize = memSize(); + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount--; + unlinkFromOldList(); + } + + m_ui64HighTransId = ui64NewTransID; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + } + + void freePurged( void); + + void linkToDatabase( + F_Database * pDatabase, + F_Db * pDb, + FLMUINT64 ui64LowTransId, + FLMBOOL bMostCurrent); + + RCODE importAttributeList( + F_Db * pDb, + IF_IStream * pIStream, + FLMBOOL bMutexAlreadyLocked); + + RCODE importAttributeList( + F_Db * pDb, + F_CachedNode * pSourceNode, + FLMBOOL bMutexAlreadyLocked); + + void resetNode( void); + + typedef union + { + FLMUINT64 ui64Val; + FLMINT64 i64Val; + } FLMNUMBER; + + // Things that manage the node's place in node cache. These are + // always initialized in the constructor + + F_CachedNode * m_pPrevInBucket; + F_CachedNode * m_pNextInBucket; + F_CachedNode * m_pPrevInDatabase; + F_CachedNode * m_pNextInDatabase; + F_CachedNode * m_pOlderVersion; + F_CachedNode * m_pNewerVersion; + F_CachedNode * m_pPrevInHeapList; + F_CachedNode * m_pNextInHeapList; + F_CachedNode * m_pPrevInOldList; + F_CachedNode * m_pNextInOldList; + + FLMUINT64 m_ui64LowTransId; + FLMUINT64 m_ui64HighTransId; + FNOTIFY * m_pNotifyList; + FLMUINT m_uiCacheFlags; + FLMUINT m_uiStreamUseCount; + + // Things we hash on - initialized by caller of constructor + + F_Database * m_pDatabase; + + // Items initialized in constructor + + F_NODE_INFO m_nodeInfo; + FLMUINT m_uiFlags; + FLMBYTE * m_pucData; + FLMUINT m_uiDataBufSize; + NODE_ITEM * m_pNodeList; + F_AttrItem ** m_ppAttrList; + FLMUINT m_uiAttrCount; + FLMUINT m_uiTotalAttrSize; + + // Items initialized by caller of constructor, but not + // in constructor - for performance reasons - so we don't + // end up setting them twice. + + FLMUINT m_uiOffsetIndex; + FLMUINT32 m_ui32BlkAddr; + + // Items that m_uiFlags indicates whether they are present + + FLMNUMBER m_numberVal; // Valid only if FDOM_SIGNED_QUICK_VAL + // or FDOM_UNSIGNED_QUICK_VAL is set on m_uiFlags + +friend class F_NodeCacheMgr; +friend class F_GlobalCacheMgr; +friend class F_Database; +friend class F_Db; +friend class F_DbSystem; +friend class F_BTreeIStream; +friend class F_LocalNodeCache; +friend class F_DOMNode; +friend class F_Rfl; +friend class F_NodeInfo; +friend class F_RebuildNodeIStream; +friend class F_DbRebuild; +friend class F_AttrItem; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +}; + +RCODE flmReadNodeInfo( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_IStream * pIStream, + FLMUINT uiOverallLength, + FLMBOOL bAssertOnCorruption, + F_NODE_INFO * pNodeInfo, + FLMUINT * puiStorageFlags, + FLMBOOL * pbFixedSizeHeader = NULL); + +#endif // FCACHE_H diff --git a/version5/src/fcollate.cpp b/version5/src/fcollate.cpp new file mode 100644 index 0000000..39207c3 --- /dev/null +++ b/version5/src/fcollate.cpp @@ -0,0 +1,7362 @@ +//------------------------------------------------------------------------------ +// Desc: Routines for building collation keys +// +// Tabs: 3 +// +// Copyright (c) 1993-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: fcollate.cpp 3111 2006-01-19 13:10:50 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define shiftN(data,size,distance) \ + f_memmove((FLMBYTE *)(data) + (FLMINT)(distance), \ + (FLMBYTE *)(data), (size_t)(size)) + +typedef struct +{ + FLMBYTE base; + FLMBYTE diacrit; +} BASE_DIACRIT_TABLE; + +typedef struct +{ + FLMUINT16 char_count; // # of characters in table + FLMUINT16 start_char; // start char. + BASE_DIACRIT_TABLE * table; + +} BASE_DIACRIT; + +typedef struct +{ + FLMBYTE key; // character key to search on + FLMBYTE * charPtr; // character pointer for matched key +} TBL_B_TO_BP; + +typedef struct +{ + FLMBYTE ByteValue; + FLMUINT16 WordValue; +} BYTE_WORD_TBL; + +// Static functions + +FSTATIC RCODE flmColStr2WPStr( + const FLMBYTE * pucColStr, + FLMUINT uiColStrLen, + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT uiLang, + FLMUINT * puiUnconvChars, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbFirstSubstring); + +FSTATIC RCODE flmWPCmbSubColBuf( + FLMBYTE * pucWPStr, + FLMUINT * uiWPStrLen, + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucSubColBuf, + FLMBOOL bHebrewArabic, + FLMUINT * puiSubColBitPos); + +FSTATIC FLMUINT flmWPToMixed( + FLMBYTE * pucWPStr, + FLMUINT uiWPStrLen, + const FLMBYTE * pucLowUpBitStr, + FLMUINT uiLang); + +FSTATIC FLMUINT16 flmWPZenToHankaku( + FLMUINT16 ui16WpChar, + FLMUINT16 * pui16DakutenOrHandakuten); + +FSTATIC FLMUINT16 flmWPHanToZenkaku( + FLMUINT16 ui16WpChar, + FLMUINT16 ui16NextWpChar, + FLMUINT16 * pui16Zenkaku); + +FSTATIC FLMBOOL flmWPCmbcar( + FLMUINT16 * pui16WpChar, + FLMUINT16 ui16BaseChar, + FLMINT16 ui16DiacriticChar); + +FSTATIC RCODE flmAsiaColStr2WPStr( + const FLMBYTE * pucColStr, + FLMUINT uiColStrLen, + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT * puiUnconvChars, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbFirstSubstring); + +FSTATIC RCODE flmAsiaParseSubCol( + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucSubColBuf, + FLMUINT * puiSubColBitPos); + +FSTATIC RCODE flmAsiaParseCase( + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucCaseBits, + FLMUINT * puiColBytesProcessed); + +#define MAX_COL_OPCODE COLL_TRUNCATED // Max. opcode for any collation markers +#define WP_MAX_CAR60_SIZE NCHSETS +#define SET_CASE_BIT 0x01 +#define SET_KATAKANA_BIT 0x01 +#define SET_WIDTH_BIT 0x02 +#define COLS_ASIAN_MARKS 0x140 +#define COLS_ASIAN_MARK_VAL 0x40 // Without 0x100 + +#define ASCTBLLEN 95 +#define MNTBLLEN 219 +#define SYMTBLLEN 9 +#define GRKTBLLEN 219 +#define CYRLTBLLEN 200 +#define HEBTBL1LEN 27 +#define HEBTBL2LEN 35 +#define AR1TBLLEN 158 +#define AR2TBLLEN 179 + +#define Upper_JP_A 0x2520 +#define Upper_JP_Z 0x2539 +#define Upper_KR_A 0x5420 +#define Upper_KR_Z 0x5439 +#define Upper_CS_A 0x82FC +#define Upper_CS_Z 0x8316 +#define Upper_CT_A 0xA625 +#define Upper_CT_Z 0xA63E + +#define Lower_JP_a 0x2540 +#define Lower_JP_z 0x2559 +#define Lower_KR_a 0x5440 +#define Lower_KR_z 0x5459 +#define Lower_CS_a 0x82DC +#define Lower_CS_z 0x82F5 +#define Lower_CT_a 0xA60B +#define Lower_CT_z 0xA624 + +// # of characters in each character set. +// CHANGING ANY OF THESE DEFINES WILL CAUSE BUGS! + +#define ASC_N 95 +#define ML1_N 242 +#define ML2_N 145 +#define BOX_N 88 +#define TYP_N 103 +#define ICN_N 255 +#define MTH_N 238 +#define MTX_N 229 +#define GRK_N 219 +#define HEB_N 123 +#define CYR_N 250 +#define KAN_N 63 +#define USR_N 255 +#define ARB_N 196 +#define ARS_N 220 + +// TOTAL: 1447 WP + 255 User Characters + +#define C_N ASC_N + ML1_N + ML2_N + BOX_N +\ + MTH_N + MTX_N + TYP_N + ICN_N +\ + GRK_N + HEB_N + CYR_N + KAN_N +\ + USR_N + ARB_N + ARS_N + +// State table constants for double character sorting + +#define STATE1 1 +#define STATE2 2 +#define STATE3 3 +#define STATE4 4 +#define STATE5 5 +#define STATE6 6 +#define STATE7 7 +#define STATE8 8 +#define STATE9 9 +#define STATE10 10 +#define STATE11 11 +#define AFTERC 12 +#define AFTERH 13 +#define AFTERL 14 +#define INSTAE 15 +#define INSTOE 16 +#define INSTSG 17 +#define INSTIJ 18 +#define WITHAA 19 + +#define START_COL 12 +#define START_ALL (START_COL + 1) // all US and european +#define START_DK (START_COL + 2) // Danish +#define START_IS (START_COL + 3) // Icelandic +#define START_NO (START_COL + 4) // Norwegian +#define START_SU (START_COL + 5) // Finnish +#define START_SV (START_COL + 5) // Swedish +#define START_YK (START_COL + 6) // Ukrain +#define START_TK (START_COL + 7) // Turkish +#define START_CZ (START_COL + 8) // Czech +#define START_SL (START_COL + 8) // Slovak + +#define FIXUP_AREA_SIZE 24 // Number of characters to fix up + +// Collation tables + +/**************************************************************************** +Desc: Table of # of characters in each character set +****************************************************************************/ +FLMBYTE fwp_c60_max[] = +{ + ASC_N, // ascii + ML1_N, // multinational 1 + ML2_N, // multinational 2 + BOX_N, // line draw + TYP_N, // typographic + ICN_N, // icons + MTH_N, // math + MTX_N, // math extension + GRK_N, // Greek + HEB_N, // Hebrew + CYR_N, // Cyrillic - Russian + KAN_N, // Kana + USR_N, // user + ARB_N, // Arabic + ARS_N, // Arabic Script +}; + +/**************************************************************************** +Desc: Base character location table + Bit mapped table. (1) - corresponding base char is in same + set as combined + (0) - corresponding base char is in ascii set + +Notes: In the following table, the bits are numbered from left + to right relative to each individual byte. + EX. 00000000b ;0-7 + bit# 01234567 +****************************************************************************/ +FLMBYTE fwp_ml1_cb60[] = +{ + 0x00, // 0-7 + 0x00, // 8-15 + 0x00, // 16-23 + 0x00, // 24-31 + 0x00, // 32-39 + 0x00, // 40-47 + 0x55, // 48-55 + 0x00, // 56-63 + 0x00, // 64-71 + 0x00, // 72-79 + 0x00, // 80-87 + 0x00, // 88-95 + 0x00, // 96-103 + 0x00, // 104-111 + 0x00, // 112-119 + 0x00, // 120-127 + 0x14, // 128-135 + 0x44, // 136-143 + 0x00, // 144-151 + 0x00, // 152-159 + 0x00, // 160-167 + 0x00, // 168-175 + 0x00, // 176-183 + 0x00, // 184-191 + 0x00, // 192-199 + 0x00, // 200-207 + 0x00, // 208-215 + 0x00, // 216-223 + 0x00, // 224-231 + 0x04, // 232-239 + 0x00, // 240-241 +}; + +/**************************************************************************** +Desc: Format of index: + 2 words before = count. + word before = start character. + db code for base char. + db code for diacritic +Notes: Diacritical char is always in same set as composed char + base is in same set if other table indicates, else in ASCII +****************************************************************************/ +BASE_DIACRIT_TABLE fwp_ml1c_table[] = +{ + {'A',acute}, + {'a',acute}, + {'A',circum}, + {'a',circum}, + {'A',umlaut}, + {'a',umlaut}, + {'A',grave}, + {'a',grave}, + {'A',ring}, + {'a',ring}, + {0xff,0xff}, // no AE diagraph + {0xff,0xff}, // no ae diagraph + {'C',cedilla}, + {'c',cedilla}, + {'E',acute}, + {'e',acute}, + {'E',circum}, + {'e',circum}, + {'E',umlaut}, + {'e',umlaut}, + {'E',grave}, + {'e',grave}, + {'I',acute}, + {dotlesi,acute}, + {'I',circum}, + {dotlesi,circum}, + {'I',umlaut}, + {dotlesi,umlaut}, + {'I',grave}, + {dotlesi,grave}, + {'N',tilde}, + {'n',tilde}, + {'O',acute}, + {'o',acute}, + {'O',circum}, + {'o',circum}, + {'O',umlaut}, + {'o',umlaut}, + {'O',grave}, + {'o',grave}, + {'U',acute}, + {'u',acute}, + {'U',circum}, + {'u',circum}, + {'U',umlaut}, + {'u',umlaut}, + {'U',grave}, + {'u',grave}, + {'Y',umlaut}, + {'y',umlaut}, + {'A',tilde}, + {'a',tilde}, + {'D',crossb}, + {'d',crossb}, + {'O',slash}, + {'o',slash}, + {'O',tilde}, + {'o',tilde}, + {'Y',acute}, + {'y',acute}, + {0xff,0xff}, // no eth + {0xff,0xff}, // no eth + {0xff,0xff}, // no Thorn + {0xff,0xff}, // no Thorn + {'A',breve}, + {'a',breve}, + {'A',macron}, + {'a',macron}, + {'A',ogonek}, + {'a',ogonek}, + {'C',acute}, + {'c',acute}, + {'C',caron}, + {'c',caron}, + {'C',circum}, + {'c',circum}, + {'C',dota}, + {'c',dota}, + {'D',caron}, + {'d',caron}, + {'E',caron}, + {'e',caron}, + {'E',dota}, + {'e',dota}, + {'E',macron}, + {'e',macron}, + {'E',ogonek}, + {'e',ogonek}, + {'G',acute}, + {'g',acute}, + {'G',breve}, + {'g',breve}, + {'G',caron}, + {'g',caron}, + {'G',cedilla}, + {'g',aposab}, + {'G',circum}, + {'g',circum}, + {'G',dota}, + {'g',dota}, + {'H',circum}, + {'h',circum}, + {'H',crossb}, + {'h',crossb}, + {'I',dota}, + {dotlesi,dota}, + {'I',macron}, + {dotlesi,macron}, + {'I',ogonek}, + {'i',ogonek}, + {'I',tilde}, + {dotlesi,tilde}, + {0xff,0xff}, // no IJ digraph + {0xff,0xff}, // no ij digraph + {'J',circum}, + {dotlesj,circum}, + {'K',cedilla}, + {'k',cedilla}, + {'L',acute}, + {'l',acute}, + {'L',caron}, + {'l',caron}, + {'L',cedilla}, + {'l',cedilla}, + {'L',centerd}, + {'l',centerd}, + {'L',stroke}, + {'l',stroke}, + {'N',acute}, + {'n',acute}, + {'N',aposba}, + {'n',aposba}, + {'N',caron}, + {'n',caron}, + {'N',cedilla}, + {'n',cedilla}, + {'O',dacute}, + {'o',dacute}, + {'O',macron}, + {'o',macron}, + {0xff,0xff}, // OE digraph + {0xff,0xff}, // oe digraph + {'R',acute}, + {'r',acute}, + {'R',caron}, + {'r',caron}, + {'R',cedilla}, + {'r',cedilla}, + {'S',acute}, + {'s',acute}, + {'S',caron}, + {'s',caron}, + {'S',cedilla}, + {'s',cedilla}, + {'S',circum}, + {'s',circum}, + {'T',caron}, + {'t',caron}, + {'T',cedilla}, + {'t',cedilla}, + {'T',crossb}, + {'t',crossb}, + {'U',breve}, + {'u',breve}, + {'U',dacute}, + {'u',dacute}, + {'U',macron}, + {'u',macron}, + {'U',ogonek}, + {'u',ogonek}, + {'U',ring}, + {'u',ring}, + {'U',tilde}, + {'u',tilde}, + {'W',circum}, + {'w',circum}, + {'Y',circum}, + {'y',circum}, + {'Z',acute}, + {'z',acute}, + {'Z',caron}, + {'z',caron}, + {'Z',dota}, + {'z',dota}, + {0xff,0xff}, // no Eng + {0xff,0xff}, // no eng + {'D',macron}, + {'d',macron}, + {'L',macron}, + {'l',macron}, + {'N',macron}, + {'n',macron}, + {'R',grave}, + {'r',grave}, + {'S',macron}, + {'s',macron}, + {'T',macron}, + {'t',macron}, + {'Y',breve}, + {'y',breve}, + {'Y',grave}, + {'y',grave}, + {'D',aposbes}, + {'d',aposbes}, + {'O',aposbes}, + {'o',aposbes}, + {'U',aposbes}, + {'u',aposbes}, + {'E',breve}, + {'e',breve}, + {'I',breve}, + {dotlesi,breve}, + {0xff,0xff}, // no dotless I + {0xff,0xff}, // no dotless i + {'O',breve}, + {'o',breve} +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +BASE_DIACRIT fwp_ml1c = +{ + 216, // # of characters in table + 26, // start char + fwp_ml1c_table, +}; + +/**************************************************************************** +Desc: Format of index: + 2 words before = count. + word before = start character. + db code for base char. + db code for diacritic +Notes: Diacritical char is always in same set as composed char + base is in same set +****************************************************************************/ +static BASE_DIACRIT_TABLE fwp_grk_c_table[] = +{ + { 0, ghprime }, // ALPHA High Prime + { 1, gacute }, // alpha acute + { 10, ghprime }, // EPSILON High Prime + { 11, gacute }, // epsilon Acute + { 14, ghprime }, // ETA High Prime + { 15, gacute }, // eta Acute + { 18, ghprime }, // IOTA High Prime + { 19, gacute }, // iota Acute + { 0xFF, 0xFF }, // IOTA Diaeresis + { 19, gdia }, // iota Diaeresis + { 30, ghprime }, // OMICRON High Prime + { 31, gacute }, // omicron Acute + { 42, ghprime }, // UPSILON High Prime + { 43, gacute }, // upsilon Acute + { 0xFF, 0xFF }, // UPSILON Diaeresis + { 43,gdia }, // upsilon Diaeresis + { 50,ghprime }, // OMEGA High Prime + { 51,gacute }, // omega Acute + { 0xFF, 0xFF }, // epsilon (Variant) + { 0xFF, 0xFF }, // theta (Variant) + { 0xFF, 0xFF }, // kappa (Variant) + { 0xFF, 0xFF }, // pi (Variant) + { 0xFF, 0xFF }, // rho (Variant) + { 0xFF, 0xFF }, // sigma (Variant) + { 0xFF, 0xFF }, // UPSILON (Variant) + { 0xFF, 0xFF }, // phi (Variant) + { 0xFF, 0xFF }, // omega (Variant) + { 0xFF, 0xFF }, // Greek Question Mark + { 0xFF, 0xFF }, // Greek Semicolon + { 0xFF, 0xFF }, // High Prime + { 0xFF, 0xFF }, // Low Prime + { 0xFF, 0xFF }, // Acute (Greek) + { 0xFF, 0xFF }, // Diaeresis (Greek) + { gacute,gdia }, // Acute Diaeresis + { ggrave, gdia }, // Grave Diaeresis + { 0xFF, 0xFF }, // Grave (Greek) + { 0xFF, 0xFF }, // Circumflex (Greek) + { 0xFF, 0xFF }, // Smooth Breathing + { 0xFF, 0xFF }, // Rough Breathing + { 0xFF, 0xFF }, // Iota Subscript + { gsmooth, gacute }, // Smooth Breathing Acute + { grough, gacute }, // Rough Breathing Acute + { gsmooth, ggrave }, // Smooth Breathing Grave + { grough, ggrave }, // Rough Breathing Grave + { gsmooth, gcircm }, // Smooth Breathing Circumflex + { grough, gcircm }, // Rough Breathing Circumflex + { gacute, giota }, // Acute w/Iota Subscript + { ggrave, giota }, // Grave w/Iota Subscript + { gcircm, giota }, // Circumflex w/Iota Subscript + { gsmooth, giota }, // Smooth Breathing w/Iota Subscript + { grough, giota }, // Rough Breathing w/Iota Subscript + { gsmact, giota }, // Smooth Breathing Acute w/Iota Subscript + { grgact, giota }, // Rough Breathing Acute w/Iota Subscript + { gsmgrv, giota }, // Smooth Breathing Grave w/Iota Subscript + { grggrv, giota }, // Rough Breathing Grave w/Iota Subscript + { gsmcir, giota }, // Smooth Breathing Circumflex w/Iota Sub + { grgcir, giota }, // Rough Breathing Circumflex w/Iota Sub + { 1, ggrave }, // alpha Grave + { 1, gcircm }, // alpha Circumflex + { 1, giota }, // alpha w/Iota + { 1, gactio }, // alpha Acute w/Iota + { 1, ggrvio }, // alpha Grave w/Iota + { 1, gcirio }, // alpha Circumflex w/Iota + { 1, gsmooth }, // alpha Smooth + { 1, gsmact }, // alpha Smooth Acute + { 1, gsmgrv }, // alpha Smooth Grave + { 1, gsmcir }, // alpha Smooth Circumflex + { 1, gsmio }, // alpha Smooth w/Iota + { 1, gsmaio }, // alpha Smooth Acute w/Iota + { 1, gsmgvio }, // alpha Smooth Grave w/Iota + { 1, gsmcio }, // alpha Smooth Circumflex w/Iota + { 1, grough }, // alpha Rough + { 1, grgact }, // alpha Rough Acute + { 1, grggrv }, // alpha Rough Grave + { 1, grgcir }, // alpha Rough Circumflex + { 1, grgio }, // alpha Rough w/Iota + { 1, grgaio }, // alpha Rough Acute w/Iota + { 1, grggvio }, // alpha Rough Grave w/Iota + { 1, grgcio }, // alpha Rough Circumflex w/Iota + { 11, ggrave }, // epsilon Grave + { 11, gsmooth }, // epsilon Smooth + { 11, gsmact }, // epsilon Smooth Acute + { 11, gsmgrv }, // epsilon Smooth Grave + { 11, grough }, // epsilon Rough + { 11, grgact }, // epsilon Rough Acute + { 11, grggrv }, // epsilon Rough Grave + { 15, ggrave }, // eta Grave + { 15, gcircm }, // eta Circumflex + { 15, giota }, // eta w/Iota + { 15, gactio }, // eta Acute w/Iota + { 15, ggrvio }, // eta Grave w/Iota + { 15, gcirio }, // eta Circumflex w/Iota + { 15, gsmooth }, // eta Smooth + { 15, gsmact }, // eta Smooth Acute + { 15, gsmgrv }, // eta Smooth Grave + { 15, gsmcir }, // eta Smooth Circumflex + { 15, gsmio }, // eta Smooth w/Iota + { 15, gsmaio }, // eta Smooth Acute w/Iota + { 15, gsmgvio }, // eta Smooth Grave w/Iota + { 15, gsmcio }, // eta Smooth Circumflex w/Iota + { 15, grough }, // eta Rough + { 15, grgact }, // eta Rough Acute + { 15, grggrv }, // eta Rough Grave + { 15, grgcir }, // eta Rough Circumflex + { 15, grgio }, // eta Rough w/Iota + { 15, grgaio }, // eta Rough Acute w/Iota + { 15, grggvio }, // eta Rough Grave w/Iota + { 15, grgcio }, // eta Rough Circumflex w/Iota + { 19, ggrave }, // iota Grave + { 19, gcircm }, // iota Circumflex + { 19, gactdia }, // iota Acute Diaeresis + { 19, ggrvdia }, // iota Grave Diaeresis + { 19, gsmooth }, // iota Smooth + { 19, gsmact }, // iota Smooth Acute + { 19, gsmgrv }, // iota Smooth Grave + { 19, gsmcir }, // iota Smooth Circumflex + { 19, grough }, // iota Rough + { 19, grgact }, // iota Rough Acute + { 19, grggrv }, // iota Rough Grave + { 19, grgcir }, // iota Rough Circumflex + { 31, ggrave }, // omicron Grave + { 31, gsmooth }, // omicron Smooth + { 31, gsmact }, // omicron Smooth Acute + { 31, gsmgrv }, // omicron Smooth Grave + { 31, grough }, // omicron Rough + { 31, grgact }, // omicron Rough Acute + { 31, grggrv }, // omicron Rough Grave + { 0xFF, 0xFF }, // rho rough + { 0xFF, 0xFF }, // rho smooth + { 43, ggrave }, // upsilon Grave + { 43, gcircm }, // upsilon Circumflex + { 43, gactdia }, // upsilon Acute Diaeresis + { 43, ggrvdia }, // upsilon Grave Diaeresis + { 43, gsmooth }, // upsilon Smooth + { 43, gsmact }, // upsilon Smooth Acute + { 43, gsmgrv }, // upsilon Smooth Grave + { 43, gsmcir }, // upsilon Smooth Circumflex + { 43, grough }, // upsilon Rough + { 43, grgact }, // upsilon Rough Acute + { 43, grggrv }, // upsilon Rough Grave + { 43, grgcir }, // upsilon Rough Circumflex + { 51, ggrave }, // omega Grave + { 51, gcircm }, // omega Circumflex + { 51, giota }, // omega w/Iota + { 51, gactio }, // omega Acute w/Iota + { 51, ggrvio }, // omega Grave w/Iota + { 51, gcirio }, // omega Circumflex w/Iota + { 51, gsmooth }, // omega Smooth + { 51, gsmact }, // omega Smooth Acute + { 51, gsmgrv }, // omega Smooth Grave + { 51, gsmcir }, // omega Smooth Circumflex + { 51, gsmio }, // omega Smooth w/Iota + { 51, gsmaio }, // omega Smooth Acute w/Iota + { 51, gsmgvio }, // omega Smooth Grave w/Iota + { 51, gsmcio }, // omega Smooth Circumflex w/Iota + { 51, grough }, // omega Rough + { 51, grgact }, // omega Rough Acute + { 51, grggrv }, // omega Rough Grave + { 51, grgcir }, // omega Rough Circumflex + { 51, grgio }, // omega Rough w/Iota + { 51, grgaio }, // omega Rough Acute w/Iota + { 51, grggvio }, // omega Rough Grave w/Iota + { 51, grgcio} // omega Rough Circumflex w/Iota +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT fwp_grk_c = +{ + 163, // # of characters in table. + 52, // start char. + fwp_grk_c_table +}; + +/**************************************************************************** +Desc: Format of index: + 2 words before = count. + word before = start character. + db code for base char. + db code for diacritic +Notes: Diacritical char is always in same set as composed char + base is in same set +****************************************************************************/ +static BASE_DIACRIT_TABLE fwp_rus_c_table[] = +{ + { 14, 204 }, // ZHE with right descender + { 15, 204 }, // zhe with right descender + { 0xFF, 0xFF}, // DZE + { 0xFF, 0xFF}, // dze + { 0xFF, 0xFF}, // Z + { 0xFF, 0xFF}, // z + { 18, 206 }, // II with macron + { 19, 206}, // ii with macron + { 0xFF, 0xFF}, // I + { 0xFF, 0xFF}, // i + { 0xFF, 0xFF}, // YI + { 0xFF, 0xFF}, // yi + { 0xFF, 0xFF}, // I ligature + { 0xFF, 0xFF}, // i ligature + { 0xFF, 0xFF}, // JE + { 0xFF, 0xFF}, // je + { 0xFF, 0xFF}, // KJE + { 0xFF, 0xFF}, // kje + { 22, 204}, // KA with right descender + { 23, 204}, // ka with right descender + { 22, 205 }, // KA ogonek + { 23, 205 }, // ka ogonek + { 0xFF, 0xFF}, // KA vertical bar + { 0xFF, 0xFF}, // ka vertical bar + { 0xFF, 0xFF}, // LJE + { 0xFF, 0xFF}, // lje + { 28, 204 }, // EN with right descender + { 29, 204 }, // en with right descender + { 0xFF, 0xFF}, // NJE + { 0xFF, 0xFF}, // nje + { 0xFF, 0xFF}, // ROUND OMEGA + { 0xFF, 0xFF}, // round omega + { 0xFF, 0xFF}, // OMEGA + { 0xFF, 0xFF}, // omega + { 0xFF, 0xFF}, // TSHE + { 0xFF, 0xFF}, // tshe + { 0xFF, 0xFF}, // SHORT U + { 0xFF, 0xFF}, // short u + { 40, 206}, // U with macron + { 41, 206 }, // u with macron + { 0xFF, 0xFF}, // STRAIGHT U + { 0xFF, 0xFF}, // straight u + { 0xFF, 0xFF}, // STRAIGHT U BAR + { 0xFF, 0xFF}, // straight u bar + { 0xFF, 0xFF}, // OU ligature + { 0xFF, 0xFF}, // ou ligature + { 44, 204 }, // KHA with right descender + { 45, 204 }, // kha with right descender + { 44, 205 }, // KHA ogonek + { 45, 205 }, // kha ogonek + { 0xFF, 0xFF}, // H + { 0xFF, 0xFF}, // h + { 0xFF, 0xFF}, // OMEGA titlo + { 0xFF, 0xFF}, // omega titlo + { 0xFF, 0xFF}, // DZHE + { 0xFF, 0xFF}, // dzhe + { 48, 204 }, // CHE with right descender + { 49, 204 }, // che with right descender + { 0xFF, 0xFF}, // CHE vertical bar + { 0xFF, 0xFF}, // che vertical bar + { 0xFF, 0xFF}, // SHCHA (variant) + { 0xFF, 0xFF}, // shcha (variant) + { 0xFF, 0xFF}, // YAT + { 0xFF, 0xFF}, // yat + { 0xFF, 0xFF}, // YUS BOLSHOI + { 0xFF, 0xFF}, // yus bolshoi + { 0xFF, 0xFF}, // BIG MALYI + { 0xFF, 0xFF}, // big malyi + { 0xFF, 0xFF}, // KSI + { 0xFF, 0xFF}, // ksi + { 0xFF, 0xFF}, // PSI + { 0xFF, 0xFF}, // psi + { 0xFF, 0xFF}, // FITA + { 0xFF, 0xFF}, // fita + { 0xFF, 0xFF}, // IZHITSA + { 0xFF, 0xFF}, // izhitsa + { 00, racute}, // Russian A acute + { 01, racute }, // Russian a acute + { 10, racute }, // Russian IE acute + { 11, racute }, // Russian ie acute + { 78, racute }, // Russian E acute + { 79, racute }, // Russian e acute + { 18, racute }, // Russian II acute + { 19, racute }, // Russian ii acute + { 88, racute }, // Russian I acute + { 89, racute }, // Russian i acute + { 90, racute }, // Russian YI acute + { 91, racute }, // Russian yi acute + { 30, racute }, // Russian O acute + { 31, racute }, // Russian o acute + { 40, racute }, // Russian U acute + { 41, racute }, // Russian u acute + { 56, racute }, // Russian YERI acute + { 57, racute }, // Russian yeri acute + { 60, racute }, // Russian REVERSED E acute + { 61, racute }, // Russian reversed e acute + { 62, racute }, // Russian IU acute + { 63, racute }, // Russian iu acute + { 64, racute }, // Russian IA acute + { 65, racute }, // Russian ia acute + { 00, rgrave }, // Russian A grave + { 01, rgrave }, // Russian a grave + { 10, rgrave }, // Russian IE grave + { 11, rgrave }, // Russian ie grave + { 12, rgrave }, // Russian YO grave + { 13, rgrave }, // Russian yo grave + { 18, rgrave }, // Russian I grave + { 19, rgrave }, // Russian i grave + { 30, rgrave }, // Russian O grave + { 31, rgrave }, // Russian o grave + { 40, rgrave }, // Russian U grave + { 41, rgrave }, // Russian u grave + { 56, rgrave }, // Russian YERI grave + { 57, rgrave }, // Russian yeri grave + { 60, rgrave }, // Russian REVERSED E grave + { 61, rgrave }, // Russian reversed e grave + { 62, rgrave }, // Russian IU grave + { 63, rgrave }, // Russian iu grave + { 64, rgrave }, // Russian IA grave + { 65, rgrave} // Russian ia grave +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static BASE_DIACRIT fwp_rus_c = +{ + 120, // # of characters in table. + 156, // start char. + fwp_rus_c_table, +}; + +/**************************************************************************** +Desc: Table of pointers to character component tables. +****************************************************************************/ +BASE_DIACRIT * fwp_car60_c[ NCHSETS] = +{ + (BASE_DIACRIT*)0, // no composed characters for ascii. + &fwp_ml1c, + (BASE_DIACRIT*)0, // no composed characters for multinational 2 + (BASE_DIACRIT*)0, // no composed characters for line draw. + (BASE_DIACRIT*)0, // no composed characters for typographic. + (BASE_DIACRIT*)0, // no composed characters for icons. + (BASE_DIACRIT*)0, // no composed characters for math. + (BASE_DIACRIT*)0, // no composed characters for math extension. + &fwp_grk_c, // Greek + (BASE_DIACRIT*)0, // Hebrew + &fwp_rus_c, // Cyrillic - Russian + (BASE_DIACRIT*)0, // Hiragana or Katakana (Japanese) + (BASE_DIACRIT*)0, // no composed characters for user. + (BASE_DIACRIT*)0, // no composed characters for Arabic. + (BASE_DIACRIT*)0, // no composed characters for Arabic Script . +}; + +/**************************************************************************** +Desc: Map special chars in CharSet (x24) to collation values +****************************************************************************/ +BYTE_WORD_TBL fwp_Ch24ColTbl[] = // Position in the table+1 is subColValue +{ + {1, COLLS+2}, // comma + {2, COLLS+1}, // maru + {5, COLS_ASIAN_MARKS+2}, // chuuten + {10, COLS_ASIAN_MARKS}, // dakuten + {11, COLS_ASIAN_MARKS+1}, // handakuten + {43, COLS2+2}, // angled brackets + {44, COLS2+3}, // + {49, COLS2+2}, // pointy brackets + {50, COLS2+3}, + {51, COLS2+2}, // double pointy brackets + {52, COLS2+3}, + {53, COLS1}, // Japanese quotes + {54, COLS1}, + {55, COLS1}, // hollow Japanese quotes + {56, COLS1}, + {57, COLS2+2}, // filled rounded brackets + {58, COLS2+3} +}; + +/**************************************************************************** +Desc: Kana subcollation values + BIT 0: set if large char + BIT 1: set if voiced + BIT 2: set if half voiced +Notes: + To save space should be nibbles + IMPORTANT: + The '1' entries that do not have + a matching '0' entry have been + changed to zero to save space in + the subcollation area. + The original table is listed below. +****************************************************************************/ +FLMBYTE KanaSubColTbl[] = +{ + 0,1,0,1,0,1,0,1,0,1, // a A i I u U e E o O + 1,3,0,3,0,3,1,3,0,3, // KA GA KI GI KU GU KE GE KO GO + 0,3,0,3,0,3,0,3,0,3, // SA ZA SHI JI SU ZU SE ZE SO ZO + 0,3,0,3,0,1,3,0,3,0,3, // TA DA CHI JI tsu TSU ZU TE DE TO DO + 0,0,0,0,0, // NA NI NU NE NO + 0,3,5,0,3,5,0,3,5, // HA BA PA HI BI PI FU BU PU + 0,3,5,0,3,5, // HE BE PE HO BO PO + 0,0,0,0,0, // MA MI MU ME MO + 0,1,0,1,0,1, // ya YA yu YU yo YO + 0,0,0,0,0, // RA RI RU RE RO + 0,1,0,0,0, // wa WA WI WE WO + 0,3,0,0 // N VU ka ke +}; + +/**************************************************************************** +Desc: Map katakana (CharSet x26) to collation values + kana collating values are two byte values + where the high byte is 0x01. +****************************************************************************/ +FLMBYTE KanaColTbl[] = +{ + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, // a A i I u U e E o O + 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, // KA GA KI GI KU GU KE GE KO GO + 10,10,11,11,12,12,13,13,14,14, // SA ZA SHI JI SU ZU SE ZE SO ZO + 15,15,16,16,17,17,17,18,18,19,19, // TA DA CHI JI tsu TSU ZU TE DE TO DO + 20,21,22,23,24, // NA NI NU NE NO + 25,25,25,26,26,26,27,27,27, // HA BA PA HI BI PI FU BU PU + 28,28,28,29,29,29, // HE BE PE HO BO PO + 30,31,32,33,34, // MA MI MU ME MO + 35,35,36,36,37,37, // ya YA yu YU yo YO + 38,39,40,41,42, // RA RI RU RE RO + 43,43,44,45,46, // wa WA WI WE WO + 47, 2, 5, 8 // N VU ka ke +}; + +/**************************************************************************** +Desc: Map KataKana collated value to vowel value for + use for the previous char. +****************************************************************************/ +FLMBYTE KanaColToVowel[] = +{ + 0,1,2,3,4, // a i u e o + 0,1,2,3,4, // ka ki ku ke ko + 0,1,2,3,4, // sa shi su se so + 0,1,2,3,4, // ta chi tsu te to + 0,1,2,3,4, // na ni nu ne no + 0,1,2,3,4, // ha hi hu he ho + 0,1,2,3,4, // ma mi mu me mo + 0,2,4, // ya yu yo + 0,1,2,3,4, // ra ri ru re ro + 0,1,3,4, // wa wi we wo +}; + +/**************************************************************************** +Desc: Convert Zenkaku (double wide) to Hankaku (single wide) + Character set 0x24 maps to single wide chars in other char sets. + This enables collation values to be found on some symbols. + This is also used to convert symbols from hankaku to Zen24. +****************************************************************************/ +BYTE_WORD_TBL Zen24ToHankaku[] = +{ + { 0 ,0x0020 }, // space + { 1 ,0x0b03 }, // japanese comma + { 2 ,0x0b00 }, // circle period + { 3 , 44 }, // comma + { 4 , 46 }, // period + { 5 ,0x0b04 }, // center dot + { 6 , 58 }, // colon + { 7 , 59 }, // semicolon + { 8 , 63 }, // question mark + { 9 , 33 }, // exclamation mark + { 10 ,0x0b3d }, // dakuten + { 11 ,0x0b3e }, // handakuten + { 12 ,0x0106 }, // accent mark + { 13 , 96 }, // accent mark + { 14 ,0x0107 }, // umlat + { 15 , 94 }, // caret + { 16 ,0x0108 }, // macron + { 17 , 95 }, // underscore + { 27 ,0x0b0f }, // extend vowel + { 28 ,0x0422 }, // mdash + { 29 , 45 }, // hyphen + { 30 , 47 }, // slash + { 31 ,0x0607 }, // backslash + { 32 , 126 }, // tilde + { 33 ,0x0611 }, // doubleline + { 34 ,0x0609 }, // line + { 37 ,0x041d }, // left apostrophe + { 38 ,0x041c }, // right apostrophe + { 39 ,0x0420 }, // left quote + { 40 ,0x041f }, // right quote + { 41 , 40 }, // left paren + { 42 , 41 }, // right paren + { 45 , 91 }, // left bracket + { 46 , 93 }, // right bracket + { 47 , 123 }, // left curly bracket + { 48 , 125 }, // right curly bracket + { 53 ,0x0b01 }, // left j quote + { 54 ,0x0b02 }, // right j quote + { 59 , 43 }, // plus + { 60 ,0x0600 }, // minus + { 61 ,0x0601 }, // plus/minus + { 62 ,0x0627 }, // times + { 63 ,0x0608 }, // divide + { 64 , 61 }, // equal + { 65 ,0x0663 }, // unequal + { 66 , 60 }, // less + { 67 , 62 }, // greater + { 68 ,0x0602 }, // less/equal + { 69 ,0x0603 }, // greater/equal + { 70 ,0x0613 }, // infinity + { 71 ,0x0666 }, // traingle dots + { 72 ,0x0504 }, // man + { 73 ,0x0505 }, // woman + { 75 ,0x062d }, // prime + { 76 ,0x062e }, // double prime + { 78 ,0x040c }, // yen + { 79 , 36 }, // $ + { 80 ,0x0413 }, // cent + { 81 ,0x040b }, // pound + { 82 , 37 }, // % + { 83 , 35 }, // # + { 84 , 38 }, // & + { 85 , 42 }, // * + { 86 , 64 }, // @ + { 87 ,0x0406 }, // squiggle + { 89 ,0x06b8 }, // filled star + { 90 ,0x0425 }, // hollow circle + { 91 ,0x042c }, // filled circle + { 93 ,0x065f }, // hollow diamond + { 94 ,0x0660 }, // filled diamond + { 95 ,0x0426 }, // hollow box + { 96 ,0x042e }, // filled box + { 97 ,0x0688 }, // hollow triangle + { 99 ,0x0689 }, // hollow upside down triangle + { 103,0x0615 }, // right arrow + { 104,0x0616 }, // left arrow + { 105,0x0617 }, // up arrow + { 106,0x0622 }, // down arrow + { 119,0x060f }, + { 121,0x0645 }, + { 122,0x0646 }, + { 123,0x0643 }, + { 124,0x0644 }, + { 125,0x0642 }, // union + { 126,0x0610 }, // intersection + { 135,0x0655 }, + { 136,0x0656 }, + { 138,0x0638 }, // right arrow + { 139,0x063c }, // left/right arrow + { 140,0x067a }, + { 141,0x0679 }, + { 153,0x064f }, // angle + { 154,0x0659 }, + { 155,0x065a }, + { 156,0x062c }, + { 157,0x062b }, + { 158,0x060e }, + { 159,0x06b0 }, + { 160,0x064d }, + { 161,0x064e }, + { 162,0x050e }, // square root + { 164,0x0604 }, + { 175,0x0623 }, // angstrom + { 176,0x044b }, // percent + { 177,0x051b }, // sharp + { 178,0x051c }, // flat + { 179,0x0509 }, // musical note + { 180,0x0427 }, // dagger + { 181,0x0428 }, // double dagger + { 182,0x0405 }, // paragraph + { 187,0x068f } // big hollow circle +}; + +/**************************************************************************** +Desc: Maps CS26 to CharSet 11 + Used to uncollate characters for FLAIM - placed here for consistency + 0x80 - add dakuten + 0xC0 - add handakuten + 0xFF - no mapping exists +****************************************************************************/ +FLMBYTE MapCS26ToCharSet11[ 86] = +{ + 0x06, // 0 a + 0x10, // 1 A + 0x07, // 2 i + 0x11, // 3 I + 0x08, // 4 u + 0x12, // 5 U + 0x09, // 6 e + 0x13, // 7 E + 0x0a, // 8 o + 0x14, // 9 O + + 0x15, // 0x0a KA + 0x95, // GA - 21 followed by 0x3D dakuten + + 0x16, // 0x0c KI + 0x96, // GI + 0x17, // 0x0e KU + 0x97, // GU + 0x18, // 0x10 KE + 0x98, // GE + 0x19, // 0x12 KO + 0x99, // GO + + 0x1a, // 0x14 SA + 0x9a, // ZA + 0x1b, // 0x16 SHI + 0x9b, // JI + 0x1c, // 0x18 SU + 0x9c, // ZU + 0x1d, // 0x1a SE + 0x9d, // ZE + 0x1e, // 0x1c SO + 0x9e, // ZO + + 0x1f, // 0x1e TA + 0x9f, // DA + 0x20, // 0x20 CHI + 0xa0, // JI + 0x0e, // 0x22 small tsu + 0x21, // 0x23 TSU + 0xa1, // ZU + 0x22, // 0x25 TE + 0xa2, // DE + 0x23, // 0x27 TO + 0xa3, // DO + + 0x24, // 0x29 NA + 0x25, // 0x2a NI + 0x26, // 0x2b NU + 0x27, // 0x2c NE + 0x28, // 0x2d NO + + 0x29, // 0x2e HA + 0xa9, // 0x2f BA + 0xe9, // 0x30 PA + 0x2a, // 0x31 HI + 0xaa, // 0x32 BI + 0xea, // 0x33 PI + 0x2b, // 0x34 FU + 0xab, // 0x35 BU + 0xeb, // 0x36 PU + 0x2c, // 0x37 HE + 0xac, // 0x38 BE + 0xec, // 0x39 PE + 0x2d, // 0x3a HO + 0xad, // 0x3b BO + 0xed, // 0x3c PO + + 0x2e, // 0x3d MA + 0x2f, // 0x3e MI + 0x30, // 0x3f MU + 0x31, // 0x40 ME + 0x32, // 0x41 MO + + 0x0b, // 0x42 small ya + 0x33, // 0x43 YA + 0x0c, // 0x44 small yu + 0x34, // 0x45 YU + 0x0d, // 0x46 small yo + 0x35, // 0x47 YO + + 0x36, // 0x48 RA + 0x37, // 0x49 RI + 0x38, // 0x4a RU + 0x39, // 0x4b RE + 0x3a, // 0x4c RO + + 0xff, // 0x4d small wa + 0x3b, // 0x4e WA + 0xff, // 0x4f WI + 0xff, // 0x50 WE + 0x05, // 0x51 WO + + 0x3c, // 0x52 N + 0xff, // 0x53 VU + 0xff, // 0x54 ka + 0xff // 0x55 ke +}; + +/**************************************************************************** +Desc: Conversion from single (Hankaku) to double (Zenkaku) wide characters + Used in flmWPHanToZenkaku() + Maps from charset 11 to CS24 (punctuation) (starting from 11,0) +****************************************************************************/ +FLMBYTE From0AToZen[] = // ' changed because of windows +{ + 0, 9, 40, 0x53, // sp ! " # + 0x4f, 0x52, 0x54, 38, // $ % & ' + // Was 187 for ! and 186 for ' + 0x29, 0x2a, 0x55, 0x3b, // ( ) * + + 3, 0x1d, 4, 0x1e // , - . / +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From0BToZen[] = +{ + 6, 7, 0x42, 0x40, // : ; < = + 0x43, 8, 0x56 // > ? @ +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From0CToZen[] = +{ + 0x2d, 0x1f, 0x2e, 0x0f, 0x11, 0x0d // [ BACKSLASH ] ^ _ ` +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From0DToZen[] = +{ + 0x2f, 0x22, 0x30, 0x20 // { | } ~ +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From8ToZen[] = +{ + 0x5e, 0x7e, 0x5f, 0x7f, 0x5f, 0xFF, 0x60, 0x80, + 0x61, 0x81, 0x62, 0x82, 0x63, 0x83, 0x64, 0x84, + 0x65, 0x85, 0x66, 0x86, 0x67, 0x87, 0x68, 0x88, + 0x69, 0x89, 0x6a, 0x8a, 0x6b, 0x8b, 0x6c, 0x8c, + 0x6d, 0x8d, 0x6e, 0x8e, 0x6f, 0x8f, 0x6f, 0xFF, + 0x70, 0x90, 0x71, 0x91, 0x72, 0x92, 0x73, 0x93, + 0x74, 0x94, 0x75, 0x95 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From11AToZen[] = // 11 to 24 punctuation except dash +{ + 2, // japanese period + 0x35, // left bracket + 0x36, // right bracket + 0x01, // comma + 0x05 // chuuten +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE From11BToZen[] = // 11 to 26 (katakana) from 11,5 +{ + 0x51, // wo + 0,2,4,6,8,0x42,0x44,0x46,0x22, // small a i u e o ya yu yo tsu + 0xFF, 1, 3, 5, 7, 9, // dash (x241b) a i u e o + 0x0a, 0x0c, 0x0e, 0x10, 0x12, // ka ki ku ke ko + 0x14, 0x16, 0x18, 0x1a, 0x1c, // sa shi su se so + 0x1e, 0x20, 0x23, 0x25, 0x27, // ta chi tsu te to + 0x29, 0x2a, 0x2b, 0x2c, 0x2d, // na ni nu ne no + 0x2e, 0x31, 0x34, 0x37, 0x3a, // ha hi fu he ho + 0x3d, 0x3e, 0x3f, 0x40, 0x41, // ma mi mu me mo + 0x43, 0x45, 0x47, // ya yu yo + 0x48, 0x49, 0x4a, 0x4b, 0x4c, // ra ri ru re ro + 0x4e, 0x52 // WA N +}; // does not have wa WI WE VU ka ke + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 fwp_indexi[] = +{ + 0,11,14,15,17,18,19,21,22,23,24,25,26,35,59 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 fwp_indexj[] = // DOUBLE CHAR AREA - LANGUAGES +{ + XFLM_CA_LANG, // Catalan (0) + XFLM_CF_LANG, // Canadian French + XFLM_CZ_LANG, // Czech + XFLM_SL_LANG, // Slovak + XFLM_DE_LANG, // German + XFLM_SD_LANG, // Swiss German + XFLM_ES_LANG, // Spanish (Spain) + XFLM_FR_LANG, // French + XFLM_NL_LANG, // Netherlands + 0xFFFF, // DK_LANG, Danish - support for 'aa' -> a-ring out + 0xFFFF, // NO_LANG, Norwegian - support for 'aa' -> a-ring out + 0x0063, // c - DOUBLE CHARACTERS - STATE ENTRIES + 0x006c, // l + 0x0197, // l with center dot + 0x0063, // c + 0x0125, // ae digraph + 0x01a7, // oe digraph + 0x0068, // h + 0x0068, // h + 0x006c, // l + 0x0101, // center dot alone + 0x006c, // l + 0x0117, // ? (for German) + 0x018b, // ij digraph + 0x0000, // was 'a' - will no longer map 'aa' to a-ring + 0x0000, // was 'a' + + XFLM_CZ_LANG, // SINGLE CHARS - LANGUAGES + XFLM_DK_LANG, + XFLM_NO_LANG, + XFLM_SL_LANG, + XFLM_TK_LANG, + XFLM_SU_LANG, + XFLM_IS_LANG, + XFLM_SV_LANG, + XFLM_YK_LANG, + // SINGLE CHARS + 0x011e, // A Diaeresis - alternate collating sequences + 0x011f, // a Diaeresis + 0x0122, // A Ring - 2 + 0x0123, // a Ring + 0x0124, // AE Diagraph - 4 + 0x0125, // ae diagraph + 0x013e, // O Diaeresis - 6 + 0x013f, // o Diaeresis + 0x0146, // U Diaeresis - 8 + 0x0147, // u Diaeresis + 0x0150, // O Slash - 10 + 0x0151, // o Slash + + 0x0A3a, // CYRILLIC SOFT SIGN - 12 + 0x0A3b, // CYRILLIC soft sign + 0x01ee, // dotless i - turkish - 14 + 0x01ef, // dotless I - turkish + 0x0162, // C Hacek/caron - 1,98 - 16 + 0x0163, // c Hacek/caron - 1,99 + 0x01aa, // R Hacek/caron - 1,170 - 18 + 0x01ab, // r Hacek/caron - 1,171 + 0x01b0, // S Hacek/caron - 1,176 - 20 + 0x01b1, // s Hacek/caron - 1,177 + 0x01ce, // Z Hacek/caron - 1,206 - 22 + 0x01cf, // z Hacek/caron - 1,207 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 fwp_valuea[] = +{ +// DOUBLE CHAR STATE VALUES + STATE1, // 00 + STATE3, + STATE2, + STATE2, + STATE8, + STATE8, + STATE1, + STATE3, + STATE9, + STATE10, // No longer in use + STATE10, // No longer in use + STATE4, + STATE6, + STATE6, + STATE5, + INSTAE, + INSTOE, + AFTERC, + AFTERH, + AFTERL, + STATE7, + STATE6, + INSTSG, // ss for German + INSTIJ, + STATE11, // aa - no longer in use + WITHAA, // aa - no longer in use + +// SINGLE CHARS - LANGUAGES + START_CZ, // Czech + START_DK, // Danish + START_NO, // Norwegian + START_SL, // Slovak + START_TK, // Turkish + START_SU, // Finnish + START_IS, // Icelandic + START_SV, // Swedish + START_YK, // Ukrainian + +// SINGLE CHARS FIXUP AREAS + COLS9, COLS9, COLS9, COLS9, // US & OTHERS + COLS9+1, COLS9+1, COLS9+21, COLS9+21, + COLS9+30, COLS9+30, COLS9+21, COLS9+21, + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9+45, COLS9+45, COLS9+55, COLS9+55, // DANISH + COLS9+42, COLS9+42, COLS9+53, COLS9+53, + COLS9+30, COLS9+30, COLS9+49, COLS9+49, // Oct98 U Diaer no longer to y Diaer + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9, COLS9, COLS9, COLS9, // Icelandic + COLS9+46, COLS9+46, COLS9+50, COLS9+50, + COLS9+30, COLS9+30, COLS9+54, COLS9+54, + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9, COLS9, COLS9+51, COLS9+51, // Norwegian + COLS9+43, COLS9+43, COLS9+21, COLS9+21, + COLS9+30, COLS9+30, COLS9+47, COLS9+47, + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9+48, COLS9+48, COLS9+44, COLS9+44, // Finnish/Swedish + COLS9+1, COLS9+1, COLS9+52, COLS9+52, + COLS9+30, COLS9+30, COLS9+21, COLS9+21, // Oct98 U Diaer no longer to y Diaer + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9, COLS9, COLS9, COLS9, // Ukrain + COLS9+1, COLS9+1, COLS9+21, COLS9+21, + COLS9+30, COLS9+30, COLS9+21, COLS9+21, + COLS10+48, COLS10+48, COLS9+12, COLS9+12, + COLS9+3, COLS9+3, COLS9+25, COLS9+25, + COLS9+27, COLS9+27, COLS9+35, COLS9+35, + + COLS9, COLS9, COLS9, COLS9, // Turkish + COLS9+1, COLS9+1, COLS9+21, COLS9+21, + COLS9+30, COLS9+30, COLS9+21, COLS9+21, + COLS9+43, COLS9+43, COLS9+11, COLS9+11, // dotless i same as + COLS9+3, COLS9+3, COLS9+25, COLS9+25, // the "CH" in Czech + COLS9+27, COLS9+27, COLS9+35, COLS9+35, // works because char + // fails brkcar() + + COLS9, COLS9, COLS9, COLS9, // Czech / Slovak + COLS9+1, COLS9+1, COLS9+21, COLS9+21, + COLS9+30, COLS9+30, COLS9+21, COLS9+21, + COLS10+43, COLS10+43, COLS9+12, COLS9+12, + COLS9+5, COLS9+5, COLS9+26, COLS9+26, // carons + COLS9+28, COLS9+28, COLS9+36, COLS9+36 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_asc60Tbl[ ASCTBLLEN + 2] = +{ + 0x20, // initial character offset!! + ASCTBLLEN, // len of this table + COLLS, // + COLLS+5, // ! + COLS1, // " + COLS6+1, // # + COLS3, // $ + COLS6, // % + COLS6+2, // & + COLS1+1, // ' + COLS2, // ( + COLS2+1, // ) + COLS4+2, // * + COLS4, // + + COLLS+2, // , + COLS4+1, // - + COLLS+1, // . + COLS4+3, // / + COLS8, // 0 + COLS8+1, // 1 + COLS8+2, // 2 + COLS8+3, // 3 + COLS8+4, // 4 + COLS8+5, // 5 + COLS8+6, // 6 + COLS8+7, // 7 + COLS8+8, // 8 + COLS8+9, // 9 + COLLS+3, // : + COLLS+4, // ; + COLS5, // < + COLS5+2, // = + COLS5+4, // > + COLLS+7, // ? + COLS6+3, // @ + COLS9, // A + COLS9+2, // B + COLS9+3, // C + COLS9+6, // D + COLS9+7, // E + COLS9+8, // F + COLS9+9, // G + COLS9+10, // H + COLS9+12, // I + COLS9+14, // J + COLS9+15, // K + COLS9+16, // L + COLS9+18, // M + COLS9+19, // N + COLS9+21, // O + COLS9+23, // P + COLS9+24, // Q + COLS9+25, // R + COLS9+27, // S + COLS9+29, // T + COLS9+30, // U + COLS9+31, // V + COLS9+32, // W + COLS9+33, // X + COLS9+34, // Y + COLS9+35, // Z + COLS9+40, // [ (note: alphabetic - end of list) + COLS6+4, // Backslash + COLS9+41, // ] (note: alphabetic - end of list) + COLS4+4, // ^ + COLS6+5, // _ + COLS1+2, // ` + COLS9, // a + COLS9+2, // b + COLS9+3, // c + COLS9+6, // d + COLS9+7, // e + COLS9+8, // f + COLS9+9, // g + COLS9+10, // h + COLS9+12, // i + COLS9+14, // j + COLS9+15, // k + COLS9+16, // l + COLS9+18, // m + COLS9+19, // n + COLS9+21, // o + COLS9+23, // p + COLS9+24, // q + COLS9+25, // r + COLS9+27, // s + COLS9+29, // t + COLS9+30, // u + COLS9+31, // v + COLS9+32, // w + COLS9+33, // x + COLS9+34, // y + COLS9+35, // z + COLS2+4, // { + COLS6+6, // | + COLS2+5, // } + COLS6+7 // ~ +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_mn60Tbl[ MNTBLLEN + 2] = // multinational table +{ + 23, // initial character offset!! + MNTBLLEN, // len of this table + COLS9+27, // German Double s + COLS9+15, // Icelandic k + COLS9+14, // Dotless j + +// IBM Charset + + COLS9, // A Acute + COLS9, // a Acute + COLS9, // A Circumflex + COLS9, // a Circumflex + COLS9, // A Diaeresis or Umlaut + COLS9, // a Diaeresis or Umlaut + COLS9, // A Grave + COLS9, // a Grave + COLS9, // A Ring + COLS9, // a Ring + COLS9+1, // AE digraph + COLS9+1, // ae digraph + COLS9+3, // C Cedilla + COLS9+3, // c Cedilla + COLS9+7, // E Acute + COLS9+7, // e Acute + COLS9+7, // E Circumflex + COLS9+7, // e Circumflex + COLS9+7, // E Diaeresis or Umlaut + COLS9+7, // e Diaeresis or Umlaut + COLS9+7, // E Grave + COLS9+7, // e Grave + COLS9+12, // I Acute + COLS9+12, // i Acute + COLS9+12, // I Circumflex + COLS9+12, // i Circumflex + COLS9+12, // I Diaeresis or Umlaut + COLS9+12, // i Diaeresis or Umlaut + COLS9+12, // I Grave + COLS9+12, // i Grave + COLS9+20, // N Tilde + COLS9+20, // n Tilde + COLS9+21, // O Acute + COLS9+21, // o Acute + COLS9+21, // O Circumflex + COLS9+21, // o Circumflex + COLS9+21, // O Diaeresis or Umlaut + COLS9+21, // o Diaeresis or Umlaut + COLS9+21, // O Grave + COLS9+21, // o Grave + COLS9+30, // U Acute + COLS9+30, // u Acute + COLS9+30, // U Circumflex + COLS9+30, // u Circumflex + COLS9+30, // U Diaeresis or Umlaut + COLS9+30, // u Diaeresis or Umlaut + COLS9+30, // U Grave + COLS9+30, // u Grave + COLS9+34, // Y Diaeresis or Umlaut + COLS9+34, // y Diaeresis or Umlaut + +// IBM foreign + + COLS9, // A Tilde + COLS9, // a Tilde + COLS9+6, // D Cross Bar + COLS9+6, // d Cross Bar + COLS9+21, // O Slash + COLS9+21, // o Slash + COLS9+21, // O Tilde + COLS9+21, // o Tilde + COLS9+34, // Y Acute + COLS9+34, // y Acute + COLS9+6, // Uppercase Eth + COLS9+6, // Lowercase Eth + COLS9+37, // Uppercase Thorn + COLS9+37, // Lowercase Thorn + +// Teletex chars + + COLS9, // A Breve + COLS9, // a Breve + COLS9, // A Macron + COLS9, // a Macron + COLS9, // A Ogonek + COLS9, // a Ogonek + COLS9+3, // C Acute + COLS9+3, // c Acute + COLS9+3, // C Caron or Hachek + COLS9+3, // c Caron or Hachek + COLS9+3, // C Circumflex + COLS9+3, // c Circumflex + COLS9+3, // C Dot Above + COLS9+3, // c Dot Above + COLS9+6, // D Caron or Hachek (Apostrophe Beside) + COLS9+6, // d Caron or Hachek (Apostrophe Beside) + COLS9+7, // E Caron or Hachek + COLS9+7, // e Caron or Hachek + COLS9+7, // E Dot Above + COLS9+7, // e Dot Above + COLS9+7, // E Macron + COLS9+7, // e Macron + COLS9+7, // E Ogonek + COLS9+7, // e Ogonek + COLS9+9, // G Acute + COLS9+9, // g Acute + COLS9+9, // G Breve + COLS9+9, // g Breve + COLS9+9, // G Caron or Hachek + COLS9+9, // g Caron or Hachek + COLS9+9, // G Cedilla (Apostrophe Under) + COLS9+9, // g Cedilla (Apostrophe Over) + COLS9+9, // G Circumflex + COLS9+9, // g Circumflex + COLS9+9, // G Dot Above + COLS9+9, // g Dot Above + COLS9+10, // H Circumflex + COLS9+10, // h Circumflex + COLS9+10, // H Cross Bar + COLS9+10, // h Cross Bar + COLS9+12, // I Dot Above (Sharp Accent) + COLS9+12, // i Dot Above (Sharp Accent) + COLS9+12, // I Macron + COLS9+12, // i Macron + COLS9+12, // I Ogonek + COLS9+12, // i Ogonek + COLS9+12, // I Tilde + COLS9+12, // i Tilde + COLS9+13, // IJ Digraph + COLS9+13, // ij Digraph + COLS9+14, // J Circumflex + COLS9+14, // j Circumflex + COLS9+15, // K Cedilla (Apostrophe Under) + COLS9+15, // k Cedilla (Apostrophe Under) + COLS9+16, // L Acute + COLS9+16, // l Acute + COLS9+16, // L Caron or Hachek (Apostrophe Beside) + COLS9+16, // l Caron or Hachek (Apostrophe Beside) + COLS9+16, // L Cedilla (Apostrophe Under) + COLS9+16, // l Cedilla (Apostrophe Under) + COLS9+16, // L Center Dot + COLS9+16, // l Center Dot + COLS9+16, // L Stroke + COLS9+16, // l Stroke + COLS9+19, // N Acute + COLS9+19, // n Acute + COLS9+19, // N Apostrophe + COLS9+19, // n Apostrophe + COLS9+19, // N Caron or Hachek + COLS9+19, // n Caron or Hachek + COLS9+19, // N Cedilla (Apostrophe Under) + COLS9+19, // n Cedilla (Apostrophe Under) + COLS9+21, // O Double Acute + COLS9+21, // o Double Acute + COLS9+21, // O Macron + COLS9+21, // o Macron + COLS9+22, // OE digraph + COLS9+22, // oe digraph + COLS9+25, // R Acute + COLS9+25, // r Acute + COLS9+25, // R Caron or Hachek + COLS9+25, // r Caron or Hachek + COLS9+25, // R Cedilla (Apostrophe Under) + COLS9+25, // r Cedilla (Apostrophe Under) + COLS9+27, // S Acute + COLS9+27, // s Acute + COLS9+27, // S Caron or Hachek + COLS9+27, // s Caron or Hachek + COLS9+27, // S Cedilla + COLS9+27, // s Cedilla + COLS9+27, // S Circumflex + COLS9+27, // s Circumflex + COLS9+29, // T Caron or Hachek (Apostrophe Beside) + COLS9+29, // t Caron or Hachek (Apostrophe Beside) + COLS9+29, // T Cedilla (Apostrophe Under) + COLS9+29, // t Cedilla (Apostrophe Under) + COLS9+29, // T Cross Bar + COLS9+29, // t Cross Bar + COLS9+30, // U Breve + COLS9+30, // u Breve + COLS9+30, // U Double Acute + COLS9+30, // u Double Acute + COLS9+30, // U Macron + COLS9+30, // u Macron + COLS9+30, // U Ogonek + COLS9+30, // u Ogonek + COLS9+30, // U Ring + COLS9+30, // u Ring + COLS9+30, // U Tilde + COLS9+30, // u Tilde + COLS9+32, // W Circumflex + COLS9+32, // w Circumflex + COLS9+34, // Y Circumflex + COLS9+34, // y Circumflex + COLS9+35, // Z Acute + COLS9+35, // z Acute + COLS9+35, // Z Caron or Hachek + COLS9+35, // z Caron or Hachek + COLS9+35, // Z Dot Above + COLS9+35, // z Dot Above + COLS9+19, // Uppercase Eng + COLS9+19, // Lowercase Eng + +// Other + + COLS9+6, // D Macron + COLS9+6, // d Macron + COLS9+16, // L Macron + COLS9+16, // l Macron + COLS9+19, // N Macron + COLS9+19, // n Macron + COLS9+25, // R Grave + COLS9+25, // r Grave + COLS9+27, // S Macron + COLS9+27, // s Macron + COLS9+29, // T Macron + COLS9+29, // t Macron + COLS9+34, // Y Breve + COLS9+34, // y Breve + COLS9+34, // Y Grave + COLS9+34, // y Grave + COLS9+6, // D Apostrophe Beside + COLS9+6, // d Apostrophe Beside + COLS9+21, // O Apostrophe Beside + COLS9+21, // o Apostrophe Beside + COLS9+30, // U Apostrophe Beside + COLS9+30, // u Apostrophe Beside + COLS9+7, // E breve + COLS9+7, // e breve + COLS9+12, // I breve + COLS9+12, // i breve + COLS9+12, // dotless I + COLS9+12, // dotless i + COLS9+21, // O breve + COLS9+21 // o breve +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_sym60Tbl[ SYMTBLLEN + 2] = +{ + 11, // initial character offset!! + SYMTBLLEN, // len of this table + COLS3+2, // pound + COLS3+3, // yen + COLS3+4, // pacetes + COLS3+5, // floren + COLS0, + COLS0, + COLS0, + COLS0, + COLS3+1, // cent +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_grk60Tbl[ GRKTBLLEN + 2] = +{ + 0, // starting offset + GRKTBLLEN, // length + COLS7, // Uppercase Alpha + COLS7, // Lowercase Alpha + COLS7+1, // Uppercase Beta + COLS7+1, // Lowercase Beta + COLS7+1, // Uppercase Beta Medial + COLS7+1, // Lowercase Beta Medial + COLS7+2, // Uppercase Gamma + COLS7+2, // Lowercase Gamma + COLS7+3, // Uppercase Delta + COLS7+3, // Lowercase Delta + COLS7+4, // Uppercase Epsilon + COLS7+4, // Lowercase Epsilon + COLS7+5, // Uppercase Zeta + COLS7+5, // Lowercase Zeta + COLS7+6, // Uppercase Eta + COLS7+6, // Lowercase Eta + COLS7+7, // Uppercase Theta + COLS7+7, // Lowercase Theta + COLS7+8, // Uppercase Iota + COLS7+8, // Lowercase Iota + COLS7+9, // Uppercase Kappa + COLS7+9, // Lowercase Kappa + COLS7+10, // Uppercase Lambda + COLS7+10, // Lowercase Lambda + COLS7+11, // Uppercase Mu + COLS7+11, // Lowercase Mu + COLS7+12, // Uppercase Nu + COLS7+12, // Lowercase Nu + COLS7+13, // Uppercase Xi + COLS7+13, // Lowercase Xi + COLS7+14, // Uppercase Omicron + COLS7+14, // Lowercase Omicron + COLS7+15, // Uppercase Pi + COLS7+15, // Lowercase Pi + COLS7+16, // Uppercase Rho + COLS7+16, // Lowercase Rho + COLS7+17, // Uppercase Sigma + COLS7+17, // Lowercase Sigma + COLS7+17, // Uppercase Sigma Terminal + COLS7+17, // Lowercase Sigma Terminal + COLS7+18, // Uppercase Tau + COLS7+18, // Lowercase Tau + COLS7+19, // Uppercase Upsilon + COLS7+19, // Lowercase Upsilon + COLS7+20, // Uppercase Phi + COLS7+20, // Lowercase Phi + COLS7+21, // Uppercase Chi + COLS7+21, // Lowercase Chi + COLS7+22, // Uppercase Psi + COLS7+22, // Lowercase Psi + COLS7+23, // Uppercase Omega + COLS7+23, // Lowercase Omega + +// Other Modern Greek Characters [8,52] + + COLS7, // Uppercase ALPHA Tonos high prime + COLS7, // Lowercase Alpha Tonos - acute + COLS7+4, // Uppercase EPSILON Tonos - high prime + COLS7+4, // Lowercase Epslion Tonos - acute + COLS7+6, // Uppercase ETA Tonos - high prime + COLS7+6, // Lowercase Eta Tonos - acute + COLS7+8, // Uppercase IOTA Tonos - high prime + COLS7+8, // Lowercase iota Tonos - acute + COLS7+8, // Uppercase IOTA Diaeresis + COLS7+8, // Lowercase iota diaeresis + COLS7+14, // Uppercase OMICRON Tonos - high prime + COLS7+14, // Lowercase Omicron Tonos - acute + COLS7+19, // Uppercase UPSILON Tonos - high prime + COLS7+19, // Lowercase Upsilon Tonos - acute + COLS7+19, // Uppercase UPSILON Diaeresis + COLS7+19, // Lowercase Upsilon diaeresis + COLS7+23, // Uppercase OMEGA Tonos - high prime + COLS7+23, // Lowercase Omega Tonso - acute + +// Variants [8,70] + + COLS7+4, // epsilon (variant) + COLS7+7, // theta (variant) + COLS7+9, // kappa (variant) + COLS7+15, // pi (variant) + COLS7+16, // rho (variant) + COLS7+17, // sigma (variant) + COLS7+19, // upsilon (variant) + COLS7+20, // phi (variant) + COLS7+23, // omega (variant) + +// Greek Diacritic marks [8,79] + + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, + COLS0, // 8,108 end of diacritic marks + +// Ancient Greek [8,109] + + COLS7, // alpha grave + COLS7, // alpha circumflex + COLS7, // alpha w/iota + COLS7, // alpha acute w/iota + COLS7, // alpha grave w/iota + COLS7, // alpha circumflex w/Iota + COLS7, // alpha smooth + COLS7, // alpha smooth acute + COLS7, // alpha smooth grave + COLS7, // alpha smooth circumflex + COLS7, // alpha smooth w/Iota + COLS7, // alpha smooth acute w/Iota + COLS7, // alpha smooth grave w/Iota + COLS7, // alpha smooth circumflex w/Iota +// [8,123] + COLS7, // alpha rough + COLS7, // alpha rough acute + COLS7, // alpha rough grave + COLS7, // alpha rough circumflex + COLS7, // alpha rough w/Iota + COLS7, // alpha rough acute w/Iota + COLS7, // alpha rough grave w/Iota + COLS7, // alpha rough circumflex w/Iota +// [8,131] + COLS7+4, // epsilon grave + COLS7+4, // epsilon smooth + COLS7+4, // epsilon smooth acute + COLS7+4, // epsilon smooth grave + COLS7+4, // epsilon rough + COLS7+4, // epsilon rough acute + COLS7+4, // epsilon rough grave +// [8,138] + COLS7+6, // eta grave + COLS7+6, // eta circumflex + COLS7+6, // eta w/iota + COLS7+6, // eta acute w/iota + COLS7+6, // eta grave w/Iota + COLS7+6, // eta circumflex w/Iota + COLS7+6, // eta smooth + COLS7+6, // eta smooth acute + COLS7+6, // eta smooth grave + COLS7+6, // eta smooth circumflex + COLS7+6, // eta smooth w/Iota + COLS7+6, // eta smooth acute w/Iota + COLS7+6, // eta smooth grave w/Iota + COLS7+6, // eta smooth circumflex w/Iota + COLS7+6, // eta rough + COLS7+6, // eta rough acute + COLS7+6, // eta rough grave + COLS7+6, // eta rough circumflex + COLS7+6, // eta rough w/Iota + COLS7+6, // eta rough acute w/Iota + COLS7+6, // eta rough grave w/Iota + COLS7+6, // eta rough circumflex w/Iota +// [8,160] + COLS7+8, // iota grave + COLS7+8, // iota circumflex + COLS7+8, // iota acute diaeresis + COLS7+8, // iota grave diaeresis + COLS7+8, // iota smooth + COLS7+8, // iota smooth acute + COLS7+8, // iota smooth grave + COLS7+8, // iota smooth circumflex + COLS7+8, // iota rough + COLS7+8, // iota rough acute + COLS7+8, // iota rough grave + COLS7+8, // iota rough circumflex +// [8,172] + COLS7+14, // omicron grave + COLS7+14, // omicron smooth + COLS7+14, // omicron smooth acute + COLS7+14, // omicron smooth grave + COLS7+14, // omicron rough + COLS7+14, // omicron rough acute + COLS7+14, // omicron rough grave +// [8,179] + COLS7+16, // rho smooth + COLS7+16, // rho rough +// [8,181] + COLS7+19, // upsilon grave + COLS7+19, // upsilon circumflex + COLS7+19, // upsilon acute diaeresis + COLS7+19, // upsilon grave diaeresis + COLS7+19, // upsilon smooth + COLS7+19, // upsilon smooth acute + COLS7+19, // upsilon smooth grave + COLS7+19, // upsilon smooth circumflex + COLS7+19, // upsilon rough + COLS7+19, // upsilon rough acute + COLS7+19, // upsilon rough grave + COLS7+19, // upsilon rough circumflex +// [8,193] + COLS7+23, // omega grave + COLS7+23, // omega circumflex + COLS7+23, // omega w/Iota + COLS7+23, // omega acute w/Iota + COLS7+23, // omega grave w/Iota + COLS7+23, // omega circumflex w/Iota + COLS7+23, // omega smooth + COLS7+23, // omega smooth acute + COLS7+23, // omega smooth grave + COLS7+23, // omega smooth circumflex + COLS7+23, // omega smooth w/Iota + COLS7+23, // omega smooth acute w/Iota + COLS7+23, // omega smooth grave w/Iota + COLS7+23, // omega smooth circumflex w/Iota + COLS7+23, // omega rough + COLS7+23, // omega rough acute + COLS7+23, // omega rough grave + COLS7+23, // omega rough circumflex + COLS7+23, // omega rough w/Iota + COLS7+23, // omega rough acute w/Iota + COLS7+23, // omega rough grave w/Iota + COLS7+23, // omega rough circumflex w/Iota +// [8,215] + COLS7+24, // Uppercase Stigma--the number 6 + COLS7+24, // Uppercase Digamma--Obsolete letter used as 6 + COLS7+24, // Uppercase Koppa--Obsolete letter used as 90 + COLS7+24 // Uppercase Sampi--Obsolete letter used as 900 +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_cyrl60Tbl[ CYRLTBLLEN + 2] = +{ + 0, // starting offset + CYRLTBLLEN, // len of table + + COLS10, // Russian uppercase A + COLS10, // Russian lowercase A + COLS10+1, // Russian uppercase BE + COLS10+1, // Russian lowercase BE + COLS10+2, // Russian uppercase VE + COLS10+2, // Russian lowercase VE + COLS10+3, // Russian uppercase GHE + COLS10+3, // Russian lowercase GHE + COLS10+5, // Russian uppercase DE + COLS10+5, // Russian lowercase DE + + COLS10+8, // Russian uppercase E + COLS10+8, // Russian lowercase E + COLS10+9, // Russian lowercase YO + COLS10+9, // Russian lowercase YO + COLS10+11, // Russian uppercase ZHE + COLS10+11, // Russian lowercase ZHE + COLS10+12, // Russian uppercase ZE + COLS10+12, // Russian lowercase ZE + COLS10+14, // Russian uppercase I + COLS10+14, // Russian lowercase I + + COLS10+17, // Russian uppercase SHORT I + COLS10+17, // Russian lowercase SHORT I + COLS10+19, // Russian uppercase KA + COLS10+19, // Russian lowercase KA + COLS10+20, // Russian uppercase EL + COLS10+20, // Russian lowercase EL + COLS10+22, // Russian uppercase EM + COLS10+22, // Russian lowercase EM + COLS10+23, // Russian uppercase EN + COLS10+23, // Russian lowercase EN + + COLS10+25, // Russian uppercase O + COLS10+25, // Russian lowercase O + COLS10+26, // Russian uppercase PE + COLS10+26, // Russian lowercase PE + COLS10+27, // Russian uppercase ER + COLS10+27, // Russian lowercase ER + COLS10+28, // Russian uppercase ES + COLS10+28, // Russian lowercase ES + COLS10+29, // Russian uppercase TE + COLS10+29, // Russian lowercase TE + + COLS10+32, // Russian uppercase U + COLS10+32, // Russian lowercase U + COLS10+34, // Russian uppercase EF + COLS10+34, // Russian lowercase EF + COLS10+35, // Russian uppercase HA + COLS10+35, // Russian lowercase HA + COLS10+36, // Russian uppercase TSE + COLS10+36, // Russian lowercase TSE + COLS10+37, // Russian uppercase CHE + COLS10+37, // Russian lowercase CHE + + COLS10+39, // Russian uppercase SHA + COLS10+39, // Russian lowercase SHA + COLS10+40, // Russian uppercase SHCHA + COLS10+40, // Russian lowercase SHCHA + COLS10+41, // Russian lowercase ER (also hard sign) + COLS10+41, // Russian lowercase ER (also hard sign) + COLS10+42, // Russian lowercase ERY + COLS10+42, // Russian lowercase ERY + COLS10+43, // Russian lowercase SOFT SIGN + COLS10+43, // Russian lowercase SOFT SIGN + + COLS10+45, // Russian uppercase REVERSE E + COLS10+45, // Russian lowercase REVERSE E + COLS10+46, // Russian uppercase YU + COLS10+46, // Russian lowercase yu + COLS10+47, // Russian uppercase YA + COLS10+47, // Russian lowercase ya + + COLS0, // Russian uppercase EH + COLS0, // Russian lowercase eh + COLS10+7, // Macedonian uppercase SOFT DJ + COLS10+7, // Macedonian lowercase soft dj + + COLS10+4, // Ukrainian uppercase HARD G + COLS10+4, // Ukrainian lowercase hard g + COLS0, // GE bar + COLS0, // ge bar + COLS10+6, // Serbian uppercase SOFT DJ + COLS10+6, // Serbian lowercase SOFT DJ + COLS0, // IE (variant) + COLS0, // ie (variant) + COLS10+10, // Ukrainian uppercase YE + COLS10+10, // Ukrainian lowercase YE + + COLS0, // ZHE with right descender + COLS0, // zhe with right descender + COLS10+13, // Macedonian uppercase ZELO + COLS10+13, // Macedonian lowercase ZELO + COLS0, // Old Slovanic uppercase Z + COLS0, // Old Slovanic uppercase z + COLS0, // II with macron + COLS0, // ii with mscron + COLS10+15, // Ukrainian uppercase I + COLS10+15, // Ukrainian lowercase I + + COLS10+16, // Ukrainian uppercase I with Two Dots + COLS10+16, // Ukrainian lowercase I with Two Dots + COLS0, // Old Slovanic uppercase I ligature + COLS0, // Old Slovanic lowercase I ligature + COLS10+18, // Serbian--Macedonian uppercase JE + COLS10+18, // Serbian--Macedonian lowercase JE + COLS10+31, // Macedonian uppercase SOFT K + COLS10+31, // Macedonian lowercase SOFT K + COLS0, // KA with right descender + COLS0, // ka with right descender + + COLS0, // KA ogonek + COLS0, // ka ogonek + COLS0, // KA vertical bar + COLS0, // ka vertical bar + COLS10+21, // Serbian--Macedonian uppercase SOFT L + COLS10+21, // Serbian--Macedonian lowercase SOFT L + COLS0, // EN with right descender + COLS0, // en with right descender + COLS10+24, // Serbian--Macedonian uppercase SOFT N + COLS10+24, // Serbian--Macedonian lowercase SOFT N + + COLS0, // ROUND OMEGA + COLS0, // round omega + COLS0, // OMEGA + COLS0, // omega + COLS10+30, // Serbian uppercase SOFT T + COLS10+30, // Serbian lowercase SOFT T + COLS10+33, // Byelorussian uppercase SHORT U + COLS10+33, // Byelorussian lowercase SHORT U + COLS0, // U with macron + COLS0, // u with macron + + COLS0, // STRAIGHT U + COLS0, // straight u + COLS0, // STRAIGHT U bar + COLS0, // straight u bar + COLS0, // OU ligature + COLS0, // ou ligature + COLS0, // KHA with right descender + COLS0, // kha with right descender + COLS0, // KHA ogonek + COLS0, // kha ogonek + + COLS0, // H + COLS0, // h + COLS0, // OMEGA titlo + COLS0, // omega titlo + COLS10+38, // Serbian uppercase HARD DJ + COLS10+38, // Serbian lowercase HARD DJ + COLS0, // CHE with right descender + COLS0, // che with right descender + COLS0, // CHE vertical bar + COLS0, // che vertical bar + + COLS0, // Old Slavonic SHCHA (variant) + COLS0, // old SLAVONIC shcha (variant) + COLS10+44, // Old Russian uppercase YAT + COLS10+44, // Old Russian lowercase YAT + +// END OF UNIQUE COLLATED BYTES +// CHARACTERS BELOW MUST HAVE HAVE THEIR OWN +// SUB-COLLATION VALUE TO COMPARE CORRECTLY. + + COLS0, // Old Bulgarian uppercase YUS + COLS0, // Old Bulgarian lowercase YUS + COLS0, // Old Slovanic uppercase YUS MALYI + COLS0, // Old Slovanic uppercase YUS MALYI + COLS0, // KSI + COLS0, // ksi + + COLS0, // PSI + COLS0, // psi + COLS0, // Old Russian uppercase FITA + COLS0, // Old Russian lowercase FITA + COLS0, // Old Russian uppercase IZHITSA + COLS0, // Old Russian lowercase IZHITSA + COLS0, // Russian uppercase A acute + COLS0, // Russian lowercase A acute + COLS10+8, // Russian uppercase E acute + COLS10+8, // Russian lowercase E acute + +// 160-below all characters are russian to 199 + + COLS0, // E acute + COLS0, // e acute + COLS10+14, // II acute + COLS10+14, // ii acute + COLS0, // I acute + COLS0, // i acute + COLS0, // YI acute + COLS0, // yi acute + COLS10+25, // O acute + COLS10+25, // o acute + + COLS10+32, // U acute + COLS10+32, // u acute + COLS10+42, // YERI acute + COLS10+42, // YERI acute + COLS10+45, // REVERSED E acute + COLS10+45, // reversed e acute + COLS10+46, // YU acute + COLS10+46, // yu acute + COLS10+47, // YA acute + COLS10+47, // ya acute + + COLS10, // A grave + COLS10, // a grave + COLS10+8, // E grave + COLS10+8, // e grave + COLS10+9, // YO grave + COLS10+9, // yo grave + COLS10+14, // I grave + COLS10+14, // i grave + COLS10+25, // O grave + COLS10+25, // o grave + + COLS10+32, // U grave + COLS10+32, // u grave + COLS10+42, // YERI grave + COLS10+42, // yeri grave + COLS10+45, // REVERSED E grave + COLS10+45, // reversed e grave + COLS10+46, // IU (YU) grave + COLS10+46, // iu (yu) grave + COLS10+47, // ia (YA) grave + COLS10+47, // ia (ya) grave ******* [10,199] +}; + +/**************************************************************************** +Desc: The Hebrew characters are collated over the Russian characters + Therefore sorting both Hebrew and Russian is impossible to do. +****************************************************************************/ +FLMBYTE fwp_heb60TblA[ HEBTBL1LEN + 2] = +{ + 0, // starting offset + HEBTBL1LEN, // len of table + COLS10h+0, // Alef + COLS10h+1, // Bet + COLS10h+2, // Gimel + COLS10h+3, // Dalet + COLS10h+4, // He + COLS10h+5, // Vav + COLS10h+6, // Zayin + COLS10h+7, // Het + COLS10h+8, // Tet + COLS10h+9, // Yod + COLS10h+10, // Kaf (final) [9,10] + COLS10h+11, // Kaf + COLS10h+12, // Lamed + COLS10h+13, // Mem (final) + COLS10h+14, // Mem + COLS10h+15, // Nun (final) + COLS10h+16, // Nun + COLS10h+17, // Samekh + COLS10h+18, // Ayin + COLS10h+19, // Pe (final) + COLS10h+20, // Pe [9,20] + COLS10h+21, // Tsadi (final) + COLS10h+22, // Tsadi + COLS10h+23, // Qof + COLS10h+24, // Resh + COLS10h+25, // Shin + COLS10h+26 // Tav [9,26] +}; + +/**************************************************************************** +Desc: This is the ANCIENT HEBREW SCRIPT piece. + The actual value will be stored in the subcollation. + This way we don't play diacritic/subcollation games. +****************************************************************************/ +FLMBYTE fwp_heb60TblB[ HEBTBL2LEN + 2] = +{ + 84, + HEBTBL2LEN, + +// [9,84] + COLS10h+0, // Alef Dagesh [9,84] + COLS10h+1, // Bet Dagesh + COLS10h+1, // Vez - looks like a bet + COLS10h+2, // Gimel Dagesh + COLS10h+3, // Dalet Dagesh + COLS10h+4, // He Dagesh + COLS10h+5, // Vav Dagesh [9,90] + COLS10h+5, // Vav Holem + COLS10h+6, // Zayin Dagesh + COLS10h+7, // Het Dagesh + COLS10h+8, // Tet Dagesh + COLS10h+9, // Yod Dagesh + COLS10h+9, // Yod Hiriq [9,96] - not on my list + + COLS10h+11, // Kaf Dagesh + COLS10h+10, // Kaf Dagesh (final) + COLS10h+10, // Kaf Sheva (final) + COLS10h+10, // Kaf Tsere (final) [9,100] + COLS10h+10, // Kaf Segol (final) + COLS10h+10, // Kaf Patah (final) + COLS10h+10, // Kaf Qamats (final) + COLS10h+10, // Kaf Dagesh Qamats (final) + COLS10h+12, // Lamed Dagesh + COLS10h+14, // Mem Dagesh + COLS10h+16, // Nun Dagesh + COLS10h+15, // Nun Qamats (final) + COLS10h+17, // Samekh Dagesh + COLS10h+20, // Pe Dagesh [9,110] + COLS10h+20, // Fe - just guessing this is like Pe - was +21 + COLS10h+22, // Tsadi Dagesh + COLS10h+23, // Qof Dagesh + COLS10h+25, // Sin (with sin dot) + COLS10h+25, // Sin Dagesh (with sin dot) + COLS10h+25, // Shin + COLS10h+25, // Shin Dagesh + COLS10h+26 // Tav Dagesh [9,118] +}; + +/**************************************************************************** +Desc: The Arabic characters are collated OVER the Russian characters + Therefore sorting both Arabic and Russian in the same database + is not supported. + + Arabic starts with a bunch of accents/diacritic marks that are + Actually placed OVER a preceeding character. These accents are + ignored while sorting the first pass - when collation == COLS0. + + There are 4 possible states for all/most arabic characters: + ?? - occurs as the only character in a word + ?? - appears at the first of the word + ?? - appears at the middle of a word + ?? - appears at the end of the word + + Usually only the simple version of the letter is stored. + Therefore we should not have to worry about sub-collation + of these characters. + + The arabic characters with diacritics differ however. The alef has + sub-collation values to sort correctly. There is not any more room + to add more collation values. Some chars in CS14 are combined when + urdu, pashto and sindhi characters overlap. +****************************************************************************/ +FLMBYTE fwp_ar160Tbl[ AR1TBLLEN + 2] = +{ + 38, // starting offset + AR1TBLLEN, // len of table +// [13,38] + COLLS+2, // , comma + COLLS+3, // : colon +// [13,40] + COLLS+7, // ? question mark + COLS4+2, // * asterick + COLS6, // % percent + COLS9+41, // >> alphabetic - end of list) + COLS9+40, // << alphabetic - end of list) + COLS2, // ( + COLS2+1, // ) +// [13,47] + COLS8+1, // ?? One + COLS8+2, // ?? Two + COLS8+3, // ?? Three +// [13,50] + COLS8+4, // ?? Four + COLS8+5, // ?? Five + COLS8+6, // ?? Six + COLS8+7, // ?? Seven + COLS8+8, // ?? Eight + COLS8+9, // ?? Nine + COLS8+0, // ?? Zero + COLS8+2, // ?? Two (Handwritten) + + COLS10a+1, // ?? alif + COLS10a+1, // ?? alif +// [13,60] + COLS10a+2, // ?? ba + COLS10a+2, // ?? ba + COLS10a+2, // ?? ba + COLS10a+2, // ?? ba + COLS10a+6, // ?? ta + COLS10a+6, // ?? ta + COLS10a+6, // ?? ta + COLS10a+6, // ?? ta + COLS10a+8, // ?? tha + COLS10a+8, // ?? tha +// [13,70] + COLS10a+8, // ?? tha + COLS10a+8, // ?? tha + COLS10a+12, // ?? jiim + COLS10a+12, // ?? jiim + COLS10a+12, // ?? jiim + COLS10a+12, // ?? jiim + COLS10a+16, // ?? Ha + COLS10a+16, // ?? Ha + COLS10a+16, // ?? Ha + COLS10a+16, // ?? Ha +// [13,80] + COLS10a+17, // ?? kha + COLS10a+17, // ?? kha + COLS10a+17, // ?? kha + COLS10a+17, // ?? kha + COLS10a+20, // ?? dal + COLS10a+20, // ?? dal + COLS10a+22, // ?? dhal + COLS10a+22, // ?? dhal + COLS10a+27, // ?? ra + COLS10a+27, // ?? ra +// [13,90] + COLS10a+29, // ?? ziin + COLS10a+29, // ?? ziin + COLS10a+31, // ?? siin + COLS10a+31, // ?? siin + COLS10a+31, // ?? siin + COLS10a+31, // ?? siin + COLS10a+32, // ?? shiin + COLS10a+32, // ?? shiin + COLS10a+32, // ?? shiin + COLS10a+32, // ?? shiin +// [13,100] + COLS10a+34, // ?? Sad + COLS10a+34, // ?? Sad + COLS10a+34, // ?? Sad + COLS10a+34, // ?? Sad + COLS10a+35, // ?? Dad + COLS10a+35, // ?? Dad + COLS10a+35, // ?? Dad + COLS10a+35, // ?? Dad + COLS10a+36, // ?? Ta + COLS10a+36, // ?? Ta +// [13,110] + COLS10a+36, // ?? Ta + COLS10a+36, // ?? Ta + COLS10a+37, // ?? Za + COLS10a+37, // ?? Za + COLS10a+37, // ?? Za + COLS10a+37, // ?? Za + COLS10a+38, // ?? 'ain + COLS10a+38, // ?? 'ain + COLS10a+38, // ?? 'ain + COLS10a+38, // ?? 'ain +// [13,120] + COLS10a+39, // ?? ghain + COLS10a+39, // ?? ghain + COLS10a+39, // ?? ghain + COLS10a+39, // ?? ghain + COLS10a+40, // ?? fa + COLS10a+40, // ?? fa + COLS10a+40, // ?? fa + COLS10a+40, // ?? fa + COLS10a+42, // ?? Qaf + COLS10a+42, // ?? Qaf +// [13,130] + COLS10a+42, // ?? Qaf + COLS10a+42, // ?? Qaf + COLS10a+43, // ?? kaf + COLS10a+43, // ?? kaf + COLS10a+43, // ?? kaf + COLS10a+43, // ?? kaf + COLS10a+46, // ?? lam + COLS10a+46, // ?? lam + COLS10a+46, // ?? lam + COLS10a+46, // ?? lam +// [13,140] + COLS10a+47, // ?? miim + COLS10a+47, // ?? miim + COLS10a+47, // ?? miim + COLS10a+47, // ?? miim + COLS10a+48, // ?? nuun + COLS10a+48, // ?? nuun + COLS10a+48, // ?? nuun + COLS10a+48, // ?? nuun + COLS10a+49, // ?? ha + COLS10a+49, // ?? ha +// [13,150] + COLS10a+49, // ?? ha + COLS10a+49, // ?? ha + // ha is also 51 for non-arabic + COLS10a+6, // ?? ta marbuuTah + COLS10a+6, // ?? ta marbuuTah + COLS10a+50, // ?? waw + COLS10a+50, // ?? waw + COLS10a+53, // ?? ya + COLS10a+53, // ?? ya + COLS10a+53, // ?? ya + COLS10a+53, // ?? ya +// [13,160] + COLS10a+52, // ?? alif maqSuurah + COLS10a+52, // ?? ya maqSuurah? + COLS10a+52, // ?? ya maqSuurah? + COLS10a+52, // ?? alif maqSuurah + + COLS10a+0, // ?? hamzah accent - never appears alone +// [13,165] + +// Store the sub-collation as the actual +// character value from this point on + + COLS10a+1, // ?? alif hamzah + COLS10a+1, // ?? alif hamzah + COLS10a+1, // ?? hamzah-under-alif + COLS10a+1, // ?? hamzah-under-alif + COLS10a+1, // ?? waw hamzah +// [13,170] + COLS10a+1, // ?? waw hamzah + COLS10a+1, // ?? ya hamzah + COLS10a+1, // ?? ya hamzah + COLS10a+1, // ?? ya hamzah + COLS10a+1, // ?? ya hamzah + COLS10a+1, // ?? alif fatHataan + COLS10a+1, // ?? alif fatHataan + COLS10a+1, // ?? alif maddah + COLS10a+1, // ?? alif maddah + COLS10a+1, // ?? alif waSlah +// [13,180] + COLS10a+1, // ?? alif waSlah (final) + +// LIGATURES +// Should NEVER be stored so will not worry +// about breaking up into pieces for collation. +// NOTE: +// Let's store the "Lam" collation value (+42) +// below and in the sub-collation store the +// actual character. This will sort real close. +// The best implementation is to +// break up ligatures into its base pieces. + + COLS10a+46, // ?? lamalif + COLS10a+46, // ?? lamalif + COLS10a+46, // ?? lamalif hamzah + COLS10a+46, // ?? lamalif hamzah + COLS10a+46, // ?? hamzah-under-lamalif + COLS10a+46, // ?? hamzah-under-lamalif + COLS10a+46, // ?? lamalif fatHataan + COLS10a+46, // ?? lamalif fatHataan + COLS10a+46, // ?? lamalif maddah +// [13,190] + COLS10a+46, // ?? lamalif maddah + COLS10a+46, // ?? lamalif waSlah + COLS10a+46, // ?? lamalif waSlah + COLS10a+46, // ?? Allah - khaDalAlif + COLS0_ARABIC, // ?? taTwiil - character extension - throw out + COLS0_ARABIC // ?? taTwiil 1/6 - character extension - throw out +}; + +/**************************************************************************** +Desc: Alef needs a subcollation table. + If colval==COLS10a+1 & char>=165 + index through this table. Otherwise + the alef value is [13,58] and subcol + value should be 7. Alef maddah is default (0) + + Handcheck if colval==COLS10a+6 + Should sort: + [13,152]..[13,153] - taa marbuuTah - nosubcoll + [13,64] ..[13,67] - taa - subcoll of 1 +****************************************************************************/ +FLMBYTE fwp_alefSubColTbl[] = +{ +// [13,165] + 1, // ?? alif hamzah + 1, // ?? alif hamzah + 3, // ?? hamzah-under-alif + 3, // ?? hamzah-under-alif + 2, // ?? waw hamzah +// [13,170] + 2, // ?? waw hamzah + 4, // ?? ya hamzah + 4, // ?? ya hamzah + 4, // ?? ya hamzah + 4, // ?? ya hamzah + 5, // ?? alif fatHataan + 5, // ?? alif fatHataan + 0, // ?? alif maddah + 0, // ?? alif maddah + 6, // ?? alif waSlah +// [13,180] + 6 // ?? alif waSlah (final) +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE fwp_ar260Tbl[ AR2TBLLEN + 2] = +{ + 41, // starting offset + AR2TBLLEN, // len of table +// [14,41] + COLS8+4, // Farsi and Urdu Four + COLS8+4, // Urdu Four + COLS8+5, // Farsi and Urdu Five + COLS8+6, // Farsi Six + COLS8+6, // Farsi and Urdu Six + COLS8+7, // Urdu Seven + COLS8+8, // Urdu Eight + + COLS10a+3, // Sindhi bb - baa /w 2 dots below (67b) + COLS10a+3, + COLS10a+3, + COLS10a+3, + COLS10a+4, // Sindhi bh - baa /w 4 dots below (680) + COLS10a+4, + COLS10a+4, + COLS10a+4, +// [14,56] + COLS10a+5, // Malay, Kurdish, Pashto, Farsi, Sindhi, and Urdu p + COLS10a+5, // =peh - taa /w 3 dots below (67e) + COLS10a+5, + COLS10a+5, + COLS10a+7, // Urdu T - taa /w small tah + COLS10a+7, + COLS10a+7, + COLS10a+7, + COLS10a+7, // Pashto T - taa /w ring (forced to combine) + COLS10a+7, + COLS10a+7, + COLS10a+7, + COLS10a+9, // Sindhi th - taa /w 4 dots above (67f) + COLS10a+9, +// [14,70] + COLS10a+9, + COLS10a+9, + COLS10a+10, // Sindhi Tr - taa /w 3 dots above (67d) + COLS10a+10, + COLS10a+10, + COLS10a+10, + COLS10a+11, // Sindhi Th - taa /w 2 dots above (67a) + COLS10a+11, + COLS10a+11, + COLS10a+11, + COLS10a+13, // Sindhi jj - haa /w 2 middle dots verticle (684) + COLS10a+13, + COLS10a+13, + COLS10a+13, + COLS10a+14, // Sindhi ny - haa /w 2 middle dots (683) + COLS10a+14, + COLS10a+14, + COLS10a+14, +// [14,88] + COLS10a+15, // Malay, Kurdish, Pashto, Farsi, Sindhi, and Urdu ch + COLS10a+15, // =tcheh (686) + COLS10a+15, + COLS10a+15, + COLS10a+15, // Sindhi chh - haa /w middle 4 dots (687) + COLS10a+15, // forced to combine + COLS10a+15, + COLS10a+15, + COLS10a+18, // Pashto ts - haa /w 3 dots above (685) + COLS10a+18, + COLS10a+18, + COLS10a+18, + COLS10a+19, // Pashto dz - hamzah on haa (681) + COLS10a+19, + COLS10a+19, + COLS10a+19, +// [14,104] + COLS10a+21, // Urdu D - dal /w small tah (688) + COLS10a+21, + COLS10a+21, // Pashto D - dal /w ring (689) forced to combine + COLS10a+21, + COLS10a+23, // Sindhi dh - dal /w 2 dots above (68c) + COLS10a+23, + COLS10a+24, // Sindhi D - dal /w 3 dots above (68e) + COLS10a+24, + COLS10a+25, // Sindhi Dr - dal /w dot below (68a) + COLS10a+25, + COLS10a+26, // Sindhi Dh - dal /w 2 dots below (68d) + COLS10a+26, + COLS10a+28, // Pashto r - ra /w ring (693) + COLS10a+28, +// [14,118] + COLS10a+28, // Urdu R - ra /w small tah (691) forced to combine + COLS10a+28, + COLS10a+28, // Sindhi r - ra /w 4 dots above (699) forced to combine + COLS10a+28, + COLS10a+27, // Kurdish rolled r - ra /w 'v' below (695) + COLS10a+27, + COLS10a+27, + COLS10a+27, +// [14,126] + COLS10a+30, // Kurdish, Pashto, Farsi, Sindhi, and Urdu Z + COLS10a+30, // = jeh - ra /w 3 dots above (698) + COLS10a+30, // Pashto zz - ra /w dot below & dot above (696) + COLS10a+30, // forced to combine + COLS10a+30, // Pashto g - not in unicode! - forced to combine + COLS10a+30, + COLS10a+33, // Pashto x - seen dot below & above (69a) + COLS10a+33, + COLS10a+33, + COLS10a+33, + COLS10a+39, // Malay ng - old maly ain /w 3 dots above (6a0) + COLS10a+39, // forced to combine + COLS10a+39, + COLS10a+39, +// [14,140] + COLS10a+41, // Malay p, Kurdish v - Farsi ? - fa /w 3 dots above + COLS10a+41, // = veh - means foreign words (6a4) + COLS10a+41, + COLS10a+41, + COLS10a+41, // Sindhi ph - fa /w 4 dots above (6a6) forced to combine + COLS10a+41, + COLS10a+41, + COLS10a+41, +// [14,148] + COLS10a+43, // Misc k - open caf (6a9) + COLS10a+43, + COLS10a+43, + COLS10a+43, + COLS10a+43, // misc k - no unicode - forced to combine + COLS10a+43, + COLS10a+43, + COLS10a+43, + COLS10a+43, // Sindhi k - swash caf (various) (6aa) -forced to combine + COLS10a+43, + COLS10a+43, + COLS10a+43, +// [14,160] + COLS10a+44, // Persian/Urdu g - gaf (6af) + COLS10a+44, + COLS10a+44, + COLS10a+44, + COLS10a+44, // Persian/Urdu g - no unicode + COLS10a+44, + COLS10a+44, + COLS10a+44, + COLS10a+44, // malay g - gaf /w ring (6b0) + COLS10a+44, + COLS10a+44, + COLS10a+44, + COLS10a+44, // Sindhi ng - gaf /w 2 dots above (6ba) + COLS10a+44, // forced to combine ng only + COLS10a+44, + COLS10a+44, + COLS10a+45, // Sindhi gg - gaf /w 2 dots vertical below (6b3) + COLS10a+45, + COLS10a+45, + COLS10a+45, +// [14,180] + COLS10a+46, // Kurdish velar l - lam /w small v (6b5) + COLS10a+46, + COLS10a+46, + COLS10a+46, + COLS10a+46, // Kurdish Lamalif with diacritic - no unicode + COLS10a+46, +// [14,186] + COLS10a+48, // Urdu n - dotless noon (6ba) + COLS10a+48, + COLS10a+48, + COLS10a+48, + COLS10a+48, // Pashto N - noon /w ring (6bc) - forced to combine + COLS10a+48, + COLS10a+48, + COLS10a+48, + COLS10a+48, // Sindhi N - dotless noon/w small tah (6bb) + COLS10a+48, // forced to combine + COLS10a+48, + COLS10a+48, + COLS10a+50, // Kurdish o - waw /w small v (6c6) + COLS10a+50, +// [14,200] + COLS10a+50, // Kurdish o - waw /w bar above (6c5) + COLS10a+50, + COLS10a+50, // Kurdish o - waw /w 2 dots above (6ca) + COLS10a+50, +// [14,204] + COLS10a+51, // Urdu h - no unicode + COLS10a+51, + COLS10a+51, + COLS10a+51, + COLS10a+52, // Kurdish ? - ya /w small v (6ce) + COLS10a+52, + COLS10a+52, + COLS10a+52, +// [14,212] + COLS10a+54, // Urdu y - ya barree (6d2) + COLS10a+54, + COLS10a+54, // Malay ny - ya /w 3 dots below (6d1) forced to combine + COLS10a+54, + COLS10a+54, + COLS10a+54, +// [14,218] + COLS10a+51, // Farsi hamzah - hamzah on ha (6c0) forced to combine + COLS10a+51 +}; + +/**************************************************************************** +Desc: If the bit position is set then save the character in the sub-col + area. The bit values are determined by looking at the + FLAIM COLTBL1 to see which characters are combined with other + Arabic characters. +****************************************************************************/ +FLMBYTE fwp_ar2BitTbl[] = +{ + // Start at character 64 + // The only 'clean' areas uncollate to the correct place, they are... + // 48..63 + // 68..91 + // 96..117 + // 126..127 + // 140..143 + // 160..163 + // 176..179 + // 212..213 + + 0xF0, // 64..71 + 0x00, // 72..79 + 0x00, // 80..87 + 0x0F, // 88..95 - 92..95 + 0x00, // 96..103 + 0x00, // 104..111 + 0x03, // 112..119 + 0xFC, // 120..127 + 0xFF, // 128..135 + 0xF0, // 136..143 - 136..139 + 0xFF, // 144..151 - 144..147, 148..159 + 0xFF, // 152..159 + 0x0F, // 160..167 - 164..175 + 0xFF, // 168..175 + 0x0F, // 176..183 - 180..185 + 0xFF, // 184..191 - 186..197 + 0xFF, // 192..199 - 198..203 + 0xFF, // 200..207 - 204..207 + 0xF3, // 208..215 - 208..211 , 214..217 + 0xF0 // 216..219 - 218..219 +}; + +/**************************************************************************** +Desc: This table describes and gives addresses for collating 5.0 + character sets. Each line corresponds with a character set. +***************************************************************************/ +TBL_B_TO_BP fwp_col60Tbl[] = +{ + {CHSASCI, fwp_asc60Tbl}, // ascii - " " - "~" + {CHSMUL1, fwp_mn60Tbl}, // multinational + {CHSSYM1, fwp_sym60Tbl}, // symbols + {CHSGREK, fwp_grk60Tbl}, // greek + {CHSCYR, fwp_cyrl60Tbl}, // Cyrillic - Russian + {0xFF, 0} // table terminator +}; + +/**************************************************************************** +Desc: This table is for sorting the hebrew/arabic languages. + These values overlap the end of ASC/european and cyrillic tables. +****************************************************************************/ +TBL_B_TO_BP fwp_HebArabicCol60Tbl[] = +{ + {CHSASCI, fwp_asc60Tbl}, // ascii - " " - "~" + {CHSMUL1, fwp_mn60Tbl}, // multinational + {CHSSYM1, fwp_sym60Tbl}, // symbols + {CHSGREK, fwp_grk60Tbl}, // greek + {CHSHEB, fwp_heb60TblA}, // Hebrew + {CHSHEB, fwp_heb60TblB}, // Hebrew + {CHSARB1, fwp_ar160Tbl}, // Arabic Set 1 + {CHSARB2, fwp_ar260Tbl}, // Arabic Set 2 + {0xff, 0} // table terminator +}; + +/**************************************************************************** +Desc: The diacritical to collated table translates the first 26 + characters of WP character set #1 into a 5 bit value for "correct" + sorting sequence for that diacritical (DCV) - diacritic collated + value. + + The attempt here is to convert the collated character value + along with the DCV to form the original WP character. + + The diacriticals are in an order to fit the most languages. + Czech, Swedish, and Finnish will have to manual reposition the + ring above (assign it a value greater then the umlaut) + + This table is index by the diacritical value. +****************************************************************************/ +FLMBYTE fwp_dia60Tbl[] = +{ + 2, // grave offset = 0 + 16, // centerd offset = 1 + 7, // tilde offset = 2 + 4, // circum offset = 3 + 12, // crossb offset = 4 + 10, // slash offset = 5 + 1, // acute offset = 6 + 6, // umlaut offset = 7 + // In SU, SV and CZ will = 9 + 17, // macron offset = 8 + 18, // aposab offset = 9 + 19, // aposbes offset = 10 + 20, // aposba offset = 11 + 21, // aposbc offset = 12 + 22, // abosbl offset = 13 + 8, // ring offset = 14 + 13, // dota offset = 15 + 23, // dacute offset = 16 + 11, // cedilla offset = 17 + 14, // ogonek offset = 18 + 5, // caron offset = 19 + 15, // stroke offset = 20 + 24, // bara offset = 21 + 3, // breve offset = 22 + 0, // dbls offset = 23 sorts as 'ss' + 25, // dotlesi offset = 24 + 26 // dotlesj offset = 25 +}; + +/**************************************************************************** +Desc: This table defines the range of characters within the set + which are case convertible. +****************************************************************************/ +static FLMBYTE fwp_caseConvertableRange[] = +{ + 26,241, // Multinational 1 + 0,0, // Multinational 2 + 0,0, // Box Drawing + 0,0, // Symbol 1 + 0,0, // Symbol 2 + 0,0, // Math 1 + 0,0, // Math 2 + 0,69, // Greek 1 + 0,0, // Hebrew + 0,199, // Cyrillic + 0,0, // Japanese Kana + 0,0, // User-defined + 0,0, // Not defined + 0,0, // Not defined + 0,0, // Not defined +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 colToWPChr[ COLS11 - COLLS] = +{ + 0x20, // colls - + 0x2e, // colls+1 - . + 0x2c, // colls+2 - , + 0x3a, // colls+3 - : + 0x3b, // colls+4 - ; + 0x21, // colls+5 - ! + 0, // colls+6 - NO VALUE + 0x3f, // colls+7 - ? + 0, // colls+8 - NO VALUE + + 0x22, // cols1 - " + 0x27, // cols1+1 - ' + 0x60, // cols1+2 - ` + 0, // cols1+3 - NO VALUE + 0, // cols1+4 - NO VALUE + + 0x28, // cols2 - ( + 0x29, // cols2+1 - ) + 0x5b, // cols2+2 - japanese angle brackets + 0x5d, // cols2+3 - japanese angle brackets + 0x7b, // cols2+4 - { + 0x7d, // cols2+5 - } + + 0x24, // cols3 - $ + 0x413, // cols3+1 - cent + 0x40b, // cols3+2 - pound + 0x40c, // cols3+3 - yen + 0x40d, // cols3+4 - pacetes + 0x40e, // cols3+5 - floren + + 0x2b, // cols4 - + + 0x2d, // cols4+1 - - + 0x2a, // cols4+2 - * + 0x2f, // cols4+3 - / + 0x5e, // cols4+4 - ^ + 0, // cols4+5 - NO VALUE + 0, // cols4+6 - NO VALUE + 0, // cols4+7 - NO VALUE + + 0x3c, // cols5 - < + 0, // cols5+1 - NO VALUE + 0x3d, // cols5+2 - = + 0, // cols5+3 - NO VALUE + 0x3e, // cols5+4 - > + 0, // cols5+5 - NO VALUE + 0, // cols5+6 - NO VALUE + 0, // cols5+7 - NO VALUE + 0, // cols5+8 - NO VALUE + 0, // cols5+9 - NO VALUE + 0, // cols5+10 - NO VALUE + 0, // cols5+11 - NO VALUE + 0, // cols5+12 - NO VALUE + 0, // cols5+13 - NO VALUE + + 0x25, // cols6 - % + 0x23, // cols6+1 - # + 0x26, // cols6+2 - & + 0x40, // cols6+3 - @ + 0x5c, // cols6+4 - Backslash + 0x5f, // cols6+5 - _ + 0x7c, // cols6+6 - | + 0x7e, // cols6+7 - ~ + 0, // cols6+8 - NO VALUE + 0, // cols6+9 - NO VALUE + 0, // cols6+10 - NO VALUE + 0, // cols6+11 - NO VALUE + 0, // cols6+12 - NO VALUE + + 0x800, // cols7 - Uppercase Alpha + 0x802, // cols7+1 - Uppercase Beta + 0x806, // cols7+2 - Uppercase Gamma + 0x808, // cols7+3 - Uppercase Delta + 0x80a, // cols7+4 - Uppercase Epsilon + 0x80c, // cols7+5 - Uppercase Zeta + 0x80e, // cols7+6 - Uppercase Eta + 0x810, // cols7+7 - Uppercase Theta + 0x812, // cols7+8 - Uppercase Iota + 0x814, // cols7+9 - Uppercase Kappa + 0x816, // cols7+10 - Uppercase Lambda + 0x818, // cols7+11 - Uppercase Mu + 0x81a, // cols7+12 - Uppercase Nu + 0x81c, // cols7+13 - Uppercase Xi + 0x81e, // cols7+14 - Uppercase Omicron + 0x820, // cols7+15 - Uppercase Pi + 0x822, // cols7+16 - Uppercase Rho + 0x824, // cols7+17 - Uppercase Sigma + 0x828, // cols7+18 - Uppercase Tau + 0x82a, // cols7+19 - Uppercase Upsilon + 0x82c, // cols7+20 - Uppercase Phi + 0x82e, // cols7+21 - Uppercase Chi + 0x830, // cols7+22 - Uppercase Psi + 0x832, // cols7+23 - Uppercase Omega + 0, // cols7+24 - NO VALUE + + 0x30, // cols8 - 0 + 0x31, // cols8+1 - 1 + 0x32, // cols8+2 - 2 + 0x33, // cols8+3 - 3 + 0x34, // cols8+4 - 4 + 0x35, // cols8+5 - 5 + 0x36, // cols8+6 - 6 + 0x37, // cols8+7 - 7 + 0x38, // cols8+8 - 8 + 0x39, // cols8+9 - 9 + + 0x41, // cols9 - A + 0x124, // cols9+1 - AE digraph + 0x42, // cols9+2 - B + 0x43, // cols9+3 - C + 0xffff, // cols9+4 - CH in spanish + 0x162, // cols9+5 - Holder for C caron in Czech + 0x44, // cols9+6 - D + 0x45, // cols9+7 - E + 0x46, // cols9+8 - F + 0x47, // cols9+9 - G + 0x48, // cols9+10 - H + 0xffff, // cols9+11 - CH in czech or dotless i in turkish + 0x49, // cols9+12 - I + 0x18a, // cols9+13 - IJ Digraph + 0x4a, // cols9+14 - J + 0x4b, // cols9+15 - K + 0x4c, // cols9+16 - L + 0xffff, // cols9+17 - LL in spanish + 0x4d, // cols9+18 - M + 0x4e, // cols9+19 - N + 0x138, // cols9+20 - N Tilde + 0x4f, // cols9+21 - O + 0x1a6, // cols9+22 - OE digraph + 0x50, // cols9+23 - P + 0x51, // cols9+24 - Q + 0x52, // cols9+25 - R + 0x1aa, // cols9+26 - Holder for R caron in Czech + 0x53, // cols9+27 - S + 0x1b0, // cols9+28 - Holder for S caron in Czech + 0x54, // cols9+29 - T + 0x55, // cols9+30 - U + 0x56, // cols9+31 - V + + 0x57, // cols9+32 - W + 0x58, // cols9+33 - X + 0x59, // cols9+34 - Y + 0x5a, // cols9+35 - Z + 0x1ce, // cols9+36 - Holder for Z caron in Czech + 0x158, // cols9+37 - Uppercase Thorn + 0, // cols9+38 - ??? + 0, // cols9+39 - ??? + 0x5b, // cols9+40 - [ (note: alphabetic - end of list) + 0x5d, // cols9+41 - ] (note: alphabetic - end of list) +// 0xAA - also start of Hebrew + 0x124, // cols9+42 - AE diagraph - DK + 0x124, // cols9+43 - AE diagraph - NO + 0x122, // cols9+44 - A ring - SW + 0x11E, // cols9+45 - A diaeresis - DK + 0x124, // cols9+46 - AE diagraph - IC + 0x150, // cols9+47 - O slash - NO + 0x11e, // cols9+48 - A diaeresis - SW + 0x150, // cols9+49 - O slash - DK + 0x13E, // cols9+50 - O Diaeresis - IC + 0x122, // cols9+51 - A ring - NO + 0x13E, // cols9+52 - O Diaeresis - SW + 0x13E, // cols9+53 - O Diaeresis - DK + 0x150, // cols9+54 - O slash - IC + 0x122, // cols9+55 - A ring - DK + 0x124, // cols9+56 - AE diagraph future + 0x13E, // cols9+57 - O Diaeresis future + 0x150, // cols9+58 - O slash future + 0, // cols9+59 - NOT USED future + + 0xA00, // cols10 - Russian A + 0xA02, // cols10+1 - Russian BE + 0xA04, // cols10+2 - Russian VE + 0xA06, // cols10+3 - Russian GHE + 0xA46, // cols10+4 - Ukrainian HARD G + 0xA08, // cols10+5 - Russian DE + 0xA4a, // cols10+6 - Serbian SOFT DJ + 0xA44, // cols10+7 - Macedonian SOFT DJ + 0xA0a, // cols10+8 - Russian E + 0xA0c, // cols10+9 - Russian YO + 0xA4e, // cols10+10 - Ukrainian YE + 0xA0e, // cols10+11 - Russian ZHE + 0xA10, // cols10+12 - Russian ZE + 0xA52, // cols10+13 - Macedonian ZELO + 0xA12, // cols10+14 - Russian I + 0xA58, // cols10+15 - Ukrainian I + 0xA5a, // cols10+16 - Ukrainian I with Two dots + 0xA14, // cols10+17 - Russian SHORT I + 0xA5e, // cols10+18 - Serbian--Macedonian JE + 0xA16, // cols10+19 - Russian KA + 0xA18, // cols10+20 - Russian EL + 0xA68, // cols10+21 - Serbian--Macedonian SOFT L + 0xA1a, // cols10+22 - Russian EM + 0xA1c, // cols10+23 - Russian EN + 0xA6c, // cols10+24 - Serbian--Macedonian SOFT N + 0xA1e, // cols10+25 - Russian O + 0xA20, // cols10+26 - Russian PE + 0xA22, // cols10+27 - Russian ER + 0xA24, // cols10+28 - Russian ES + 0xA26, // cols10+29 - Russian TE + 0xA72, // cols10+30 - Serbian SOFT T + 0xA60, // cols10+31 - Macedonian SOFT K + 0xA28, // cols10+32 - Russian U + 0xA74, // cols10+33 - Byelorussian SHORT U + 0xA2a, // cols10+34 - Russian EF + 0xA2c, // cols10+35 - Russian HA + 0xA2e, // cols10+36 - Russian TSE + 0xA30, // cols10+37 - Russian CHE + 0xA86, // cols10+38 - Serbian HARD DJ + 0xA32, // cols10+39 - Russian SHA + 0xA34, // cols10+40 - Russian SHCHA + 0xA36, // cols10+41 - Russian ER (also hard + 0xA38, // cols10+42 - Russian ERY + 0xA3a, // cols10+43 - Russian SOFT SIGN + 0xA8e, // cols10+44 - Old Russian YAT + 0xA3c, // cols10+45 - Russian uppercase REVERSE E + 0xA3e, // cols10+46 - Russian YU + 0xA40, // cols10+47 - Russian YA + 0xA3a, // cols10+48 - Russian SOFT SIGN - UKRAIN ONLY + 0 // cols10+49 - future +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 HebArabColToWPChr[] = +{ + // Start at COLS10a+0 +// [0] + 0x0D00 +164, // hamzah + 0x0D00 + 58, // [13,177] alef maddah + // Read subcollation to get other alef values + 0x0D00 + 60, // baa + 0x0E00 + 48, // Sindhi bb + 0x0E00 + 52, // Sindhi bh + 0x0E00 + 56, // Misc p = peh + 0x0D00 +152, // taa marbuuTah + // subcollation of 1 is taa [13,64] + 0x0E00 + 60, // Urdu T [14,60] + // Pashto T [14,64] +// [8] + 0x0D00 + 68, // thaa + 0x0E00 + 68, // Sindhi th + 0x0E00 + 72, // Sindhi tr + 0x0E00 + 76, // Sindhi Th + 0x0D00 + 72, // jiim - jeem + 0x0E00 + 80, // Sindhi jj + 0x0E00 + 84, // Sindhi ny + 0x0E00 + 88, // Misc ch + // Sinhi chh [14,92] +// [16] + 0x0D00 + 76, // Haa + 0x0D00 + 80, // khaa + 0x0E00 + 96, // Pashto ts + 0x0E00 +100, // Pashto dz + + 0x0D00 + 84, // dal + 0x0E00 +104, // Urdu D + // Pashto D + 0x0D00 + 86, // thal + 0x0E00 +108, // Sindhi dh + +// [24] + 0x0E00 +110, // Sindhi D + 0x0E00 +112, // Sindhi Dr + 0x0E00 +114, // Sindhi Dh + + 0x0D00 + 88, // ra + // Kurdish rolled r [14,122] + 0x0E00 +116, // Pashto r [14,116] - must pick this! + // Urdu R [14,118] + // Sindhi r [14,120] + + 0x0D00 + 90, // zain + 0x0E00 +126, // Mizc Z=jeh [14,126] + // Pashto zz [14,128] + // Pashto g [14,130] + + 0x0D00 + 92, // seen + +// [32] + 0x0D00 + 96, // sheen + 0x0E00 +132, // Pashto x + 0x0D00 +100, // Sad + 0x0D00 +104, // Dad + 0x0D00 +108, // Tah + 0x0D00 +112, // Za (dhah) + 0x0D00 +116, // 'ain + 0x0D00 +120, // ghain + // malay ng [14,136] +// [40] + 0x0D00 +124, // fa + 0x0E00 +140, // Malay p, kurdish v = veh + // Sindhi ph [14,144] + 0x0D00 +128, // Qaf + 0x0D00 +132, // kaf (caf) + // Misc k [14,148] + // misc k - no unicode [14,152] + // Sindhi k [14,156] + + 0x0E00 +160, // Persian/Urdu gaf + // gaf - no unicode [14,164] + // malay g [14,168] + // Sindhi ng [14,172] + 0x0E00 +176, // Singhi gg + + 0x0D00 +136, // lam - all ligature variants + // Kurdish valar lam [14,180] + // Kurdish lamalef - no unicode [14,184] + + 0x0D00 +140, // meem + +// [48] + 0x0D00 +144, // noon + // Urdu n [14,186] + // Pashto N [14,190] + // Sindhi N [14,194] + 0x0D00 +148, // ha - arabic language only! + 0x0D00 +154, // waw + // Kurdish o [14,198] + // Kurdish o with bar [14,200] + // Kurdish o with 2 dots [14,202] + 0x0D00 +148, // ha - non-arabic language + // Urdu h [14,204] + // Farsi hamzah on ha [14,218] + 0x0D00 +160, // alef maqsurah + // Kurdish e - ya /w small v + + 0x0D00 +156, // ya + 0x0E00 +212 // Urdu ya barree + // Malay ny [14,214] +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT16 ArabSubColToWPChr[] = +{ + 0x0D00 +177, // Alef maddah - default value - here for documentation + 0x0D00 +165, // Alef Hamzah + 0x0D00 +169, // Waw hamzah + 0x0D00 +167, // Hamzah under alef + 0x0D00 +171, // ya hamzah + 0x0D00 +175, // alef fathattan + 0x0D00 +179, // alef waslah + 0x0D00 + 58, // alef + 0x0D00 + 64 // taa - after taa marbuuTah +}; + +/**************************************************************************** +Desc: Turns a collated diacritic value into the original diacritic value +****************************************************************************/ +FLMBYTE ml1_COLtoD[27] = +{ + 23, // dbls sort value = 0 sorts as 'ss' + 6, // acute sort value = 1 + 0, // grave sort value = 2 + 22, // breve sort value = 3 + 3, // circum sort value = 4 + 19, // caron sort value = 5 + 7, // umlaut sort value = 6 + 2, // tilde sort value = 7 + 14, // ring sort value = 8 + 7, // umlaut in SU,SV & CZ after ring = 9 + 5, // slash sort value = 10 + 17, // cedilla sort value = 11 + 4, // crossb sort value = 12 + 15, // dota sort value = 13 + 18, // ogonek sort value = 14 + 20, // stroke sort value = 15 + 1, // centerd sort value = 16 + 8, // macron sort value = 17 + 9, // aposab sort value = 18 + 10, // aposbes sort value = 19 + 11, // aposba sort value = 20 + 12, // aposbc sort value = 21 + 13, // abosbl sort value = 22 + 16, // dacute sort value = 23 + 21, // bara sort value = 24 + 24, // dotlesi sort value = 25 + 25 // dotlesj sort value = 26 +}; + +/**************************************************************************** +Desc: +Notes: Only 48 values + 0x40, 0x41, 0x42 (169..171) +****************************************************************************/ +FLMBYTE ColToKanaTbl[ 48] = +{ + 0, // a=0, A=1 + 2, // i=2, I=3 + 4, // u=4, U=5, VU=83 + 6, // e=6, E=7 + 8, // o=8, O=9 + 84, // KA=10, GA=11, ka=84 - remember voicing table is optimized + // so that zero value is position and + // if voice=1 and no 0 is changed to 0 + 12, // KI=12, GI=13 + 14, // KU=14, GU=15 + 85, // KE=16, GE=17, ke=85 + 18, // KO=18, GO=19 + 20, // SA=20, ZA=21 + 22, // SHI=22, JI=23 + 24, // SU=24, ZU=25 + 26, // SE=26, ZE=27 + 28, // SO=28, ZO=29 + 30, // TA=30, DA=31 + 32, // CHI=32, JI=33 + 34, // tsu=34, TSU=35, ZU=36 + 37, // TE=37, DE=38 + 39, // TO=39, DO=40 + 41, // NA + 42, // NI + 43, // NU + 44, // NE + 45, // NO + 46, // HA, BA, PA + 49, // HI, BI, PI + 52, // FU, BU, PU + 55, // HE, BE, PE + 58, // HO, BO, PO + 61, // MA + 62, // MI + 63, // MU + 64, // ME + 65, // MO + 66, // ya, YA + 68, // yu, YU + 70, // yo, YO + 72, // RA + 73, // RI + 74, // RU + 75, // RE + 76, // RO + 77, // wa, WA + 79, // WI + 80, // WE + 81, // WO + 82 // N +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +static FLMBYTE fwp_langtbl[ LAST_LANG + LAST_LANG] = +{ + 'U', 'S', // English, United States + 'A', 'F', // Afrikaans + 'A', 'R', // Arabic + 'C', 'A', // Catalan + 'H', 'R', // Croatian + 'C', 'Z', // Czech + 'D', 'K', // Danish + 'N', 'L', // Dutch + 'O', 'Z', // English, Australia + 'C', 'E', // English, Canada + 'U', 'K', // English, United Kingdom + 'F', 'A', // Farsi + 'S', 'U', // Finnish + 'C', 'F', // French, Canada + 'F', 'R', // French, France + 'G', 'A', // Galician + 'D', 'E', // German, Germany + 'S', 'D', // German, Switzerland + 'G', 'R', // Greek + 'H', 'E', // Hebrew + 'M', 'A', // Hungarian + 'I', 'S', // Icelandic + 'I', 'T', // Italian + 'N', 'O', // Norwegian + 'P', 'L', // Polish + 'B', 'R', // Portuguese, Brazil + 'P', 'O', // Portuguese, Portugal + 'R', 'U', // Russian + 'S', 'L', // Slovak + 'E', 'S', // Spanish + 'S', 'V', // Swedish + 'Y', 'K', // Ukrainian + 'U', 'R', // Urdu + 'T', 'K', // Turkey + 'J', 'P', // Japanese + 'K', 'R', // Korean + 'C', 'T', // Chinese-Traditional + 'C', 'S', // Chinese-Simplified + 'L', 'A' // Future asian language +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT bytesInBits( + FLMUINT uiBits) +{ + return( (uiBits + 7) >> 3); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBOOL testOneBit( + const FLMBYTE * pucBuf, + FLMUINT uiBit) +{ + return( (((pucBuf[ uiBit >> 3]) >> (7 - (uiBit & 7))) & 1) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMUINT getNBits( + FLMUINT uiNumBits, + const FLMBYTE * pucBuf, + FLMUINT uiBit) +{ + return(((FLMUINT)( + ((FLMUINT)pucBuf[ uiBit >> 3] << 8) | // append high bits (byte 1) to ... + (FLMUINT)pucBuf[ (uiBit >> 3) + 1]) >> // ... overflow bits in 2nd byte + (16 - uiNumBits - (uiBit & 7))) & // reposition to low end of value + ((1 << uiNumBits) - 1)); // mask off high bits +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void setBit( + FLMBYTE * pucBuf, + FLMUINT uiBit) +{ + pucBuf[ uiBit >> 3] |= (FLMBYTE)(1 << (7 - (uiBit & 7))); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void setBits( + FLMUINT uiCount, + FLMBYTE * pucBuf, + FLMUINT uiBit, + FLMUINT uiVal) +{ + pucBuf[ uiBit >> 3] |= // 1st byte + (FLMBYTE)((uiVal << (8 - uiCount)) // Align to bit 0 + >> + (uiBit & 7)); // Re-align to actual bit position + + pucBuf[ (uiBit >> 3) + 1] = // 2nd byte + (FLMBYTE)(uiVal + << + (16 - uiCount - (uiBit & 7))); // Align spill-over bits +} + +/**************************************************************************** +Desc: getNextCharState can be thought of as a 2 dimentional array with + i and j as the row and column indicators respectively. If a value + exists at the intersection of i and j, it is returned. Sparse array + techniques are used to minimize memory usage. + +Return: 0 = no valid next state + non-zero = valid next state, offset for action, or collating value +****************************************************************************/ +FINLINE FLMUINT16 getNextCharState( + FLMUINT i, + FLMUINT j) +{ + FLMUINT k, x; + + for( k = fwp_indexi[ x = + (i > START_COL) ? (START_ALL) : i ]; // adjust so don't use full tables + k <= (FLMUINT) (fwp_indexi[ x + 1] - 1); + k++ ) + { + // FIXUP_AREA_SIZE should be 24. + if( j == fwp_indexj[ k]) + { + return( fwp_valuea[ (i > START_COL) + ? (k + (FIXUP_AREA_SIZE * (i - START_ALL))) + : k]); + } + } + + return(0); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is upper case, FALSE if lower case. +****************************************************************************/ +FINLINE FLMBOOL charIsUpper( + FLMUINT16 ui16Char) +{ + return( (FLMBOOL)((ui16Char < 0x7F) + ? (FLMBOOL)((ui16Char >= ASCII_LOWER_A && + ui16Char <= ASCII_LOWER_Z) + ? (FLMBOOL)FALSE + : (FLMBOOL)TRUE) + : flmWPIsUpper( ui16Char))); +} + +/**************************************************************************** +Desc: Convert a text string to a collated string. + If NE_XFLM_CONV_DEST_OVERFLOW is returned the string is truncated as + best as it can be. The caller must decide to return the error up + or deal with the truncation. +VISIT: If the string is EXACTLY the length of the truncation + length then it should, but doesn't, set the truncation flag. + The code didn't match the design intent. Fix next major + version. +****************************************************************************/ +RCODE flmUTF8ToColText( + IF_PosIStream * pIStream, + FLMBYTE * pucCollatedStr, // Returns collated string + FLMUINT * puiCollatedStrLen, // Returns total collated string length + // Input is maximum bytes in buffer + FLMBOOL bCaseInsensitive, // Set if to convert to uppercase + FLMUINT * puiCollationLen, // Returns the collation bytes length + FLMUINT * puiCaseLen, // Returns length of case bytes + FLMUINT uiLanguage, // Language + FLMUINT uiCharLimit, // Max number of characters in this key piece + FLMBOOL bFirstSubstring, // TRUE is this is the first substring key + FLMBOOL bDataTruncated, // TRUE if data is coming in truncated. + FLMBOOL * pbOriginalCharsLost, + FLMBOOL * pbDataTruncated) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT16 ui16Base; // Value of the base character + FLMUINT16 ui16SubColVal; // Sub-collated value (diacritic) + FLMUINT uiLength; // Temporary variable for length + FLMUINT uiTargetColLen = *puiCollatedStrLen - 8; // 4=ovhd,4=worse char + + // Need to increase the buffer sizes to not overflow. + // Characaters without COLL values will take up 3 bytes in + // the ucSubColBuf[] and easily overflow the buffer. + // Hard coded the values so as to minimize changes. + + FLMBYTE ucSubColBuf[ MAX_SUBCOL_BUF + 301]; // Holds sub-collated values(diac) + FLMBYTE ucCaseBits[ MAX_CASE_BYTES + 81]; // Holds case bits + FLMUINT16 ui16WpChr; // Current WP character + FLMUNICODE uChar = 0; // Current unconverted Unicode character + FLMUNICODE uChar2; + FLMUINT16 ui16WpChr2; // 2nd character if any; default 0 for US lang + FLMUINT uiColLen; // Return value of collated length + FLMUINT uiSubColBitPos; // Sub-collation bit position + FLMUINT uiCaseBitPos; // Case bit position + FLMUINT uiFlags; // Clear all bit flags + FLMBOOL bHebrewArabic = FALSE; // Set if language is hebrew, arabic, farsi + FLMBOOL bTwoIntoOne; + FLMUINT uiUppercaseFlag; + + uiColLen = 0; + uiSubColBitPos = 0; + uiCaseBitPos = 0; + uiFlags = 0; + ui16WpChr2 = 0; + + // We don't want any single key piece to "pig out" more + // than 256 bytes of the key + + if( uiTargetColLen > 256 - 8) + { + uiTargetColLen = 256 - 8; + } + + // Code below sets ucSubColBuf[] and ucCaseBits[] values to zero. + + if (uiLanguage != XFLM_US_LANG) + { + if (uiLanguage == XFLM_AR_LANG || // Arabic + uiLanguage == XFLM_FA_LANG || // Farsi - persian + uiLanguage == XFLM_HE_LANG || // Hebrew + uiLanguage == XFLM_UR_LANG) // Urdu + { + bHebrewArabic = TRUE; + } + } + + for (;;) + { + // Set the case bits and sub-collation bits to zero when + // on the first bit of the byte. + + if (!(uiCaseBitPos & 0x07)) + { + ucCaseBits [uiCaseBitPos >> 3] = 0; + } + if (!(uiSubColBitPos & 0x07)) + { + ucSubColBuf [uiSubColBitPos >> 3] = 0; + } + + ui16SubColVal = 0; // Default sub-collation value + + // Get the next character from the string. + + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + // flmWPCheckDoubleCollation modifies ui16WpChr if a digraph or a double + // character sequence is found. If a double character is found, pucStr + // is incremented past the next character and ui16WpChr2 is set to 1. + // If a digraph is found, pucStr is not changed, but ui16WpChr + // contains the first character and ui16WpChr2 contains the second + // character of the digraph. + + if (uiLanguage != XFLM_US_LANG) + { + if( RC_BAD( rc = flmWPCheckDoubleCollation( + pIStream, FALSE, TRUE, &uChar, &uChar2, &bTwoIntoOne, uiLanguage))) + { + goto Exit; + } + if (!flmUnicodeToWP( uChar, &ui16WpChr)) + { + ui16WpChr = UNK_UNICODE_CODE; + } + if (uChar2) + { + if (!flmUnicodeToWP( uChar2, &ui16WpChr2)) + { + ui16WpChr2 = UNK_UNICODE_CODE; + } + } + else + { + ui16WpChr2 = 0; + } + } + else + { + + // Convert the character to its WP equivalent + + if( !flmUnicodeToWP( uChar, &ui16WpChr)) + { + ui16WpChr = UNK_UNICODE_CODE; + } + } + + // Save the case bit if not case-insensitive + + if (!bCaseInsensitive) + { + + // charIsUpper returns TRUE if upper case, 0 if lower case. + + if (!charIsUpper( ui16WpChr)) + { + uiFlags |= HAD_LOWER_CASE; + } + else + { + // Set if upper case. + + setBit( ucCaseBits, uiCaseBitPos); + } + uiCaseBitPos++; + } + + // Handle non-collating characters with subcollating values, + // Get the collated value from the WP character-if not collating value + + if ((pucCollatedStr[ uiColLen++] = + (FLMBYTE)(flmWPGetCollation( ui16WpChr, uiLanguage))) >= COLS11) + { + FLMUINT uiTemp; + + // If lower case, convert to upper case. + + if (!charIsUpper( ui16WpChr)) + { + ui16WpChr &= ~1; + } + + // No collating value given for this WP char. + // Save original WP char (2 bytes) in subcollating + // buffer. + + // 1110 is a new code that will store an insert over + // the character OR a non-convertable unicode character. + // Store with the same alignment as "store_extended_char" + // below. + + // 11110 is code for unmappable UNICODE value. + // A value 0xFE will be the collation value. The sub-collation + // value will be 0xFFFF followed by the UNICODE value. + // Be sure to eat an extra case bit. + + // See specific Hebrew and Arabic comments in the + // switch statement below. + + // Set the next byte that follows in the sub collation buffer. + + ucSubColBuf [(uiSubColBitPos + 8) >> 3] = 0; + if (bHebrewArabic && (pucCollatedStr[ uiColLen - 1] == COLS0_ARABIC)) + { + // Store first bit of 1110, fall through & store remaining 3 bits + + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + + // Don't store collation value + + uiColLen--; + } + else if( uChar) + { + ui16WpChr = uChar; + uChar = 0; + + // Store 11 out of 11110 + + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + if (!bCaseInsensitive) + { + ucCaseBits [(uiCaseBitPos + 7) >> 3] = 0; + + // Set upper case bit. + + setBit( ucCaseBits, uiCaseBitPos); + uiCaseBitPos++; + } + } +store_extended_char: + + // Set the next byte that follows in the sub collation buffer. + + ucSubColBuf [(uiSubColBitPos + 8) >> 3] = 0; + ucSubColBuf [(uiSubColBitPos + 16) >> 3] = 0; + uiFlags |= HAD_SUB_COLLATION; + + // Set 110 bits in sub-collation - continued from above. + // No need to explicitly set the zero, but must increment + // for it. + + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos += 2; + + // store_aligned_word: This label is not referenced. + // Go to the next byte boundary to write the character. + + uiSubColBitPos = (uiSubColBitPos + 7) & (~7); + uiTemp = bytesInBits( uiSubColBitPos); + + // Need to big-endian - so it will sort correctly. + + ucSubColBuf [uiTemp] = (FLMBYTE)(ui16WpChr >> 8); + ucSubColBuf [uiTemp + 1] = (FLMBYTE)(ui16WpChr); + uiSubColBitPos += 16; + ucSubColBuf [uiSubColBitPos >> 3] = 0; + } + else + { + // Had a collation value + // Add the lower/uppercase bit if a mixed case output. + // If not lower ASCII set - check diacritic value for sub-collation + + if( !(ui16WpChr & 0xFF00)) + { + // ASCII character set - set a single 0 bit - just need to + // increment to do this. + + uiSubColBitPos++; + } + else + { + FLMBYTE ucChar = (FLMBYTE)ui16WpChr; + FLMBYTE ucCharSet = (FLMBYTE)(ui16WpChr >> 8); + + // Convert char to uppercase because case information + // is stored above. This will help + // ensure that the "ETA" doesn't sort before "eta" + + if( !charIsUpper( ui16WpChr)) + { + ui16WpChr &= ~1; + } + + switch( ucCharSet) + { + case CHSMUL1: // Multinational 1 + { + // If we cannot break down a char into base and + // diacritic we cannot combine the charaacter + // later when converting back the key. In that case, + // write the entire WP char in the sub-collation area. + + if( flmWPBrkcar( ui16WpChr, &ui16Base, &ui16SubColVal)) + { + goto store_extended_char; + } + + // Write the FLAIM diacritic sub-collation value. + // Prefix is 2 bits "10". Remember to leave + // "111" alone for the future. + // NOTE: The "unlaut" character must sort after the "ring" + // character. + + ui16SubColVal = ((ui16SubColVal & 0xFF) == umlaut && + (uiLanguage == XFLM_SU_LANG || + uiLanguage == XFLM_SV_LANG || + uiLanguage == XFLM_CZ_LANG || + uiLanguage == XFLM_SL_LANG)) + ? (FLMUINT16)(fwp_dia60Tbl[ ring] + 1) + : (FLMUINT16)(fwp_dia60Tbl[ ui16SubColVal & 0xFF]); + +store_sub_col: + // Set the next byte that follows in the sub collation buffer. + + ucSubColBuf[ (uiSubColBitPos + 8) >> 3] = 0; + uiFlags |= HAD_SUB_COLLATION; + + // Set the 10 bits - no need to explicitly set the zero, but + // must increment for it. + + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos += 2; + + // Set sub-collation bits. + + setBits( 5, ucSubColBuf, uiSubColBitPos, ui16SubColVal); + uiSubColBitPos += 5; + break; + } + + case CHSGREK: // Greek + { + if (ucChar >= 52 || // Keep case bit for 52-69 else ignore + ui16WpChr == 0x804 || // [ 8,4] BETA Medial | Terminal + ui16WpChr == 0x826) // [ 8,38] SIGMA terminal + { + goto store_extended_char; + } + + // No subcollation to worry about - set a zero bit by + // incrementing the bit position. + + uiSubColBitPos++; + break; + } + + case CHSCYR: + { + if (ucChar >= 144) + { + goto store_extended_char; + } + + // No subcollation to worry about - set a zero bit by + // incrementing the bit position. + + uiSubColBitPos++; + + // Georgian covers 208-249 - no collation defined yet + + break; + } + + case CHSHEB: // Hebrew + { + // Three sections in Hebrew: + // 0..26 - main characters + // 27..83 - accents that apear over previous character + // 84..118- dagesh (ancient) hebrew with accents + + // Because the ancient is only used for sayings & scriptures + // we will support a collation value and in the sub-collation + // store the actual character because sub-collation is in + // character order. + + if (ucChar >= 84) // Save ancient - value 84 and above + { + goto store_extended_char; + } + + // No subcollation to worry about - set a zero bit by + // incrementing the bit position. + + uiSubColBitPos++; + break; + } + + case CHSARB1: // Arabic 1 + { + // Three sections in Arabic: + // 00..37 - accents that display OVER a previous character + // 38..46 - symbols + // 47..57 - numbers + // 58..163 - characters + // 164 - hamzah accent + // 165..180- common characters with accents + // 181..193- ligatures - common character combinations + // 194..195- extensions - throw away when sorting + + if( ucChar <= 46) + { + goto store_extended_char; // save original character + } + + if( pucCollatedStr[ uiColLen - 1] == COLS10a + 1) // Alef? + { + ui16SubColVal = (ucChar >= 165) + ? (FLMUINT16)(fwp_alefSubColTbl[ ucChar - 165 ]) + : (FLMUINT16)7; // Alef subcol value + goto store_sub_col; + } + + if (ucChar >= 181) // Ligatures - char combination + { + goto store_extended_char; // save original character + } + + if (ucChar == 64) // taa exception + { + ui16SubColVal = 8; + goto store_sub_col; + } + + // No subcollation to worry about - set a zero bit by + // incrementing the bit position. + + uiSubColBitPos++; + break; + } + + case CHSARB2: // Arabic 2 + { + // There are some characters that share the same slot + // Check the bit table if above character 64 + + if (ucChar >= 64 && + fwp_ar2BitTbl[(ucChar-64)>> 3] & (0x80 >> (ucChar&0x07))) + { + goto store_extended_char; // Will save original + } + + // No subcollation to worry about - set a zero bit by + // incrementing the bit position. + + uiSubColBitPos++; + break; + } + + default: + { + // Increment bit position to set a zero bit. + + uiSubColBitPos++; + break; + } + } + } + + // Now let's worry about double character sorting + + if (ui16WpChr2) + { + if (pbOriginalCharsLost) + { + *pbOriginalCharsLost = TRUE; + } + + // Set the next byte that follows in the sub collation buffer. + + ucSubColBuf[ (uiSubColBitPos + 7) >> 3] = 0; + + if (bTwoIntoOne) + { + + // Sorts after character in ui16WpChr after call to + // flmWPCheckDoubleCollation + // Write the char 2 times so lower/upper bits are correct. + // Could write infinite times because of collation rules. + + pucCollatedStr[ uiColLen] = ++pucCollatedStr[ uiColLen - 1]; + uiColLen++; + + // If original was upper case, set one more upper case bit + + if( !bCaseInsensitive) + { + ucCaseBits[ (uiCaseBitPos + 7) >> 3] = 0; + if( !charIsUpper( ui16WpChr2)) + { + uiFlags |= HAD_LOWER_CASE; + } + else + { + setBit( ucCaseBits, uiCaseBitPos); + } + uiCaseBitPos++; + } + + // Take into account the diacritical space + + uiSubColBitPos++; + } + else + { + + // We have a digraph, get second collation value + + pucCollatedStr[ uiColLen++] = + (FLMBYTE)(flmWPGetCollation( ui16WpChr2, uiLanguage)); + + // Normal case, assume no diacritics set + + uiSubColBitPos++; + + // If first was upper, set one more upper bit. + + if( !bCaseInsensitive) + { + ucCaseBits [(uiCaseBitPos + 7) >> 3] = 0; + if (charIsUpper( ui16WpChr)) + { + setBit( ucCaseBits, uiCaseBitPos); + } + uiCaseBitPos++; + + // no need to reset the uiFlags + } + } + } + } + + // Check to see if uiColLen is at some overflow limit. + + if (uiColLen >= uiCharLimit || + uiColLen + bytesInBits( uiSubColBitPos) + + bytesInBits( uiCaseBitPos) >= uiTargetColLen) + { + + // We hit the maximum number of characters. See if we hit the + // end of the string. + + if (RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + bDataTruncated = TRUE; + } + break; + } + } + + if (puiCollationLen) + { + *puiCollationLen = uiColLen; + } + + // Add the first substring marker - also serves as making the string non-null. + + if (bFirstSubstring) + { + pucCollatedStr[ uiColLen++] = COLL_FIRST_SUBSTRING; + } + + if (bDataTruncated) + { + pucCollatedStr[ uiColLen++ ] = COLL_TRUNCATED; + } + + // Return NOTHING if no values found + + if (!uiColLen && !uiSubColBitPos) + { + if (puiCaseLen) + { + *puiCaseLen = 0; + } + goto Exit; + } + + // Store extra zero bit in the sub-collation area for Hebrew/Arabic + + if (bHebrewArabic) + { + uiSubColBitPos++; + } + + // Done putting the string into 4 sections - build the COLLATED KEY + // Don't set uiUppercaseFlag earlier than here because SC_LOWER may be zero + + uiUppercaseFlag = (uiLanguage == XFLM_GR_LANG) + ? SC_LOWER + : SC_UPPER; + + // Did we write anything to the subcollation area? + // The default terminating characters is (COLL_MARKER|SC_UPPER) + + if (uiFlags & HAD_SUB_COLLATION) + { + // Writes out a 0x7 + + pucCollatedStr[ uiColLen++] = COLL_MARKER | SC_SUB_COL; + + // Move the sub-collation into the collating string + + uiLength = bytesInBits( uiSubColBitPos); + f_memcpy( &pucCollatedStr[ uiColLen], ucSubColBuf, uiLength); + uiColLen += uiLength; + } + + // Move the upper/lower case stuff - force bits for Greek ONLY + // This is such a small size that a memcpy is not worth it + + if( uiFlags & HAD_LOWER_CASE) + { + FLMUINT uiNumBytes = bytesInBits( uiCaseBitPos); + FLMBYTE * pucCasePtr = ucCaseBits; + + // Output the 0x5 + + pucCollatedStr[ uiColLen++] = (FLMBYTE)(COLL_MARKER | SC_MIXED); + if( puiCaseLen) + { + *puiCaseLen = uiNumBytes + 1; + } + + if( uiUppercaseFlag == SC_LOWER) + { + // Negate case bits for languages (like GREEK) that sort + // upper case before lower case. + + while( uiNumBytes--) + { + pucCollatedStr[ uiColLen++] = ~(*pucCasePtr++); + } + } + else + { + while( uiNumBytes--) + { + pucCollatedStr[ uiColLen++] = *pucCasePtr++; + } + } + } + else + { + // All characters are either upper or lower case, as determined + // by uiUppercaseFlag. + + pucCollatedStr[ uiColLen++] = (FLMBYTE)(COLL_MARKER | uiUppercaseFlag); + if( puiCaseLen) + { + *puiCaseLen = 1; + } + } + +Exit: + + if( pbDataTruncated) + { + *pbDataTruncated = bDataTruncated; + } + + *puiCollatedStrLen = uiColLen; + return( rc); +} + +/************************************************************************** +Desc: Get the Flaim collating string and convert back to a text string +Ret: Length of new wpStr +Notes: Allocates the area for the word string buffer if will be over 256. +***************************************************************************/ +RCODE flmColText2StorageText( + const FLMBYTE * pucColStr, // Points to the collated string + FLMUINT uiColStrLen, // Length of the collated string + FLMBYTE * pucStorageBuf, // Output string to build - TEXT string + FLMUINT * puiStorageLen, // In: Size of buffer, Out: Bytes used + FLMUINT uiLang, + FLMBOOL * pbDataTruncated, // Sets to TRUE if data had been truncated + FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring +{ +#define LOCAL_CHARS 150 + FLMBYTE ucWPStr[ LOCAL_CHARS * 2 + LOCAL_CHARS / 5 ]; // Sample + 20% + FLMBYTE * pucWPPtr = NULL; + FLMBYTE * pucAllocatedWSPtr = NULL; + FLMUINT uiWPStrLen; + FLMBYTE * pucStoragePtr; + FLMUINT uiUnconvChars; + FLMUINT uiTmp; + FLMUINT uiMaxStorageBytes = *puiStorageLen; + FLMUINT uiMaxWPBytes; + FLMUINT uiStorageOffset; + FLMBYTE ucTmpSen[ 5]; + FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; + RCODE rc = NE_XFLM_OK; + + if( uiColStrLen > LOCAL_CHARS) + { + // If it won't fit, allocate a new buffer + + if( RC_BAD( rc = f_alloc( MAX_KEY_SIZ * 2, &pucWPPtr))) + { + goto Exit; + } + + pucAllocatedWSPtr = pucWPPtr; + uiMaxWPBytes = uiWPStrLen = MAX_KEY_SIZ * 2; + } + else + { + pucWPPtr = &ucWPStr[ 0]; + uiMaxWPBytes = uiWPStrLen = sizeof( ucWPStr); + } + + if( (uiLang >= FIRST_DBCS_LANG) && + (uiLang <= LAST_DBCS_LANG)) + { + if( RC_BAD( rc = flmAsiaColStr2WPStr( pucColStr, uiColStrLen, + pucWPPtr, &uiWPStrLen, &uiUnconvChars, + pbDataTruncated, pbFirstSubstring))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmColStr2WPStr( pucColStr, uiColStrLen, + pucWPPtr, &uiWPStrLen, uiLang, &uiUnconvChars, + pbDataTruncated, pbFirstSubstring))) + { + goto Exit; + } + } + + // Copy word string to the storage string area + + uiWPStrLen >>= 1; // Convert # of bytes to # of words + pucStoragePtr = pucStorageBuf; + uiStorageOffset = 0; + + // Encode the number of characters as a SEN. If pucEncPtr is + // NULL, the caller is only interested in the length of the encoded + // string, so a temporary buffer is used to call flmEncodeSEN. + + uiTmp = flmEncodeSEN( uiWPStrLen - uiUnconvChars, &pucTmpSen); + if( (uiStorageOffset + uiTmp) >= uiMaxStorageBytes) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucStoragePtr, &ucTmpSen[ 0], uiTmp); + uiStorageOffset += uiTmp; + + // Encode each of the WP characters into UTF-8 + + while( uiWPStrLen--) + { + FLMBYTE ucChar; + FLMBYTE ucCharSet; + FLMUNICODE uChar; + + // Put the character in a local variable for speed + + ucChar = *pucWPPtr++; + ucCharSet = *pucWPPtr++; + + if( ucCharSet == 0xFF && ucChar == 0xFF) + { + uChar = (((FLMUNICODE)*(pucWPPtr + 1)) << 8) | *pucWPPtr; + pucWPPtr += 2; + uiWPStrLen--; // Skip past 4 bytes for UNICODE + } + else + { + if( RC_BAD( rc = flmWPToUnicode( + (((FLMUINT16)ucCharSet) << 8) + ucChar, &uChar))) + { + goto Exit; + } + } + + uiTmp = uiMaxStorageBytes - uiStorageOffset; + if( RC_BAD( rc = flmUni2UTF8( uChar, + &pucStorageBuf[ uiStorageOffset], &uiTmp))) + { + goto Exit; + } + uiStorageOffset += uiTmp; + } + + if( uiStorageOffset >= uiMaxStorageBytes) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Tack on a trailing NULL byte + + pucStorageBuf[ uiStorageOffset++] = 0; + + // Return the length of the storage buffer + + *puiStorageLen = uiStorageOffset; + +Exit: + + if( pucAllocatedWSPtr) + { + f_free( &pucAllocatedWSPtr); + } + + return( rc); +} + +/***************************************************************************** +Desc: Convert a collated string to a WP word string +*****************************************************************************/ +FSTATIC RCODE flmColStr2WPStr( + const FLMBYTE * pucColStr, // Points to the collated string + FLMUINT uiColStrLen, // Length of the collated string + FLMBYTE * pucWPStr, // Output string to build - WP word string + FLMUINT * puiWPStrLen, + FLMUINT uiLang, + FLMUINT * puiUnconvChars, + FLMBOOL * pbDataTruncated, // Set to TRUE if truncated + FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring +{ + FLMBYTE * pucWPPtr = pucWPStr; // Points to the word string data area + FLMBYTE * pucWPEnd = &pucWPPtr[ *puiWPStrLen]; + FLMUINT uiMaxWPBytes = *puiWPStrLen; + FLMUINT uiLength = uiColStrLen; // May optimize as a register + FLMUINT uiPos = 0; // Position in pucColStr + FLMUINT uiBitPos; // Computed bit position + FLMUINT uiColChar; // Not portable if a FLMBYTE value + FLMUINT uiWPStrLen; + FLMUINT uiUnconvChars = 0; + FLMBOOL bHebrewArabic = FALSE; + RCODE rc = NE_XFLM_OK; + + // WARNING: + // The code is duplicated for performance reasons. + // The US code below is much more optimized so + // any changes must be done twice. + + if( uiLang == XFLM_US_LANG) + { + while( uiLength && (pucColStr[ uiPos] > MAX_COL_OPCODE)) + { + uiLength--; + + // Move in the WP value given uppercase collated value + + uiColChar = (FLMUINT)pucColStr[ uiPos++]; + if( uiColChar == COLS0) + { + uiColChar = (FLMUINT)0xFFFF; + uiUnconvChars++; + } + else + { + uiColChar = (FLMUINT)colToWPChr[ uiColChar - COLLS]; + } + + // Put the WP char in the word string + + if( pucWPPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( (FLMUINT16)uiColChar, pucWPPtr); + pucWPPtr += 2; + } + } + else // Non-US collation + { + if( (uiLang == XFLM_AR_LANG ) || // Arabic + (uiLang == XFLM_FA_LANG ) || // Farsi - Persian + (uiLang == XFLM_HE_LANG ) || // Hebrew + (uiLang == XFLM_UR_LANG)) // Urdu + { + bHebrewArabic = TRUE; + } + + while( uiLength && (pucColStr[ uiPos] > MAX_COL_OPCODE)) + { + uiLength--; + uiColChar = (FLMUINT)pucColStr[ uiPos++]; + + switch( uiColChar) + { + case COLS9+4: // ch in spanish + case COLS9+11: // ch in czech + { + // Put the WP char in the word string + + if( pucWPPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( (FLMUINT16) 'C', pucWPPtr); + pucWPPtr += 2; + uiColChar = (FLMUINT)'H'; + uiPos++; // Move past second duplicate char + break; + } + + case COLS9+17: // ll in spanish + { + // Put the WP char in the word string + + if( pucWPPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( (FLMUINT16)'L', pucWPPtr); + pucWPPtr += 2; + uiColChar = (FLMUINT)'L'; + uiPos++; // Move past duplicate character + break; + } + + case COLS0: // Non-collating character or OEM character + { + // Actual character is in sub-collation area + + uiColChar = (FLMUINT)0xFFFF; + uiUnconvChars++; + break; + } + + default: + { + // Watch out COLS10h has () around it for subtraction + + if( bHebrewArabic && (uiColChar >= COLS10h)) + { + uiColChar = (uiColChar < COLS10a) // Hebrew only? + ? (FLMUINT) (0x900 + (uiColChar - (COLS10h))) // Hebrew + : (FLMUINT) (HebArabColToWPChr[ uiColChar - (COLS10a)]); // Arabic + } + else + { + uiColChar = (FLMUINT)colToWPChr[ uiColChar - COLLS]; + } + break; + } + } + + // Put the WP char in the word string + + if( pucWPPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( (FLMUINT16)uiColChar, pucWPPtr); + pucWPPtr += 2; + } + } + + // Terminate the string + + if( pucWPPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( (FLMUINT16)0, pucWPPtr); + uiWPStrLen = uiPos + uiPos; // Multiply by 2 + + // Parse through the sub-collation and case information. + // Here are values for some of the codes: + // [ 0x04] - case information is all uppercase (IS,DK,GR) + // [ 0x05] - case bits follow + // [ 0x06] - case information is all uppercase + // [ 0x07] - beginning of sub-collation information + // [ 0x08] - first substring field that is made + // [ 0x09] - truncation marker for text and binary + // + // Below are some cases to consider... + // + // [ COLLATION][ 0x07 sub-collation][ 0x05 case info] + // [ COLLATION][ 0x07 sub-collation][ 0x05 case info] + // [ COLLATION][ 0x07 sub-collation] + // [ COLLATION][ 0x07 sub-collation] + // [ COLLATION][ 0x05 case info] + // [ COLLATION][ 0x05 case info] + // [ COLLATION] + // [ COLLATION] + // + // In the future still want[ 0x06] to be compressed out for uppercase + // only indexes. + + // Check first substring before truncated + + if( uiLength && pucColStr[ uiPos] == COLL_FIRST_SUBSTRING) + { + if( pbFirstSubstring) + { + *pbFirstSubstring = TRUE; // Don't need to initialize to FALSE. + } + uiLength--; + uiPos++; + } + + // Is the key truncated? + + if( uiLength && pucColStr[ uiPos] == COLL_TRUNCATED) + { + if( pbDataTruncated) + { + *pbDataTruncated = TRUE; // Don't need to initialize to FALSE. + } + uiLength--; + uiPos++; + } + + // Does sub-collation follow? + // Still more to process - first work on the sub-collation (diacritics) + // Hebrew/Arabic may have empty collation area + + if( uiLength && (pucColStr[ uiPos] == (COLL_MARKER | SC_SUB_COL))) + { + FLMUINT uiTempLen; + + // Do another pass on the word string adding the diacritics + + if( RC_BAD( rc = flmWPCmbSubColBuf( pucWPStr, &uiWPStrLen, uiMaxWPBytes, + &pucColStr[ ++uiPos], bHebrewArabic, &uiBitPos))) + { + goto Exit; + } + + // Move pos to next byte value + + uiTempLen = bytesInBits( uiBitPos); + uiPos += uiTempLen; + uiLength -= uiTempLen + 1; // The 1 includes the 0x07 byte + } + + // Does the case info follow? + + if( uiLength && (pucColStr[ uiPos] >= 0x04)) + { + // Take care of the lower and upper case conversion + // If mixed case then convert using case bits + + if( pucColStr[ uiPos++] & SC_MIXED) // Increment pos here! + { + // Don't pre-increment pos on line below! + uiPos += flmWPToMixed( pucWPStr, uiWPStrLen, + &pucColStr[ uiPos], uiLang); + } + // else 0x04 or 0x06 - all characters already in uppercase + } + + // Should end perfectly at the end of the collation buffer. + + if (uiPos != uiColStrLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + *puiWPStrLen = uiWPStrLen; + *puiUnconvChars = uiUnconvChars; + +Exit: + + return( rc); +} + +/************************************************************************** +Desc: Combine the diacritic 5-bit values to an existing WP string +***************************************************************************/ +FSTATIC RCODE flmWPCmbSubColBuf( + FLMBYTE * pucWPStr, // Existing WP string to modify + FLMUINT * puiWPStrLen, // WP string length in bytes + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucSubColBuf, // Diacritic values in 5 bit sets + FLMBOOL bHebrewArabic, // Set if language is Hebrew or Arabic + FLMUINT * puiSubColBitPos) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSubColBitPos = 0; + FLMUINT uiNumChars = *puiWPStrLen >> 1; + FLMUINT16 ui16Diac; + FLMUINT16 ui16WPChar; + FLMUINT uiTemp; + + // For each character (two bytes) in the WP string ... + + while( uiNumChars--) + { + // Label used for hebrew/arabic - additional subcollation can follow + // This macro DOESN'T increment bitPos + + if( testOneBit( pucSubColBuf, uiSubColBitPos)) + { + // If "11110" - unmappable unicode char - 0xFFFF is before it + // If "1110" then INDEX extended char is inserted + // If "110" then extended char follows that replaces collation + // If "10" then take next 5 bits which + // contain the diacritic subcollation value. + +after_last_character: + + uiSubColBitPos++; // Eat the first 1 bit + if( !testOneBit( pucSubColBuf, uiSubColBitPos)) + { + uiSubColBitPos++; // Eat the 0 bit + ui16Diac = (FLMUINT16)(getNBits( 5, pucSubColBuf, uiSubColBitPos)); + uiSubColBitPos += 5; + + // If not extended base + + if( (ui16WPChar = FB2UW( pucWPStr)) < 0x100) + { + // Convert to WP diacritic and combine characters + + flmWPCmbcar( &ui16WPChar, ui16WPChar, + (FLMUINT16)ml1_COLtoD[ ui16Diac]); + + // Even if cmbcar fails, wpchar is still set to a valid value + + UW2FBA( ui16WPChar, pucWPStr); + } + else if( (ui16WPChar & 0xFF00) == 0x0D00) // Arabic? + { + ui16WPChar = ArabSubColToWPChr[ ui16Diac]; + UW2FBA( ui16WPChar, pucWPStr); + } + // else diacritic is extra info + // cmbcar should not handle extended chars for this design + } + else // "110" or "1110" or "11110" + { + uiSubColBitPos++; // Eat the 2nd '1' bit + if( testOneBit( pucSubColBuf, uiSubColBitPos)) // Test the 3rd bit + { + if( (*puiWPStrLen) + 2 > uiMaxWPBytes) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // 1110 - shift wpchars down 1 word and insert value below + uiSubColBitPos++; // Eat the 3rd '1' bit + *puiWPStrLen += 2; // Return 2 more bytes + + if( testOneBit( pucSubColBuf, uiSubColBitPos)) // Test 4th bit + { + // Unconvertable UNICODE character + // The format will be 4 bytes, 0xFF, 0xFF, 2 byte Unicode + + shiftN( pucWPStr, uiNumChars + uiNumChars + 4, 2); + uiSubColBitPos++; // Eat the 4th '1' bit + pucWPStr += 2; // Skip the 0xFFFF for now + } + else + { + // Move down 2 byte NULL and rest of the 2 byte characters + // The extended character does not have a 0xFF col value + + shiftN( pucWPStr, uiNumChars + uiNumChars + 2, 2); + uiNumChars++; // Increment because inserted + + // Fall through reading the actual charater value + } + } + + uiSubColBitPos++; // Skip past the zero bit + uiSubColBitPos = (uiSubColBitPos + 7) & (~7); // roundup to next byte + uiTemp = bytesInBits( uiSubColBitPos); // compute position + pucWPStr[ 1] = pucSubColBuf[ uiTemp]; // Character set + pucWPStr[ 0] = pucSubColBuf[ uiTemp + 1]; // Character + uiSubColBitPos += 16; + } + } + else + { + uiSubColBitPos++; + } + + pucWPStr += 2; // Next WP character + } + + if( bHebrewArabic) + { + if( testOneBit( pucSubColBuf, uiSubColBitPos)) + { + // Hebrew/Arabic can have trailing accents that + // don't have a matching collation value. + // Keep looping in this case. + // Note that subColBitPos isn't incremented above. + + uiNumChars = 0; // Set so we won't loop forever! + goto after_last_character; // process trailing bit + } + uiSubColBitPos++; // Eat the last '0' bit + } + + *puiSubColBitPos = uiSubColBitPos; + +Exit: + + return( rc); +} + +/************************************************************************** +Desc: Convert the WP string to lower case chars given low/up bit string +Out: WP characters that have been modified to their original case +Ret: Number of bytes used in the lower/upper buffer +Notes: Only WP to lower case conversion is done here for each bit NOT set. +***************************************************************************/ +FSTATIC FLMUINT flmWPToMixed( + FLMBYTE * pucWPStr, // Existing WP string to modify + FLMUINT uiWPStrLen, // Length of the WP string in bytes + const FLMBYTE * pucLowUpBitStr, // Lower/upper case bit string + FLMUINT uiLang) +{ + FLMUINT uiNumChars; + FLMUINT uiTempWord; + FLMBYTE ucTempByte = 0; + FLMBYTE ucMaskByte; + FLMBYTE ucXorByte; // Used to reverse GR, bits + + ucXorByte = (uiLang == XFLM_US_LANG) // Do most common compare first + ? (FLMBYTE)0 + : (uiLang == XFLM_GR_LANG) // Greek has uppercase first + ? (FLMBYTE)0xFF + : (FLMBYTE)0 ; + + // For each character (two bytes) in the word string ... + for( uiNumChars = uiWPStrLen >> 1, + ucMaskByte = 0; // Force first time to get a byte + uiNumChars--; + pucWPStr += 2, // Next WP character - word + ucMaskByte >>= 1) // Next bit to mask and check + { + if( ucMaskByte == 0) + { + // Time to get another byte + + ucTempByte = ucXorByte ^ *pucLowUpBitStr++; + ucMaskByte = 0x80; + } + + // If lowercase convert, else is upper + + if( (ucTempByte & ucMaskByte) == 0) + { + // Convert to lower case - COLL -> WP is already in upper case + + uiTempWord = (FLMUINT) FB2UW( pucWPStr); + if( uiTempWord >= ASCII_UPPER_A && uiTempWord <= ASCII_UPPER_Z) + { + uiTempWord |= 0x20; + } + else + { + FLMBYTE ucCharVal = (FLMBYTE)( uiTempWord & 0xFF); + FLMBYTE ucCharSet = (FLMBYTE)( uiTempWord >> 8); + + // Check if charact within region of character set + + if( ((ucCharSet == CHSMUL1) && + ((ucCharVal >= 26) && (ucCharVal <= 241))) || + ((ucCharSet == CHSGREK) && (ucCharVal <= 69)) || + ((ucCharSet == CHSCYR) && (ucCharVal <= 199))) + { + uiTempWord |= 0x01; // Set the bit ... don't increment! + } + } + UW2FBA( (FLMUINT16)uiTempWord, pucWPStr); + } + } + + uiNumChars = uiWPStrLen >> 1; + return( bytesInBits( uiNumChars)); +} + +/**************************************************************************** +Desc: Converts a character to upper case (if possible) +****************************************************************************/ +FLMUINT16 flmWPUpper( + FLMUINT16 ui16WpChar) +{ + if( ui16WpChar < 256) + { + if( ui16WpChar >= ASCII_LOWER_A && ui16WpChar <= ASCII_LOWER_Z) + { + // Return ASCII upper case + + return( ui16WpChar & 0xdf); + } + } + else + { + FLMBYTE ucCharSet = ui16WpChar >> 8; + + if( ucCharSet == CHSMUL1) + { + FLMBYTE ucChar = ui16WpChar & 0xFF; + + if( ucChar >= fwp_caseConvertableRange[ (CHSMUL1-1) * 2] && + ucChar <= fwp_caseConvertableRange[ ((CHSMUL1-1) * 2) + 1]) + { + return( ui16WpChar & 0xFFFE); + } + } + else if( ucCharSet == CHSGREK) + { + if( (ui16WpChar & 0xFF) <= + fwp_caseConvertableRange[ ((CHSGREK-1) * 2) + 1]) + { + return( ui16WpChar & 0xFFFE); + } + } + else if( ucCharSet == CHSCYR) + { + if( (ui16WpChar & 0xFF) <= + fwp_caseConvertableRange[ ((CHSCYR-1) * 2) + 1]) + { + return( ui16WpChar & 0xFFFE); + } + } + else if( ui16WpChar >= Lower_JP_a) + { + // Possible double byte character set alphabetic character? + + if( ui16WpChar <= Lower_JP_z) + { + // Japanese? + + ui16WpChar = (ui16WpChar - Lower_JP_a) + Upper_JP_A; + } + else if( ui16WpChar >= Lower_KR_a && ui16WpChar <= Lower_KR_z) + { + // Korean? + + ui16WpChar = (ui16WpChar - Lower_KR_a) + Upper_KR_A; + } + else if( ui16WpChar >= Lower_CS_a && ui16WpChar <= Lower_CS_z) + { + // Chinese Simplified? + + ui16WpChar = (ui16WpChar - Lower_CS_a) + Upper_CS_A; + } + else if( ui16WpChar >= Lower_CT_a && ui16WpChar <= Lower_CT_z) + { + // Chinese Traditional? + + ui16WpChar = (ui16WpChar - Lower_CT_a) + Upper_CT_A; + } + } + } + + // Return original character - original not in lower case. + + return( ui16WpChar); +} + +/**************************************************************************** +Desc: Checks to see if WP character is upper case +****************************************************************************/ +FLMBOOL flmWPIsUpper( + FLMUINT16 ui16WpChar) +{ + FLMBYTE ucChar; + FLMBYTE ucCharSet; + + // Get character + + ucChar = (FLMBYTE)(ui16WpChar & 0xFF); + + // Test if ASCII character set + + if( !(ui16WpChar & 0xFF00)) + { + return( (ucChar >= ASCII_LOWER_A && ucChar <= ASCII_LOWER_Z) + ? FALSE + : TRUE); + } + + // Get the character set + + ucCharSet = (FLMBYTE) (ui16WpChar >> 8); + + // CHSMUL1 == Multinational 1 character set + // CHSGREK == Greek character set + // CHSCYR == Cyrillic character set + + if( (ucCharSet == CHSMUL1 && ucChar >= 26 && ucChar <= 241) || + (ucCharSet == CHSGREK && ucChar <= 69) || + (ucCharSet == CHSCYR && ucChar <= 199)) + { + return( (ucChar & 1) ? FALSE : TRUE); + } + + // Don't care that double ss is lower + + return( TRUE); +} + +/**************************************************************************** +Desc: Converts a character to lower case (if possible) +****************************************************************************/ +FLMUINT16 flmWPLower( + FLMUINT16 ui16WpChar) +{ + if( ui16WpChar < 256) + { + if( ui16WpChar >= ASCII_UPPER_A && ui16WpChar <= ASCII_UPPER_Z) + { + return( ui16WpChar | 0x20); + } + } + else + { + FLMBYTE ucCharSet = ui16WpChar >> 8; + + if( ucCharSet == CHSMUL1) + { + FLMBYTE ucChar = ui16WpChar & 0xFF; + + if( ucChar >= fwp_caseConvertableRange[ (CHSMUL1-1) * 2] && + ucChar <= fwp_caseConvertableRange[ ((CHSMUL1-1) * 2) + 1] ) + { + return( ui16WpChar | 1); + } + } + else if( ucCharSet == CHSGREK) + { + if( (ui16WpChar & 0xFF) <= + fwp_caseConvertableRange[ ((CHSGREK-1) * 2) + 1]) + { + return( ui16WpChar | 1); + } + } + else if( ucCharSet == CHSCYR) + { + if( (ui16WpChar & 0xFF) <= + fwp_caseConvertableRange[ ((CHSCYR-1) * 2) + 1]) + { + return( ui16WpChar | 1); + } + } + else if( ui16WpChar >= Upper_JP_A) + { + // Possible double byte character set alphabetic character? + + if( ui16WpChar <= Upper_JP_Z) + { + // Japanese? + + ui16WpChar = ui16WpChar - Upper_JP_A + Lower_JP_a; + } + else if( ui16WpChar >= Upper_KR_A && ui16WpChar <= Upper_KR_Z) + { + // Korean? + + ui16WpChar = ui16WpChar - Upper_KR_A + Lower_KR_a; + } + else if( ui16WpChar >= Upper_CS_A && ui16WpChar <= Upper_CS_Z) + { + // Chinese Simplified? + + ui16WpChar = ui16WpChar - Upper_CS_A + Lower_CS_a; + } + else if( ui16WpChar >= Upper_CT_A && ui16WpChar <= Upper_CT_Z) + { + // Chinese Traditional? + + ui16WpChar = ui16WpChar - Upper_CT_A + Lower_CT_a; + } + } + } + + // Return original character, original not in upper case + + return( ui16WpChar); +} + +/**************************************************************************** +Desc: Break a WP character into a base and a diacritical char. +Ret: TRUE - if not found + FALSE - if found +****************************************************************************/ +FLMBOOL flmWPBrkcar( + FLMUINT16 ui16WpChar, + FLMUINT16 * pui16BaseChar, + FLMUINT16 * pui16DiacriticChar) +{ + BASE_DIACRIT * pBaseDiacritic; + FLMINT iTableIndex; + + if( (pBaseDiacritic = fwp_car60_c[ HI(ui16WpChar)]) == 0) + { + return( TRUE); + } + + iTableIndex = ((FLMBYTE)ui16WpChar) - pBaseDiacritic->start_char; + if( iTableIndex < 0 || + iTableIndex > pBaseDiacritic->char_count || + pBaseDiacritic->table [iTableIndex].base == (FLMBYTE)0xFF) + { + return( TRUE); + } + + if( (HI( ui16WpChar) != CHSMUL1) || + ((fwp_ml1_cb60[ ((FLMBYTE) ui16WpChar) >> 3] >> + (7 - (ui16WpChar & 0x07))) & 0x01)) + { + + // normal case, same base as same as characters + + *pui16BaseChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table [iTableIndex].base; + *pui16DiacriticChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table[iTableIndex].diacrit; + } + else + { + + // Multi-national where base is ascii value. + + *pui16BaseChar = pBaseDiacritic->table [iTableIndex].base; + *pui16DiacriticChar = (ui16WpChar & 0xFF00) | + pBaseDiacritic->table[iTableIndex].diacrit; + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Take a base and a diacritic and compose a WP character. + Note on base character: i's and j's must be dotless i's and j's (for + those which use them) or they will not be found. +Ret: TRUE - if not found + FALSE - if found +Notes: ascii characters with diacriticals are in multi-national if anywhere; + all other base chars with diacritics are found in their own sets. +****************************************************************************/ +FSTATIC FLMBOOL flmWPCmbcar( + FLMUINT16 * pui16WpChar, + FLMUINT16 ui16BaseChar, + FLMINT16 ui16DiacriticChar) +{ + FLMUINT uiRemaining; + FLMBYTE ucCharSet; + FLMBYTE ucChar; + BASE_DIACRIT * pBaseDiacritic; + BASE_DIACRIT_TABLE * pTable; + + ucCharSet = HI( ui16BaseChar); + if( ucCharSet > WP_MAX_CAR60_SIZE) + { + return( TRUE); + } + + // Is base ASCII? If so, look in multinational 1 + + if( !ucCharSet) + { + ucCharSet = CHSMUL1; + } + + if( (pBaseDiacritic = fwp_car60_c[ucCharSet]) == 0) + { + return( TRUE); + } + + ucChar = LO( ui16BaseChar); + ui16DiacriticChar = LO( ui16DiacriticChar); + pTable = pBaseDiacritic->table; + for( uiRemaining = pBaseDiacritic->char_count; + uiRemaining; + uiRemaining--, pTable++ ) + { + // Same base? + + if( pTable->base == ucChar && + (pTable->diacrit & 0x7F) == ui16DiacriticChar) + { + // Same diacritic? + + *pui16WpChar = (FLMUINT16) (((FLMUINT16) ucCharSet << 8) + + (pBaseDiacritic->start_char + + (FLMUINT16)(pTable - pBaseDiacritic->table))); + return( FALSE); + } + } + + return( TRUE); +} + +/************************************************************************** +Desc: Find the collating value of a WP character +ret: Collating value (COLS0 is high value - undefined WP char) +***********************************************************************/ +FLMUINT16 flmWPGetCollation( + FLMUINT16 ui16WpChar, + FLMUINT uiLanguage) +{ + FLMUINT16 ui16State; + FLMBYTE ucCharVal; + FLMBYTE ucCharSet; + FLMBOOL bHebrewArabicFlag = FALSE; + TBL_B_TO_BP * pColTbl = fwp_col60Tbl; + + // State ONLY for non-US + + if( uiLanguage != XFLM_US_LANG) + { + if( uiLanguage == XFLM_AR_LANG || // Arabic + uiLanguage == XFLM_FA_LANG || // Farsi - persian + uiLanguage == XFLM_HE_LANG || // Hebrew + uiLanguage == XFLM_UR_LANG) // Urdu + { + pColTbl = fwp_HebArabicCol60Tbl; + bHebrewArabicFlag = TRUE; + } + else + { + // check if uiLanguage candidate for alternate double collating + + ui16State = getNextCharState( START_COL, uiLanguage); + if( 0 != (ui16State = getNextCharState( (ui16State + ? ui16State // look at special case languages + : START_ALL), // look at US and European + (FLMUINT) ui16WpChar))) + { + return( ui16State); + } + } + } + + ucCharVal = (FLMBYTE)ui16WpChar; + ucCharSet = (FLMBYTE)(ui16WpChar >> 8); + + do + { + if( pColTbl->key == ucCharSet) + { + FLMBYTE * pucColVals; // table of collating values + + pucColVals = pColTbl->charPtr; + + // Check if the value is in the range of collated chars + // Above lower range of table? + + if (ucCharVal >= *pucColVals) + { + // Make value zero based to index + + ucCharVal -= *pucColVals++; + + // Below maximum number of table entries? + + if( ucCharVal < *pucColVals++) + { + // Return collated value. + + return( pucColVals[ ucCharVal]); + } + } + } + + // Go to next table entry + + pColTbl++; + } while( pColTbl->key != 0xFF); + + if( bHebrewArabicFlag) + { + if( ucCharSet == CHSHEB || + ucCharSet == CHSARB1 || + ucCharSet == CHSARB2) + { + // Same as COLS0_HEBREW + + return( COLS0_ARABIC); + } + } + + // Defaults for characters that don't have a collation value. + + return( COLS0); +} + +/**************************************************************************** +Desc: Check for double characters that sort as 1 (like ch in Spanish) or + 1 character that should sort as 2 (like ? sorts as ae in French). +Return: 0 = nothing changes + 1 if sorting 2 characters as 1 - *pui16WpChar is the one character. + second character value if 1 character sorts as 2, + *pui16WpChar changes to first character in sequence +****************************************************************************/ +RCODE flmWPCheckDoubleCollation( + IF_PosIStream * pIStream, + FLMBOOL bUnicodeStream, + FLMBOOL bAllowTwoIntoOne, + FLMUNICODE * puzChar, + FLMUNICODE * puzChar2, + FLMBOOL * pbTwoIntoOne, + FLMUINT uiLanguage) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT16 ui16CurState; + FLMUINT16 ui16WpChar; + FLMUNICODE uzLastChar = 0; + FLMUNICODE uChar = *puzChar; + FLMUNICODE uDummy; + FLMBOOL bUpperFlag; + FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); + + if (!flmUnicodeToWP( *puzChar, &ui16WpChar)) + { + ui16WpChar = UNK_UNICODE_CODE; + } + bUpperFlag = flmWPIsUpper( ui16WpChar); + + if ((ui16CurState = getNextCharState( 0, uiLanguage)) == 0) + { + *pbTwoIntoOne = FALSE; + *puzChar2 = 0; + goto Exit; + } + + for (;;) + { + switch (ui16CurState) + { + case INSTSG: + *puzChar = *puzChar2 = (FLMUNICODE)f_toascii( 's'); + *pbTwoIntoOne = FALSE; + goto Exit; + case INSTAE: + if (bUpperFlag) + { + *puzChar = (FLMUNICODE)f_toascii( 'A'); + *puzChar2 = (FLMUNICODE)f_toascii( 'E'); + } + else + { + *puzChar = (FLMUNICODE)f_toascii( 'a'); + *puzChar2 = (FLMUNICODE)f_toascii( 'e'); + } + *pbTwoIntoOne = FALSE; + goto Exit; + case INSTIJ: + if (bUpperFlag) + { + *puzChar = (FLMUNICODE)f_toascii( 'I'); + *puzChar2 = (FLMUNICODE)f_toascii( 'J'); + } + else + { + *puzChar = (FLMUNICODE)f_toascii( 'i'); + *puzChar2 = (FLMUNICODE)f_toascii( 'j'); + } + *pbTwoIntoOne = FALSE; + goto Exit; + case INSTOE: + if (bUpperFlag) + { + *puzChar = (FLMUNICODE)f_toascii( 'O'); + *puzChar2 = (FLMUNICODE)f_toascii( 'E'); + } + else + { + *puzChar = (FLMUNICODE)f_toascii( 'o'); + *puzChar2 = (FLMUNICODE)f_toascii( 'e'); + } + *pbTwoIntoOne = FALSE; + goto Exit; + case WITHAA: + *puzChar = (FLMUNICODE)(bUpperFlag + ? (FLMUNICODE)0xC5 + : (FLMUNICODE)0xE5); + + if (RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) + { + goto Exit; + } + + if( bUnicodeStream) + { + rc = pIStream->read( &uDummy, sizeof( FLMUNICODE), NULL); + } + else + { + rc = flmReadUTF8CharAsUnicode( pIStream, &uDummy); + } + + if( RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + ui64SavePosition = pIStream->getCurrPosition(); + break; + case AFTERC: + *puzChar = (FLMUINT16)(bUpperFlag + ? (FLMUNICODE)f_toascii( 'C') + : (FLMUNICODE)f_toascii( 'c')); +Position_After_2nd: + + if( bAllowTwoIntoOne) + { + *puzChar2 = uzLastChar; + *pbTwoIntoOne = TRUE; + + if (RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) + { + goto Exit; + } + + if( bUnicodeStream) + { + rc = pIStream->read( &uChar, sizeof( FLMUNICODE), NULL); + } + else + { + rc = flmReadUTF8CharAsUnicode( pIStream, &uChar); + } + + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + ui64SavePosition = pIStream->getCurrPosition(); + } + goto Exit; + case AFTERH: + *puzChar = (FLMUINT16)(bUpperFlag + ? (FLMUNICODE)f_toascii( 'H') + : (FLMUNICODE)f_toascii( 'h')); + goto Position_After_2nd; + case AFTERL: + *puzChar = (FLMUINT16)(bUpperFlag + ? (FLMUNICODE)f_toascii( 'L') + : (FLMUNICODE)f_toascii( 'l')); + goto Position_After_2nd; + default: + // Handles STATE1 through STATE11 also + break; + } + + if ((ui16CurState = getNextCharState( ui16CurState, + flmWPLower( ui16WpChar))) == 0) + { + break; + } + + uzLastChar = uChar; + + if( bUnicodeStream) + { + rc = pIStream->read( &uChar, sizeof( FLMUNICODE), NULL); + } + else + { + rc = flmReadUTF8CharAsUnicode( pIStream, &uChar); + } + + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if (!flmUnicodeToWP( uChar, &ui16WpChar)) + { + ui16WpChar = UNK_UNICODE_CODE; + } + } + +Exit: + + if (RC_OK( rc)) + { + rc = pIStream->positionTo( ui64SavePosition); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the collation value of the input WP character. + If in charset 11 will convert the character to Zenkaku (double wide). +In: ui16WpChar - Char to collate off of - could be in CS0..14 or x24..up + ui16NextWpChar - next WP char for CS11 voicing marks + ui16PrevColValue - previous collating value - for repeat/vowel repeat + pui16ColValue - returns 2 byte collation value + pui16SubColVal - 0, 6 or 16 bit value for the latin sub collation + or the kana size & vowel voicing + 001 - set if large (upper) character + 010 - set if voiced + 100 - set if half voiced + + pucCaseBits - returns 2 bits + Latin/Greek/Cyrillic + 01 - case bit set if character is uppercase + 10 - double wide character in CS 0x25xx, 0x26xx and 0x27xx + Japanese + 00 - double wide hiragana 0x255e..25b0 + 01 - double wide katakana 0x2600..2655 + 10 - double wide symbols that map to charset 11 + 11 - single wide katakana from charset 11 +Ret: 0 - no valid collation value + high values set for pui16ColValue + Sub-collation gets original WP character value + 1 - valid collation value + 2 - valid collation value and used the ui16NextWpChar + +Notes: Code taken from XCH2COL.ASM - routine xch2col_f + also from CMPWS.ASM - routine getcase +Terms: + HANKAKU - single wide characters in charsets 0..14 + ZENKAKU - double wide characters in charsets 0x24..end of kanji + KANJI - collation values are 0x2900 less than WPChar value + +****************************************************************************/ +FLMUINT16 flmWPAsiaGetCollation( + FLMUINT16 ui16WpChar, // WP char to get collation values + FLMUINT16 ui16NextWpChar, // Next WP char - for CS11 voicing marks + FLMUINT16 ui16PrevColValue, // Previous collating value + FLMUINT16 * pui16ColValue, // Returns collation value + FLMUINT16 * pui16SubColVal, // Returns sub-collation value + FLMBYTE * pucCaseBits, // Returns case bits value + FLMBOOL bUppercaseFlag) // Set if to convert to uppercase +{ + FLMUINT16 ui16ColValue; + FLMUINT16 ui16SubColVal; + FLMBYTE ucCaseBits = 0; + FLMBYTE ucCharSet = ui16WpChar >> 8; + FLMBYTE ucCharVal = ui16WpChar & 0xFF; + FLMUINT16 ui16Hankaku; + FLMUINT uiLoop; + FLMUINT16 ui16ReturnValue = 1; + + ui16ColValue = ui16SubColVal = 0; + + // Kanji or above + + if( ucCharSet >= 0x2B) + { + // Puts 2 or above into high byte. + + ui16ColValue = ui16WpChar - 0x2900; + + // No subcollation or case bits need to be set + + goto Exit; + } + + // Single wide character? (HANKAKU) + + if( ucCharSet < 11) + { + // Get the values from a non-asian character + // LATIN, GREEK or CYRILLIC + // The width bit may have been set on a jump to + // label from below. + +Latin_Greek_Cyrillic: + + // YES: Pass XFLM_US_LANG because this is what we want - + // Prevents double character sorting. + + ui16ColValue = flmWPGetCollation( ui16WpChar, XFLM_US_LANG); + + if (bUppercaseFlag || flmWPIsUpper( ui16WpChar)) + { + // Uppercase - set case bit + + ucCaseBits |= SET_CASE_BIT; + } + + // Character for which there is no collation value? + + if( ui16ColValue == COLS0) + { + ui16ReturnValue = 0; + if( !flmWPIsUpper( ui16WpChar)) + { + // Convert to uppercase + + ui16WpChar--; + } + ui16ColValue = 0xFFFF; + ui16SubColVal = ui16WpChar; + } + else if( ucCharSet) // Don't bother with ascii + { + if( !flmWPIsUpper( ui16WpChar)) + { + // Convert to uppercase + + ui16WpChar--; + } + + if( ucCharSet == CHSMUL1) + { + FLMUINT16 ui16Base; + FLMUINT16 ui16Diacritic; + + ui16SubColVal = !flmWPBrkcar( ui16WpChar, &ui16Base, + &ui16Diacritic) + ? fwp_dia60Tbl[ ui16Diacritic & 0xFF] + : ui16WpChar; + } + else if( ucCharSet == CHSGREK) // GREEK + { + if( ui16WpChar >= 0x834 || // [8,52] or above + ui16WpChar == 0x804 || // [8,4] BETA Medial | Terminal + ui16WpChar == 0x826) // [8,38] SIGMA terminal + { + ui16SubColVal = ui16WpChar; + } + } + else if( ucCharSet == CHSCYR) // CYRILLIC + { + if( ui16WpChar >= 0xA90) // [10, 144] or above + { + ui16SubColVal = ui16WpChar; // Dup collation values + } + } + // else don't need a sub collation value + } + goto Exit; + } + + // Single wide Japanese character? + + if( ucCharSet == 11) + { + FLMUINT16 ui16KanaChar; + + // Convert charset 11 to Zenkaku (double wide) CS24 or CS26 hex. + // All characters in charset 11 will convert to CS24 or CS26. + // when combining the collation and the sub-collation values. + + if( flmWPHanToZenkaku( ui16WpChar, + ui16NextWpChar, &ui16KanaChar ) == 2) + { + // Return 2 + + ui16ReturnValue++; + } + + ucCaseBits |= SET_WIDTH_BIT; // Set so will allow to go back + ui16WpChar = ui16KanaChar; // If in CS24 will fall through to ZenKaku + ucCharSet = ui16KanaChar >> 8; + ucCharVal = ui16KanaChar & 0xFF; + } + + if( ui16WpChar < 0x2400) + { + // In some other character set + + goto Latin_Greek_Cyrillic; + } + else if( ui16WpChar >= 0x255e && // Hiragana? + ui16WpChar <= 0x2655) // Katakana? + { + if( ui16WpChar >= 0x2600) + { + ucCaseBits |= SET_KATAKANA_BIT; + } + + // HIRAGANA & KATAKANA + // Kana contains both hiragana and katakana. + // The tables contain the same characters in same order + + if( ucCharSet == 0x25) + { + // Change value to be in character set 26 + + ucCharVal -= 0x5E; + } + + ui16ColValue = 0x0100 + KanaColTbl[ ucCharVal ]; + ui16SubColVal = KanaSubColTbl[ ucCharVal ]; + goto Exit; + } + + // ZenKaku - means any double wide character + // Hankaku - single wide character + + // Inputs: 0x2400..2559 symbols..latin - Zenkaku + // 0x265B..2750 greek..cyrillic - Zenkaku + + // SET_WIDTH_BIT may have been set if original char + // was in 11 and got converted to CS24. [1,2,5,27(extendedVowel),53,54] + // Original chars from CS11 will have some collation value that when + // combined with the sub-collation value will format a character in + // CS24. The width bit will then convert back to CS11. + + if( (ui16Hankaku = flmWPZenToHankaku( ui16WpChar, NULL)) != 0) + { + if( (ui16Hankaku >> 8) != 11) // if CharSet11 was a CS24 symbol + { + ui16WpChar = ui16Hankaku; // May be CS24 symbol/latin/gk/cy + ucCharSet = ui16WpChar >> 8; + ucCharVal = ui16WpChar & 0xFF; + ucCaseBits |= SET_WIDTH_BIT; // Latin symbols double wide + goto Latin_Greek_Cyrillic; + } + } + + // 0x2400..0x24bc Japanese symbols that cannot be converted to Hankaku. + // All 6 original symbol chars from 11 will also be here. + // First try to find a collation value of the symbol. + // The sub-collation value will be the position in the CS24 table + 1. + + for( uiLoop = 0; + uiLoop < (sizeof( fwp_Ch24ColTbl) / sizeof( BYTE_WORD_TBL)); + uiLoop++ ) + { + if( ucCharVal == fwp_Ch24ColTbl[ uiLoop].ByteValue) + { + if( (ui16ColValue = fwp_Ch24ColTbl[ uiLoop].WordValue) < 0x100) + { + // Don't save for chuuten, dakuten, handakuten + + ui16SubColVal = (FLMUINT16)(uiLoop + 1); + } + break; + } + } + + if( !ui16ColValue) + { + // Now see if it's a repeat or repeat-vowel character + + if( (((ucCharVal >= 0x12) && (ucCharVal <= 0x15)) || + (ucCharVal == 0x17) || + (ucCharVal == 0x18)) && + ((ui16PrevColValue >> 8) == 1)) + { + ui16ColValue = ui16PrevColValue; + + // Store original WP character + + ui16SubColVal = ui16WpChar; + } + else if( (ucCharVal == 0x1B) && // repeat vowel? + (ui16PrevColValue >= 0x100) && + (ui16PrevColValue < COLS_ASIAN_MARKS)) // Previous kana char? + { + ui16ColValue = 0x0100 + KanaColToVowel[ ui16PrevColValue & 0xFF ]; + + // Store original WP character + + ui16SubColVal = ui16WpChar; + } + else + { + ui16ReturnValue = 0; + ui16ColValue = 0xFFFF; // No collation value + ui16SubColVal = ui16WpChar; // Never have changed if gets here + } + } + +Exit: + + // Set return values + + *pui16ColValue = ui16ColValue; + *pui16SubColVal = ui16SubColVal; + *pucCaseBits = ucCaseBits; + + return( ui16ReturnValue); +} + +/**************************************************************************** +Desc: Convert a text string to a collated string. +****************************************************************************/ +RCODE flmAsiaUTF8ToColText( + IF_PosIStream * pIStream, + FLMBYTE * pucColStr, // Output collated string + FLMUINT * puiColStrLen, // Collated string length return value + // Input value is MAX num of bytes in buffer + FLMBOOL bCaseInsensitive, // Set if to convert to uppercase + FLMUINT * puiCollationLen, // Returns the collation bytes length + FLMUINT * puiCaseLen, // Returns length of case bytes + FLMUINT uiCharLimit, // Max number of characters in this key piece + FLMBOOL bFirstSubstring, // TRUE is this is the first substring key + FLMBOOL bDataTruncated, // Was input data already truncated. + FLMBOOL * pbDataTruncated) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bEndOfStr = FALSE; + FLMUINT uiLength; + FLMUINT uiTargetColLen = *puiColStrLen - 12; // 6=ovhd,6=worst char + FLMBYTE ucSubColBuf[ MAX_SUBCOL_BUF + 1]; // Holds Sub-col values (diac) + FLMBYTE ucLowUpBuf[ MAX_CASE_BYTES + MAX_CASE_BYTES + 2]; // 2 case bits/wpchar + FLMUINT uiColLen; + FLMUINT uiSubColBitPos; + FLMUINT uiLowUpBitPos; + FLMUINT uiFlags; + FLMUNICODE uChar; + FLMUINT16 ui16NextWpChar; + FLMUINT16 ui16ColValue; + + uiColLen = uiSubColBitPos = uiLowUpBitPos = uiFlags = 0; + uChar = ui16ColValue = 0; + + // We don't want any single key piece to "pig out" more + // than 256 bytes of the key + + if( uiTargetColLen > 256 - 12) + { + uiTargetColLen = 256 - 12; + } + + // Make sure ucSubColBuf and ucLowUpBuf are set to 0 + + f_memset( ucSubColBuf, 0, sizeof( ucSubColBuf)); + f_memset( ucLowUpBuf, 0, sizeof( ucLowUpBuf)); + + ui16NextWpChar = 0; + + while( !bEndOfStr || ui16NextWpChar || uChar) + { + FLMUINT16 ui16WpChar; // Current WP character + FLMUINT16 ui16SubColVal; // Sub-collated value (diacritic) + FLMBYTE ucCaseFlags; + FLMUINT16 ui16CurWpChar; + + // Get the next character from the string. + + ui16WpChar = ui16NextWpChar; + for( ui16NextWpChar = 0; + (!ui16WpChar || !ui16NextWpChar) && + !uChar && !bEndOfStr;) + { + if (!bEndOfStr) + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + bEndOfStr = TRUE; + } + else + { + goto Exit; + } + } + } + else + { + uChar = 0; + } + + if( flmUnicodeToWP( uChar, &ui16CurWpChar)) + { + uChar = 0; + } + + if( !ui16WpChar) + { + ui16WpChar = ui16CurWpChar; + } + else + { + ui16NextWpChar = ui16CurWpChar; + } + } + + // If we didn't get a character, break out of the outer + // processing loop. + + if( !ui16WpChar && !uChar) + { + break; + } + + if( ui16WpChar) + { + if( flmWPAsiaGetCollation( ui16WpChar, ui16NextWpChar, ui16ColValue, + &ui16ColValue, &ui16SubColVal, &ucCaseFlags, bCaseInsensitive) == 2) + { + // Took the ui16NextWpChar value + // Force to skip this value + + ui16NextWpChar = 0; + } + } + else // Use the uChar value for this pass + { + // This handles all of the UNICODE characters that could not + // be converted to WP characters - which will include most + // of the Asian characters. + + ucCaseFlags = 0; + if( uChar < 0x20) + { + ui16ColValue = 0xFFFF; + + // Setting ui16SubColVal to a high code will ensure + // that the code that the uChar value will be stored + // in in the sub-collation area. + + ui16SubColVal = 0xFFFF; + + // NOTE: uChar SHOULD NOT be set to zero here. + // It will be set to zero below. + } + else + { + ui16ColValue = uChar; + ui16SubColVal = 0; + uChar = 0; + } + } + + // Store the values in 2 bytes + + pucColStr[ uiColLen++] = (FLMBYTE)(ui16ColValue >> 8); + pucColStr[ uiColLen++] = (FLMBYTE)(ui16ColValue & 0xFF); + + if( ui16SubColVal) + { + uiFlags |= HAD_SUB_COLLATION; + if( ui16SubColVal <= 31) // 5 bit - store bits 10 + { + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos += 1 + 1; // Stores a zero + setBits( 5, ucSubColBuf, uiSubColBitPos, ui16SubColVal); + uiSubColBitPos += 5; + } + else // 2 bytes - store bits 110 or 11110 + { + FLMUINT uiTemp; + + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + + if( !ui16WpChar && uChar) // Store as "11110" + { + ui16SubColVal = uChar; + uChar = 0; + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + setBit( ucSubColBuf, uiSubColBitPos); + uiSubColBitPos++; + } + uiSubColBitPos++; // Skip past the zero + + // Go to the next byte boundary to write the WP char + uiSubColBitPos = (uiSubColBitPos + 7) & (~7); + uiTemp = bytesInBits( uiSubColBitPos); + + // Need to store HIGH-Low - PC format is Low-high! + ucSubColBuf[ uiTemp ] = (FLMBYTE)(ui16SubColVal >> 8); + ucSubColBuf[ uiTemp + 1] = (FLMBYTE)(ui16SubColVal); + + uiSubColBitPos += 16; + } + } + else + { + uiSubColBitPos++; + } + + // Save case information - always 2 bits worth for Asian + + if( ucCaseFlags & 0x02) + { + setBit( ucLowUpBuf, uiLowUpBitPos); + } + + uiLowUpBitPos++; + + if( ucCaseFlags & 0x01) + { + setBit( ucLowUpBuf, uiLowUpBitPos); + } + uiLowUpBitPos++; + + // Check to see if uiColLen is within 1 byte of max + + if( (uiColLen >= uiCharLimit) || + (uiColLen + bytesInBits( uiSubColBitPos) + + bytesInBits( uiLowUpBitPos) >= uiTargetColLen)) + { + // Still something left? + + if (ui16NextWpChar || uChar) + { + bDataTruncated = TRUE; + } + else if (!bEndOfStr) + { + if (RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + bEndOfStr = TRUE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + bDataTruncated = TRUE; + } + } + break; // Hit the max. number of characters + } + } + + if( puiCollationLen) + { + *puiCollationLen = uiColLen; + } + + // Add the first substring marker - also serves + // as making the string non-null. + + if( bFirstSubstring) + { + pucColStr[ uiColLen++] = 0; + pucColStr[ uiColLen++] = COLL_FIRST_SUBSTRING; + } + + if( bDataTruncated) + { + pucColStr[ uiColLen++] = 0; + pucColStr[ uiColLen++] = COLL_TRUNCATED; + } + + // Return NOTHING if no values found + + if( !uiColLen && !uiSubColBitPos) + { + if( puiCaseLen) + { + *puiCaseLen = 0; + } + goto Exit; + } + + // Done putting the String into 3 sections - build the COLLATED KEY + + if( uiFlags & HAD_SUB_COLLATION) + { + pucColStr[ uiColLen++] = 0; + pucColStr[ uiColLen++] = COLL_MARKER | SC_SUB_COL; + + // Move the Sub-collation (diacritics) into the collating string + + uiLength = (FLMUINT)(bytesInBits( uiSubColBitPos)); + f_memcpy( &pucColStr[ uiColLen], ucSubColBuf, uiLength); + uiColLen += uiLength; + } + + // Always represent the marker as 2 bytes and case bits in Asia + + pucColStr[ uiColLen++] = 0; + pucColStr[ uiColLen++] = COLL_MARKER | SC_MIXED; + + uiLength = (FLMUINT)(bytesInBits( uiLowUpBitPos)); + f_memcpy( &pucColStr[ uiColLen ], ucLowUpBuf, uiLength); + + if( puiCaseLen) + { + *puiCaseLen = (FLMUINT)(uiLength + 2); + } + uiColLen += uiLength; + +Exit: + + if( pbDataTruncated) + { + *pbDataTruncated = bDataTruncated; + } + + *puiColStrLen = uiColLen; + return( rc); +} + +/*************************************************************************** +Desc: Get the original string from an asian collation string +Ret: Length of the word string in bytes +****************************************************************************/ +FSTATIC RCODE flmAsiaColStr2WPStr( + const FLMBYTE * pucColStr, // Points to the collated string + FLMUINT uiColStrLen, // Length of the collated string + FLMBYTE * pucWPStr, // Output string to build - WP word string + FLMUINT * puiWPStrLen, + FLMUINT * puiUnconvChars, + FLMBOOL * pbDataTruncated, // Set to TRUE if truncated + FLMBOOL * pbFirstSubstring) // Sets to TRUE if first substring +{ + FLMBYTE * pucWPStrPtr = pucWPStr; + FLMBYTE * pucWPEnd = &pucWPStr[ *puiWPStrLen]; + FLMUINT uiLength = uiColStrLen; + FLMUINT uiMaxWPBytes = *puiWPStrLen; + FLMUINT uiColStrPos = 0; + FLMBOOL bHadExtended = FALSE; + FLMUINT uiWPStrLen; + FLMUINT16 ui16ColChar; + FLMUINT uiUnconvChars = 0; + FLMUINT uiColBytesProcessed; + RCODE rc = NE_XFLM_OK; + + while( uiLength) + { + FLMBYTE ucChar = pucColStr[ uiColStrPos + 1]; + FLMBYTE ucCharSet = pucColStr[ uiColStrPos]; + + ui16ColChar = (FLMUINT16)((ucCharSet << 8) + ucChar); + if( ui16ColChar <= MAX_COL_OPCODE) + { + break; + } + + uiColStrPos += 2; + uiLength -= 2; + if( ucCharSet == 0) // Normal Latin/Greek/Cyrillic value + { + ui16ColChar = colToWPChr[ ucChar - COLLS]; + } + else if( ucCharSet == 1) // Katakana or Hiragana character + { + if( ucChar > sizeof( ColToKanaTbl)) // Special cases below + { + if( ucChar == COLS_ASIAN_MARK_VAL) // Dakuten + { + ui16ColChar = 0x240a; + } + else if( ucChar == COLS_ASIAN_MARK_VAL + 1) // Handakuten + { + ui16ColChar = 0x240b; + } + else if( ucChar == COLS_ASIAN_MARK_VAL + 2) // Chuuten + { + ui16ColChar = 0x2405; + } + else + { + ui16ColChar = 0xFFFF; // Error + } + } + else + { + ui16ColChar = (FLMUINT16)(0x2600 + ColToKanaTbl[ ucChar]); + } + } + else if( ucCharSet != 0xFF || ucChar != 0xFF) // Asian characters + { + // Insert zeroes that will be treated as a signal for + // uncoverted unicode characters later on. NOTE: Cannot + // use 0xFFFF, because we need to be able to detect this + // case in the sub-collation stuff, and we don't want + // to confuse it with the 0xFFFF that may have been inserted + // in another case. + // THIS IS A REALLY BAD HACK, BUT IT IS THE BEST WE CAN DO + // FOR NOW! + + if( pucWPStrPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + *pucWPStrPtr++ = 0; + *pucWPStrPtr++ = 0; + uiUnconvChars++; + bHadExtended = TRUE; + } + // else, there is no collation value - found in sub-collation part + + if( pucWPStrPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( ui16ColChar, pucWPStrPtr); // Put the uncollation value back + pucWPStrPtr += 2; + } + + if( pucWPStrPtr + 2 >= pucWPEnd) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + UW2FBA( 0, pucWPStrPtr); // Terminate the string + uiWPStrLen = (FLMUINT)(pucWPStrPtr - pucWPStr); + + // Parse through the sub-collation and case information. + // Here are values for some of the codes: + // [ 0x05] - case bits follow + // [ 0x06] - case information is all uppercase + // [ 0x07] - beginning of sub-collation information + // [ 0x08] - first substring field that is made + // [ 0x09] - truncation marker for text and binary + // + // Asian chars the case information should always be there and not + // compressed out. This is because the case information could change + // the actual width of the character from 0x26xx to charset 11. + + // Does truncation marker or sub-collation follow? + + if( uiLength) + { + ui16ColChar = (FLMUINT16)((pucColStr[ uiColStrPos] << 8) + + pucColStr[ uiColStrPos + 1]); + + // First substring is before truncated. + if( ui16ColChar == COLL_FIRST_SUBSTRING) + { + if( pbFirstSubstring) + { + *pbFirstSubstring = TRUE; // Don't need to initialize to FALSE. + } + + uiLength -= 2; + uiColStrPos += 2; + ui16ColChar = (FLMUINT16)((pucColStr[ uiColStrPos] << 8) + + pucColStr[ uiColStrPos + 1]); + } + + if( ui16ColChar == COLL_TRUNCATED) + { + if( pbDataTruncated) + { + *pbDataTruncated = TRUE; // Don't need to initialize to FALSE. + } + uiLength -= 2; + uiColStrPos += 2; + ui16ColChar = (FLMUINT16)((pucColStr[ uiColStrPos] << 8) + + pucColStr[ uiColStrPos+1]); + } + + if( ui16ColChar == (COLL_MARKER | SC_SUB_COL)) + { + FLMUINT uiTempLen; + + // Do another pass on the word string adding diacritics/voicings + + uiColStrPos += 2; + uiLength -= 2; + if( RC_BAD( rc = flmAsiaParseSubCol( pucWPStr, &uiWPStrLen, + uiMaxWPBytes, &pucColStr[ uiColStrPos], &uiTempLen))) + { + goto Exit; + } + + uiColStrPos += uiTempLen; + uiLength -= uiTempLen; + } + else + { + goto check_case; + } + } + + // Does the case info follow? + + if( uiLength) + { + ui16ColChar = (FLMUINT16)((pucColStr[ uiColStrPos] << 8) + + pucColStr[ uiColStrPos + 1]); +check_case: + + if( ui16ColChar == (COLL_MARKER | SC_MIXED)) + { + uiColStrPos += 2; + + if( RC_BAD( rc = flmAsiaParseCase( pucWPStr, &uiWPStrLen, + uiMaxWPBytes, &pucColStr[ uiColStrPos], &uiColBytesProcessed))) + { + goto Exit; + } + + uiColStrPos += uiColBytesProcessed; + + // Set bHadExtended to FALSE, because they will have + // been taken care of in this pass. + + bHadExtended = FALSE; + } + } + + // Change embedded zeroes to 0xFFFFs + + if (bHadExtended) + { + FLMUINT uiCnt; + FLMBYTE * pucTmp; + + for( uiCnt = 0, pucTmp = pucWPStr; + uiCnt < uiWPStrLen; + uiCnt += 2, pucTmp += 2) + { + if( FB2UW( pucTmp) == 0) + { + UW2FBA( 0xFFFF, pucTmp); + } + } + } + + if (uiColStrLen != uiColStrPos) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + *puiUnconvChars = uiUnconvChars; + *puiWPStrLen = uiWPStrLen; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Combine the diacritic 5 and 16 bit values to an existing word string. +Ret: FLMUINT - Number of bytes parsed +Notes: For each bit in the sub-collation section: + 0 - no subcollation information + 10 - take next 5 bits - will tell about diacritics or japanese vowel + 110 - align to next byte & take word value as extended character + +****************************************************************************/ +FSTATIC RCODE flmAsiaParseSubCol( + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucSubColBuf, + FLMUINT * puiSubColBitPos) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSubColBitPos = 0; + FLMUINT uiNumChars = *puiWPStrLen >> 1; + FLMUINT16 ui16Diac; + FLMUINT16 ui16WpChar; + + // For each character (16 bits) in the WP string ... + + while( uiNumChars--) + { + // Have to skip 0, because it is not accounted for + // in the sub-collation bits. It was inserted when we + // encountered unconverted unicode characters (Asian). + // Will be converted to something else later on. + // SEE NOTE ABOVE. + + if( FB2UW( pucWPStr) == 0) + { + pucWPStr += 2; + continue; + } + + // This macro DOESN'T increment uiBitPos + + if( testOneBit( pucSubColBuf, uiSubColBitPos)) + { + // Bits 10 - take next 5 bits + // Bits 110 align and take next word + // Bits 11110 align and take unicode value + + uiSubColBitPos++; + if( !testOneBit( pucSubColBuf, uiSubColBitPos)) + { + uiSubColBitPos++; + ui16Diac = (FLMUINT16)(getNBits( 5, pucSubColBuf, uiSubColBitPos)); + uiSubColBitPos += 5; + + if( (ui16WpChar = FB2UW( pucWPStr)) < 0x100) + { + if( (ui16WpChar >= 'A') && (ui16WpChar <= 'Z')) + { + // Convert to WP diacritic and combine characters + + flmWPCmbcar( &ui16WpChar, ui16WpChar, + (FLMUINT16)ml1_COLtoD[ ui16Diac]); + + // Even if cmbcar fails, WpChar is still set to a valid value + } + else + { + // Symbols from charset 0x24 + + ui16WpChar = (FLMUINT16)(0x2400 + + fwp_Ch24ColTbl[ ui16Diac - 1 ].ByteValue); + } + } + else if( ui16WpChar >= 0x2600) // Katakana + { + // Voicings - will allow to select original char + // 000 - some 001 are changed to 000 to save space + // 001 - set if large char (uppercase) + // 010 - set if voiced + // 100 - set if half voiced + // + // Should NOT match voicing or wouldn't be here! + + FLMBYTE ucChar = (FLMBYTE)(ui16WpChar & 0xFF); + + // Try exceptions first so don't access out of bounds + + if( ucChar == 84) + { + ui16WpChar = (FLMUINT16)(0x2600 + + ((ui16Diac == 1) + ? (FLMUINT16)10 + : (FLMUINT16)11)); + } + else if( ucChar == 85) + { + ui16WpChar = (FLMUINT16)(0x2600 + + ((ui16Diac == 1) + ? (FLMUINT16)16 + : (FLMUINT16)17)); + } + + // Try the next 2 slots, if not then + // value is 83, 84 or 85 + + else if( KanaSubColTbl[ ucChar + 1 ] == ui16Diac) + { + ui16WpChar++; + } + else if( KanaSubColTbl[ ucChar + 2 ] == ui16Diac) + { + ui16WpChar += 2; + } + else if( ucChar == 4) // Last exception + { + ui16WpChar = 0x2600 + 83; + } + + // else, leave alone! - invalid storage + } + + UW2FBA( ui16WpChar, pucWPStr); // Set if changed or not + } + else // "110" + { + FLMUINT uiTemp; + + uiSubColBitPos++; // Skip second '1' + if( testOneBit( pucSubColBuf, uiSubColBitPos)) // 11?10 ? + { + if( (*puiWPStrLen) + 2 > uiMaxWPBytes) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Unconvertable UNICODE character + // The format will be 4 bytes, 0xFF, 0xFF, 2 byte Unicode + + shiftN( pucWPStr, + (FLMUINT16)(uiNumChars + uiNumChars + 4), 2); + + pucWPStr += 2; // Skip the 0xFFFF for now + uiSubColBitPos += 2; // Skip next "11" + (*puiWPStrLen) += 2; + } + uiSubColBitPos++; // Skip the zero + + // Round up to next byte + uiSubColBitPos = (uiSubColBitPos + 7) & (~7); + uiTemp = bytesInBits( uiSubColBitPos); + pucWPStr[ 1] = pucSubColBuf[ uiTemp]; // Character set + pucWPStr[ 0] = pucSubColBuf[ uiTemp + 1]; // Character + uiSubColBitPos += 16; + } + } + else + { + uiSubColBitPos++; // Be sure to increment this! + } + + pucWPStr += 2; // Next WP character + } + + *puiSubColBitPos = bytesInBits( uiSubColBitPos); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: The case bits for asia are: + Latin/Greek/Cyrillic + 01 - case bit set if character is uppercase + 10 - double wide character in CS 0x25xx, 0x26xx and 0x27xx + Japanese + 00 - double wide hiragana 0x255e..25b0 + 01 - double wide katakana 0x2600..2655 + 10 - single wide symbols from charset 11 that map to CS24?? + 11 - single wide katakana from charset 11 +Ret: +Notes: This is tricky to really understand the inputs. + This looks at the bits according to the current character value. +****************************************************************************/ +FSTATIC RCODE flmAsiaParseCase( + FLMBYTE * pucWPStr, + FLMUINT * puiWPStrLen, + FLMUINT uiMaxWPBytes, + const FLMBYTE * pucCaseBits, + FLMUINT * puiColBytesProcessed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiWPStrLen = *puiWPStrLen; + FLMUINT uiCharCnt; + FLMUINT uiExtraBytes = 0; + FLMUINT16 ui16WpChar; + FLMBYTE ucTempByte = 0; + FLMBYTE ucMaskByte; + + // For each character (two bytes) in the string ... + + for( uiCharCnt = uiWPStrLen >> 1, // Total number of words in word string + ucMaskByte = 0; // Force first time to get a byte + uiCharCnt--;) + { + FLMBYTE ucChar; + FLMBYTE ucCharSet; + + ui16WpChar = FB2UW( pucWPStr); // Get the next character + + // Must skip any 0xFFFFs or zeroes that were inserted. + + if( ui16WpChar == 0xFFFF || ui16WpChar == 0) + { + // Put back 0xFFFF in case it was a zero. + + UW2FBA( 0xFFFF, pucWPStr); + pucWPStr += 2; + uiExtraBytes += 2; + continue; + } + + // Time to get another byte? + + if( ucMaskByte == 0) + { + ucTempByte = *pucCaseBits++; + ucMaskByte = 0x80; + } + + ucCharSet = (FLMBYTE)(ui16WpChar >> 8); + ucChar = (FLMBYTE)(ui16WpChar & 0xFF); + + // SINGLE WIDE - NORMAL CHARACTERS + + if( ui16WpChar < 0x2400) + { + // Convert to double wide? + + if( ucTempByte & ucMaskByte) + { + // Latin/greek/cyrillic + // Convert to uppercase double wide char + + if( ucCharSet == 0) // Latin - uppercase + { + // May convert to 0x250F (Latin) or CS24 + + if( ui16WpChar >= ASCII_UPPER_A && ui16WpChar <= ASCII_UPPER_Z) + { + // Convert to double wide + + ui16WpChar = (FLMUINT16)(ui16WpChar - 0x30 + 0x250F); + } + else + { + flmWPHanToZenkaku( ui16WpChar, 0, &ui16WpChar); + } + } + else if( ucCharSet == 8) // Greek + { + if( ucChar > 38) // Adjust for spaces in Greek + { + ucChar -= 2; + } + + if( ucChar > 4) + { + ucChar -= 2; + } + + ui16WpChar = (FLMUINT16)((ucChar >> 1) + 0x265E); + } + else if( ucCharSet == 10) // Cyrillic + { + ui16WpChar = (FLMUINT16)((ucChar >> 1) + 0x2700); + } + else + { + flmWPHanToZenkaku( ui16WpChar, 0, &ui16WpChar); + } + + ucCharSet = (FLMBYTE)(ui16WpChar >> 8); + ucChar = (FLMBYTE)(ui16WpChar & 0xFF); + } + + ucMaskByte >>= 1; // Next bit + + // Change to lower case? + + if( (ucTempByte & ucMaskByte) == 0) + { + // Convert ui16WpChar to lower case + + switch( ucCharSet) + { + case 0: + // Bit zero only if lower case + + ui16WpChar |= 0x20; + break; + + case 1: + // In upper/lower case region? + + if( ucChar >= 26) + { + ui16WpChar++; + } + break; + + case 8: + // All lowercase after 69 + + if( ucChar <= 69) + { + ui16WpChar++; + } + break; + + case 10: + // No cases after 199 + + if( ucChar <= 199) + { + ui16WpChar++; + } + break; + + case 0x25: + case 0x26: + // Should be double wide latin or Greek + // Add offset to convert to lowercase + + ui16WpChar += 0x20; + break; + + case 0x27: + // Double wide cyrillic only + // Add offset to convert to lowercase + + ui16WpChar += 0x30; + break; + } + } + } + else // JAPANESE CHARACTERS + { + if( ucTempByte & ucMaskByte) // Original chars from CharSet 11 + { + if( ucCharSet == 0x26) // Convert to Zen to Hankaku + { + FLMUINT16 ui16NextChar = 0; + + ui16WpChar = flmWPZenToHankaku( ui16WpChar, &ui16NextChar); + if( ui16NextChar) // Move everyone down + { + if( (*puiWPStrLen) + 2 > uiMaxWPBytes) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + uiCharCnt++; + shiftN( pucWPStr, uiCharCnt + uiCharCnt + 2, 2); + UW2FBA( ui16WpChar, pucWPStr); + pucWPStr += 2; + ui16WpChar = ui16NextChar; // This will be stored below + + // Adjust the length + *puiWPStrLen = *puiWPStrLen + 2; + } + } + else if( ucCharSet == 0x24) + { + ui16WpChar = flmWPZenToHankaku( ui16WpChar, NULL); + } + ucMaskByte >>= 1; // Eat the next bit + } + else + { + ucMaskByte >>= 1; // Next bit + if( (ucTempByte & ucMaskByte) == 0) // Convert to Hiragana? + { + // Kanji will also fall through here + + if( ucCharSet == 0x26) + { + // Convert to Hiragana + ui16WpChar = (FLMUINT16)(0x255E + ucChar); + } + } + } + } + UW2FBA( ui16WpChar, pucWPStr); + pucWPStr += 2; + ucMaskByte >>= 1; + } + + uiCharCnt = uiWPStrLen - uiExtraBytes; // Should be 2 bits for each character. + *puiColBytesProcessed = bytesInBits( uiCharCnt); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Convert a zenkaku (double wide) char to a hankaku (single wide) char +Ret: Hankaku char or 0 if a conversion doesn't exist +Notes: Taken from CHAR.ASM - zen2han_f routine +****************************************************************************/ +FSTATIC FLMUINT16 flmWPZenToHankaku( + FLMUINT16 ui16WpChar, + FLMUINT16 * pui16DakutenOrHandakuten) +{ + FLMUINT16 ui16Hankaku = 0; + FLMBYTE ucCharSet = ui16WpChar >> 8; + FLMBYTE ucCharVal = ui16WpChar & 0xFF; + FLMUINT uiLoop; + + switch( ucCharSet) + { + // SYMBOLS + + case 0x24: + { + for( uiLoop = 0; + uiLoop < (sizeof( Zen24ToHankaku) / sizeof( BYTE_WORD_TBL)); + uiLoop++) + { + // List is sorted so table entry is more you are done + + if( Zen24ToHankaku [uiLoop].ByteValue >= ucCharVal) + { + if( Zen24ToHankaku [uiLoop].ByteValue == ucCharVal) + { + ui16Hankaku = Zen24ToHankaku [uiLoop].WordValue; + } + break; + } + } + break; + } + + // ROMAN - 0x250F..2559 + // Hiragana - 0x255E..2580 + + case 0x25: + { + if( ucCharVal >= 0x0F && ucCharVal < 0x5E) + { + ui16Hankaku = ucCharVal + 0x21; + } + break; + } + + // Katakana - 0x2600..2655 + // Greek - 0x265B..2695 + + case 0x26: + { + if( ucCharVal <= 0x55) // Katakana range + { + FLMBYTE ucCS11CharVal; + FLMUINT16 ui16NextWpChar = 0; + + if( (ucCS11CharVal = MapCS26ToCharSet11[ ucCharVal ]) != 0xFF) + { + if( ucCS11CharVal & 0x80) + { + if( ucCS11CharVal & 0x40) + { + // Handakuten voicing + + ui16NextWpChar = 0xB3E; + } + else + { + // Dakuten voicing + + ui16NextWpChar = 0xB3D; + } + ucCS11CharVal &= 0x3F; + } + ui16Hankaku = 0x0b00 + ucCS11CharVal; + if( ui16NextWpChar && pui16DakutenOrHandakuten) + { + *pui16DakutenOrHandakuten = ui16NextWpChar; + } + } + } + else if( ucCharVal <= 0x95) // Greek + { + FLMBYTE ucGreekChar = ucCharVal; + + // Make a zero based number. + + ucGreekChar -= 0x5E; + + // Check for lowercase + if( ucGreekChar >= 0x20) + { + // Convert to upper case for now + + ucGreekChar -= 0x20; + } + + if( ucGreekChar >= 2) + { + ucGreekChar++; + } + + if (ucGreekChar >= 19) + { + ucGreekChar++; + } + + // Convert to character set 8 + + ui16Hankaku = (ucGreekChar << 1) + 0x800; + if( ucCharVal >= (0x5E + 0x20)) + { + // Adjust to lower case character + + ui16Hankaku++; + } + } + break; + } + + // Cyrillic + + case 0x27: + { + // Uppercase? + + if( ucCharVal <= 0x20) + { + ui16Hankaku = (ucCharVal << 1) + 0xa00; + } + else if( ucCharVal >= 0x30 && ucCharVal <= 0x50) + { + // Lower case + + ui16Hankaku = ((ucCharVal - 0x30) << 1) + 0xa01; + } + break; + } + } + + return( ui16Hankaku); +} + +/**************************************************************************** +Desc: Convert a WPChar from hankaku (single wide) to zenkaku (double wide). + 1) Used to see if a char in CS11 can map to a double wide character + 2) Used to convert keys into original data. +Ret: 0 = no conversion + 1 = converted character to zenkaku + 2 = ui16NextWpChar dakuten or handakuten voicing got combined +Notes: Taken from char.asm - han2zen() + From8ToZen could be taken out and placed in code. +****************************************************************************/ +FSTATIC FLMUINT16 flmWPHanToZenkaku( + FLMUINT16 ui16WpChar, + FLMUINT16 ui16NextWpChar, + FLMUINT16 * pui16Zenkaku) +{ + FLMUINT16 ui16Zenkaku = 0; + FLMBYTE ucCharSet = ui16WpChar >> 8; + FLMBYTE ucCharVal = ui16WpChar & 0xFF; + FLMUINT uiLoop; + FLMUINT16 ui16CharsUsed = 1; + + switch( ucCharSet) + { + // Character set 0 - symbols + + case 0: + { + // Invalid? - all others are used. + + if( ucCharVal < 0x20) + { + ; + } + else if( ucCharVal <= 0x2F) + { + // Symbols A + ui16Zenkaku = 0x2400 + From0AToZen[ ucCharVal - 0x20 ]; + } + else if( ucCharVal <= 0x39) + { + // 0..9 + ui16Zenkaku = 0x2500 + (ucCharVal - 0x21); + } + else if( ucCharVal <= 0x40) + { + // Symbols B + ui16Zenkaku = 0x2400 + From0BToZen[ ucCharVal - 0x3A ]; + } + else if( ucCharVal <= 0x5A) + { + // A..Z + ui16Zenkaku = 0x2500 + (ucCharVal - 0x21); + } + else if( ucCharVal <= 0x60) + { + // Symbols C + ui16Zenkaku = 0x2400 + From0CToZen[ ucCharVal - 0x5B ]; + } + else if( ucCharVal <= 0x7A) + { + // a..z + ui16Zenkaku = 0x2500 + (ucCharVal - 0x21); + } + else if( ucCharVal <= 0x7E) + { + // Symbols D + ui16Zenkaku = 0x2400 + From0DToZen[ ucCharVal - 0x7B ]; + } + break; + } + + // GREEK + + case 8: + { + if( (ucCharVal >= sizeof( From8ToZen)) || + ((ui16Zenkaku = 0x2600 + From8ToZen[ ucCharVal ]) == 0x26FF)) + { + ui16Zenkaku = 0; + } + break; + } + + // CYRILLIC + + case 10: + { + // Check range + + ui16Zenkaku = 0x2700 + (ucCharVal >> 1); // Uppercase value + + // Convert to lower case? + + if( ucCharVal & 0x01) + { + ui16Zenkaku += 0x30; + } + break; + } + + // JAPANESE + + case 11: + { + if( ucCharVal < 5) + { + ui16Zenkaku = 0x2400 + From11AToZen[ ucCharVal ]; + } + else if( ucCharVal < 0x3D) // katakana? + { + if( (ui16Zenkaku = 0x2600 + + From11BToZen[ ucCharVal - 5 ]) == 0x26FF) + { + // Dash - convert to this + ui16Zenkaku = 0x241b; + } + else + { + if( ui16NextWpChar == 0xB3D) // dakuten? - voicing + { + // First check exception(s) then + // check if voicing exists! - will NOT access out of table + + if( (ui16Zenkaku != 0x2652) && // is not 'N'? + (KanaSubColTbl[ ui16Zenkaku - 0x2600 + 1 ] == 3)) + { + ui16Zenkaku++; + + // Return 2 + + ui16CharsUsed++; + } + } + else if( ui16NextWpChar == 0xB3E) // handakuten? - voicing + { + // Check if voicing exists! - will NOT access out of table + + if( KanaSubColTbl [ui16Zenkaku - 0x2600 + 2 ] == 5) + { + ui16Zenkaku += 2; + + // Return 2 + + ui16CharsUsed++; + } + } + } + } + else if( ucCharVal == 0x3D) // dakuten? + { + // Convert to voicing symbol + + ui16Zenkaku = 0x240A; + } + else if( ucCharVal == 0x3E) // handakuten? + { + // Convert to voicing symbol + + ui16Zenkaku = 0x240B; + } + // else cannot convert + + break; + } + + // Other character sets + // CS 1,4,5,6 - symbols + + default: + { + // Instead of includes more tables from char.asm - look down the + // Zen24Tohankaku[] table for a matching value - not much slower. + + for( uiLoop = 0; + uiLoop < (sizeof(Zen24ToHankaku) / sizeof(BYTE_WORD_TBL)); + uiLoop++) + { + if( Zen24ToHankaku[ uiLoop].WordValue == ui16WpChar) + { + ui16Zenkaku = 0x2400 + Zen24ToHankaku[ uiLoop].ByteValue; + break; + } + } + break; + } + } + + if( !ui16Zenkaku) + { + // Change return value + + ui16CharsUsed = 0; + } + + *pui16Zenkaku = ui16Zenkaku; + return( ui16CharsUsed); +} + +/**************************************************************************** +Desc: Converts a 2-byte language code into its corresponding language ID +****************************************************************************/ +FLMUINT F_DbSystem::languageToNum( + const char * pszLanguage) +{ + FLMBYTE ucFirstChar = (FLMBYTE)(*pszLanguage); + FLMBYTE ucSecondChar = (FLMBYTE)(*(pszLanguage + 1)); + FLMUINT uiTablePos; + + for( uiTablePos = 0; + uiTablePos < (LAST_LANG + LAST_LANG); uiTablePos += 2) + { + if( fwp_langtbl [uiTablePos] == ucFirstChar && + fwp_langtbl [uiTablePos+1] == ucSecondChar) + { + + // Return uiTablePos div 2 + + return( uiTablePos >> 1); + } + } + + // Language not found, return default US language + + return( XFLM_US_LANG); +} + +/**************************************************************************** +Desc: Converts a language ID to its corresponding 2-byte language code +****************************************************************************/ +void F_DbSystem::languageToStr( + FLMINT iLangNum, + char * pszLanguage) +{ + // iLangNum could be negative + + if( iLangNum < 0 || iLangNum >= LAST_LANG) + { + iLangNum = XFLM_US_LANG; + } + + iLangNum += iLangNum; + *pszLanguage++ = (char)fwp_langtbl [iLangNum ]; + *pszLanguage++ = (char)fwp_langtbl [iLangNum+1]; + *pszLanguage = 0; +} + +/*************************************************************************** +Desc: Return the sub-collation value of a WP character. Unconverted + unicode values always have a sub-collation value of + 11110+UnicodeChar +***************************************************************************/ +FLMUINT16 flmWPGetSubCol( + FLMUINT16 ui16WPValue, // [in] WP Character value. + FLMUINT16 ui16ColValue, // [in] Collation Value (for arabic) + FLMUINT uiLanguage) // [in] WP Language ID. +{ + FLMUINT16 ui16SubColVal; + FLMBYTE ucCharVal; + FLMBYTE ucCharSet; + FLMUINT16 ui16Base; + + // Easy case first - ascii characters. + + ui16SubColVal = 0; + if (ui16WPValue <= 127) + { + goto Exit; + } + + // From here down default ui16SubColVal is WP value. + + ui16SubColVal = ui16WPValue; + ucCharVal = (FLMBYTE) ui16WPValue; + ucCharSet = (FLMBYTE) (ui16WPValue >> 8); + + // Convert char to uppercase because case information + // is stored above. This will help + // ensure that the "ETA" doesn't sort before "eta" + // could use is lower code here for added performance. + + // This just happens to work with all WP character values. + + if (!flmWPIsUpper( ui16WPValue)) + { + ui16WPValue &= ~1; + } + + switch (ucCharSet) + { + case CHSMUL1: + + // If you cannot break down a char into base and + // diacritic then you cannot combine the charaacter + // later when converting back the key. So, write + // the entire WP char in the sub-collation area. + // We can ONLY SUPPORT MULTINATIONAL 1 for brkcar() + + if (flmWPBrkcar( ui16WPValue, &ui16Base, &ui16SubColVal)) + { + + // WordPerfect character cannot be broken down. + // If we had a collation value other than 0xFF (COLS0), don't + // return a sub-collation value. This will allow things like + // upper and lower AE digraphs to compare properly. + + if (ui16ColValue != COLS0) + { + ui16SubColVal = 0; + } + goto Exit; + } + + // Write the FLAIM diacritic sub-collation value. + // Prefix is 2 bits "10". Remember to leave + // "111" alone for the future. + // Bug 11/16/92 = was only writing a "1" and not "10" + + ui16SubColVal = ( + (ui16SubColVal & 0xFF) == umlaut + && ( (uiLanguage == XFLM_SU_LANG) || + (uiLanguage == XFLM_SV_LANG) || + (uiLanguage == XFLM_CZ_LANG) || + (uiLanguage == XFLM_SL_LANG) + ) + ) + ? (FLMUINT16)(fwp_dia60Tbl[ ring] + 1) // umlaut must be after ring above + : (FLMUINT16)(fwp_dia60Tbl[ ui16SubColVal & 0xFF]); + + break; + + case CHSGREK: + + // Greek + + if( (ucCharVal >= 52) || // Keep case bit for 52-69 else ignore + (ui16WPValue == 0x804) || // [ 8,4] BETA Medial | Terminal + (ui16WPValue == 0x826)) // [ 8,38] SIGMA termainal + { + ui16SubColVal = ui16WPValue; + } + // else no subcollation to worry about + break; + + case CHSCYR: + if (ucCharVal >= 144) + { + ui16SubColVal = ui16WPValue; + } + // else no subcollation to worry about + + // VISIT: Georgian covers 208-249 - no collation defined yet + break; + + case CHSHEB: // Hebrew + + // Three sections in Hebrew: + // 0..26 - main characters + // 27..83 - accents that apear over previous character + // 84..118- dagesh (ancient) hebrew with accents + + // Because the ancient is only used for sayings & scriptures + // we will support a collation value and in the sub-collation + // store the actual character because sub-collation is in + // character order. + + if (ucCharVal >= 84) // Save ancient - value 84 and above + { + ui16SubColVal = ui16WPValue; + } + break; + + case CHSARB1: // Arabic 1 + + // Three sections in Arabic: + // 00..37 - accents that display OVER a previous character + // 38..46 - symbols + // 47..57 - numbers + // 58..163 - characters + // 164 - hamzah accent + // 165..180- common characters with accents + // 181..193- ligatures - common character combinations + // 194..195- extensions - throw away when sorting + + if (ucCharVal <= 46) + { + ui16SubColVal = ui16WPValue; + } + else + { + if (ui16ColValue == COLS10a+1) // Alef? + { + ui16SubColVal = (ucCharVal >= 165) + ? (FLMUINT16)(fwp_alefSubColTbl[ ucCharVal - 165 ]) + : (FLMUINT16)7; // Alef subcol value + } + else + { + if (ucCharVal >= 181) // Ligatures - char combination + { + ui16SubColVal = ui16WPValue; + } + else if (ucCharVal == 64) // taa exception + { + ui16SubColVal = 8; + } + } + } + break; + + case CHSARB2: // Arabic 2 + + // There are some characters that share the same slot + // Check the bit table if above character 64 + + if ((ucCharVal >= 64) && + (fwp_ar2BitTbl[(ucCharVal-64)>> 3] & (0x80 >> (ucCharVal&0x07)))) + { + ui16SubColVal = ui16WPValue; + } + break; + + } + +Exit: + + return( ui16SubColVal); +} diff --git a/version5/src/fcollate.h b/version5/src/fcollate.h new file mode 100644 index 0000000..de316b1 --- /dev/null +++ b/version5/src/fcollate.h @@ -0,0 +1,633 @@ +//------------------------------------------------------------------------------ +// Desc: Header for collation routines +// +// Tabs: 3 +// +// Copyright (c) 1991-1992, 1994-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: fcollate.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FCOLLATE_H +#define FCOLLATE_H + +// Character set #'s are same as high byte values +// except for algorithmic set. + +#define CHSASCI 0 // ASCII +#define CHSMUL1 1 // Multinational 1 +#define CHSMUL2 2 // Multinational 2 +#define CHSBOXD 3 // Box drawing +#define CHSSYM1 4 // Typographic Symbols +#define CHSSYM2 5 // Iconic Symbols +#define CHSMATH 6 // Math +#define CHMATHX 7 // Math Extension +#define CHSGREK 8 // Greek +#define CHSHEB 9 // Hebrew +#define CHSCYR 10 // Cyrillic +#define CHSKANA 11 // Japanese Kana +#define CHSUSER 12 // User-defined +#define CHSARB1 13 // Arabic +#define CHSARB2 14 // Arabic script + +#define NCHSETS 15 // # of character sets (excluding Asian) +#define ACHSETS 0x0E0 // Maximum character set value - Asian +#define ACHSMIN 0x024 // Minimum character set value - Asian +#define ACHCMAX 0x0FE // Maxmimum character value in Asian sets + +// LAST_LANG - Used for tables than contain items for each language. +// *_DBCS_LANG - Start and end points for double byte languages. + +#define LAST_LANG (XFLM_LA_LANG + 1) // Last language marker +#define FIRST_DBCS_LANG (XFLM_JP_LANG) +#define LAST_DBCS_LANG (XFLM_LA_LANG) + +// Collating Sequence Equates +// NOTE:The collating sequence MUST start at 32 (20h). This allows for the +// handling of nulls and control characters. + +#define COLLS 32 // first collating number (space/end of line) +#define COLS1 (COLLS + 9) // quotes +#define COLS2 (COLS1 + 5) // parens +#define COLS3 (COLS2 + 6) // money +#define COLS4 (COLS3 + 6) // math ops +#define COLS5 (COLS4 + 8) // math others +#define COLS6 (COLS5 + 14) // others: %#&@\_|~ +#define COLS7 (COLS6 + 13) // greek +#define COLS8 (COLS7 + 25) // numbers +#define COLS9 (COLS8 + 10) // alphabet +// Three below will overlap each other +#define COLS10 (COLS9 + 60) // cyrillic +#define COLS10h (COLS9 + 42) // hebrew - writes over european & cyrilic +#define COLS10a (COLS10h + 28) // arabic - inclusive from 198(C6)-252(FC) + +#define COLS11 253 // End of list - arabic goes to the end + +#define COLS0_ARABIC COLS11 // Set if arabic accent marking +#define COLS0_HEBREW COLS11 // Set if hebrew accent marking + +#define COLSOEM 254 // OEM character in upper range - non-collatable + // Phase COLSOEM out - not used! +#define COLS0_UNICODE 254 // Use this for UNICODE +#define COLS0 255 // graphics/misc - chars without a collate value + +// Definitions for diacritics. + +#define grave 0 +#define centerd 1 +#define tilde 2 +#define circum 3 +#define crossb 4 +#define slash 5 +#define acute 6 +#define umlaut 7 +#define macron 8 + +#define aposab 9 +#define aposbes 10 +#define aposba 11 + +#define ring 14 +#define dota 15 +#define dacute 16 +#define cedilla 17 +#define ogonek 18 +#define caron 19 +#define stroke 20 + +#define breve 22 +#define dotlesi 239 +#define dotlesj 25 + +#define gacute 83 // greek acute +#define gdia 84 // greek diaeresis +#define gactdia 85 // acute diaeresis +#define ggrvdia 86 // grave diaeresis +#define ggrave 87 // greek grave +#define gcircm 88 // greek circumflex +#define gsmooth 89 // smooth breathing +#define grough 90 // rough breathing +#define giota 91 // iota subscript +#define gsmact 92 // smooth breathing acute +#define grgact 93 // rough breathing acute +#define gsmgrv 94 // smooth breathing grave +#define grggrv 95 // rough breathing grave +#define gsmcir 96 // smooth breathing circumflex +#define grgcir 97 // rough breathing circumflex +#define gactio 98 // acute iota +#define ggrvio 99 // grave iota +#define gcirio 100 // circumflex iota +#define gsmio 101 // smooth iota +#define grgio 102 // rough iota +#define gsmaio 103 // smooth acute iota +#define grgaio 104 // rough acute iota +#define gsmgvio 105 // smooth grave iota +#define grggvio 106 // rough grave iota +#define gsmcio 107 // smooth circumflex iota +#define grgcio 108 // rough circumflex iota +#define ghprime 81 // high prime +#define glprime 82 // low prime + +#define racute 200 // russian acute +#define rgrave 201 // russian grave +#define rrtdesc 204 // russian right descender +#define rogonek 205 // russian ogonek +#define rmacron 206 // russian macron + +// GWBUG 30,645 - Had 200 and 70 - the 200 for max subcol was not +// enough for the sset and JP characters - computed wrong. +// This crashed the process that was building a key of sset characaters. + +#define MAX_SUBCOL_BUF (500) // (((MAX_KEY_SIZ / 4) * 3 + fluff +#define MAX_CASE_BYTES (150) // ((MAX_KEY_SIZ -(MAX_KEY_SIZ / 8)) / 8) * 2 + +// Flags + +#define HAD_SUB_COLLATION 0x01 // Set if had sub-collating values-diacritics +#define HAD_LOWER_CASE 0x02 // Set if you hit a lowercase character + + +#define COLL_FIRST_SUBSTRING 0x03 // First substring marker + +#define COLL_MARKER 0x04 // Marks place of sub-collation + +#define SC_LOWER 0x00 // Only lowercase characters exist +#define SC_MIXED 0x01 // Lower/uppercase flags follow in next byte +#define SC_UPPER 0x02 // Only upper characters exist +#define SC_SUB_COL 0x03 // Sub-collation follows (diacritics|extCh) + +#define COLL_TRUNCATED 0x0C // This key piece has been truncated from original + +#define UNK_UNICODE_CODE 0xFFFE // Used for collation + +#define TRUNCATED_FLAG 0x8000 +#define EXCLUSIVE_LT_FLAG 0x4000 +#define EXCLUSIVE_GT_FLAG 0x2000 +#define SEARCH_KEY_FLAG 0x1000 +#define KEY_COMPONENT_LENGTH_MASK 0x0FFF +#define KEY_LOW_VALUE 0x0FFE +#define KEY_HIGH_VALUE 0x0FFF + +FINLINE FLMBOOL isKeyComponentLTExclusive( + const FLMBYTE * pucKeyComponent) +{ + return( (FB2UW( pucKeyComponent) & EXCLUSIVE_LT_FLAG) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isKeyComponentGTExclusive( + const FLMBYTE * pucKeyComponent) +{ + return( (FB2UW( pucKeyComponent) & EXCLUSIVE_GT_FLAG) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isKeyComponentTruncated( + const FLMBYTE * pucKeyComponent) +{ + return( (FB2UW( pucKeyComponent) & TRUNCATED_FLAG) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isSearchKeyComponent( + const FLMBYTE * pucKeyComponent) +{ + return( (FB2UW( pucKeyComponent) & SEARCH_KEY_FLAG) ? TRUE : FALSE); +} + +FINLINE FLMUINT getKeyComponentLength( + const FLMBYTE * pucKeyComponent) +{ + return( (FLMUINT)(FB2UW( pucKeyComponent)) & KEY_COMPONENT_LENGTH_MASK); +} + +// Prototypes + +RCODE flmUTF8ToColText( // Source: fcollate.cpp + IF_PosIStream * pIStream, + FLMBYTE * pucCollatedStr, + FLMUINT * puiCollatedStrLen, + FLMBOOL bCaseInsensitive, + FLMUINT * puiCollationLen, + FLMUINT * puiCaseLen, + FLMUINT uiLanguage, + FLMUINT uiCharLimit, + FLMBOOL bFirstSubstring, + FLMBOOL bDataTruncated, + FLMBOOL * pbOriginalCharsLost, + FLMBOOL * pbDataTruncated); + +RCODE flmColText2StorageText( // Source: fcollate.cpp + const FLMBYTE * pucColStr, + FLMUINT uiColStrLen, + FLMBYTE * pucStorageBuf, + FLMUINT * puiStorageLen, + FLMUINT uiLang, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbFirstSubstring); + +RCODE flmAsiaUTF8ToColText( // Source: fcollate.cpp + IF_PosIStream * pIStream, + FLMBYTE * pucColStr, + FLMUINT * puiColStrLen, + FLMBOOL bCaseInsensitive, + FLMUINT * puiCollationLen, + FLMUINT * puiCaseLen, + FLMUINT uiCharLimit, + FLMBOOL bFirstSubstring, + FLMBOOL bDataTruncated, + FLMBOOL * pbDataTruncated); + +RCODE flmWPCheckDoubleCollation( // Source: fcollate.cpp + IF_PosIStream * pIStream, + FLMBOOL bUnicodeStream, + FLMBOOL bAllowTwoIntoOne, + FLMUNICODE * puzChar, + FLMUNICODE * puzChar2, + FLMBOOL * pbTwoIntoOne, + FLMUINT uiLanguage); + +FLMUINT16 flmWPAsiaGetCollation( // Source: fcollate.cpp + FLMUINT16 ui16WpChar, + FLMUINT16 ui16NextWpChar, + FLMUINT16 ui16PrevColValue, + FLMUINT16 * pui16ColValue, + FLMUINT16 * pui16SubColVal, + FLMBYTE * pucCaseBits, + FLMBOOL bUppercaseFlag); + +FLMUINT16 flmWPGetCollation( // Source: fcollate.cpp + FLMUINT16 ui16WpChar, + FLMUINT uiLanguage); + +FLMUINT16 flmWPUpper( // Source: fcollate.cpp + FLMUINT16 ui16WpChar); + +FLMUINT16 flmWPLower( // Source: fcollate.cpp + FLMUINT16 ui16WpChar); + +FLMBOOL flmWPIsUpper( // Source: fcollate.cpp + FLMUINT16 ui16WpChar); + +FLMBOOL flmWPBrkcar( // Source: fcollate.cpp + FLMUINT16 ui16WpChar, + FLMUINT16 * pui16BaseChar, + FLMUINT16 * pui16DiacriticChar); + +FLMUINT16 flmWPGetSubCol( // Source: fcollate.cpp + FLMUINT16 ui16WPValue, + FLMUINT16 ui16ColValue, + FLMUINT uiLanguage); + +RCODE flmInitCharMappingTables( void); // Source: funicode.cpp + +#ifdef DEF_FLM_UNI_GLOBALS + #define UNIG_EXTERN +#else + #define UNIG_EXTERN extern +#endif + +UNIG_EXTERN FLMUINT16 * gv_pUnicodeToWP60 +#ifdef DEF_FLM_UNI_GLOBALS + = NULL +#endif + ; + +UNIG_EXTERN FLMUINT16 * gv_pWP60ToUnicode +#ifdef DEF_FLM_UNI_GLOBALS + = NULL +#endif + ; + +UNIG_EXTERN FLMUINT gv_uiMinUniChar +#ifdef DEF_FLM_UNI_GLOBALS + = 0 +#endif + ; + +UNIG_EXTERN FLMUINT gv_uiMaxUniChar +#ifdef DEF_FLM_UNI_GLOBALS + = 0 +#endif + ; + +UNIG_EXTERN FLMUINT gv_uiMinWPChar +#ifdef DEF_FLM_UNI_GLOBALS + = 0 +#endif + ; + +UNIG_EXTERN FLMUINT gv_uiMaxWPChar +#ifdef DEF_FLM_UNI_GLOBALS + = 0 +#endif + ; +/**************************************************************************** +Desc: Convert a Unicode character to its WP equivalent +Ret: Returns TRUE if the character could be converted +****************************************************************************/ +FINLINE FLMBOOL flmUnicodeToWP( + FLMUNICODE uUniChar, // Unicode character to convert + FLMUINT16 * pui16WPChar) // Returns 0 or WPChar converted. +{ + if( uUniChar <= 127) + { + // Character is in the ASCII conversion range + + *pui16WPChar = uUniChar; + return( TRUE); + } + + if( uUniChar < gv_uiMinUniChar || uUniChar > gv_uiMaxUniChar) + { + *pui16WPChar = 0; + return( FALSE); + } + + if( (*pui16WPChar = gv_pUnicodeToWP60[ uUniChar - gv_uiMinUniChar]) != 0) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Convert a WP character to its Unicode equivalent +****************************************************************************/ +FINLINE RCODE flmWPToUnicode( + FLMUINT16 ui16WPChar, + FLMUNICODE * puUniChar) +{ + if( ui16WPChar <= 127) + { + // Character is in the ASCII conversion range + + *puUniChar = (FLMUNICODE)ui16WPChar; + return( NE_XFLM_OK); + } + + if( ui16WPChar < gv_uiMinWPChar || ui16WPChar > gv_uiMaxWPChar) + { + return( RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL)); + } + + *puUniChar = gv_pWP60ToUnicode[ ui16WPChar - gv_uiMinWPChar]; + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Reads the next character from the storage buffer +****************************************************************************/ +FINLINE RCODE flmGetCharFromUTF8Buf( + 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_XFLM_OK); + } + + if( pucBuf[ 0] <= 0x7F) + { + if( (*puChar = (FLMUNICODE)pucBuf[ 0]) != 0) + { + (*ppucBuf)++; + } + return( NE_XFLM_OK); + } + + if( uiMaxLen < 2 || (pucBuf[ 1] >> 6) != 0x02) + { + return( RC_SET( NE_XFLM_BAD_UTF8)); + } + + if( (pucBuf[ 0] >> 5) == 0x06) + { + *puChar = + (FLMUNICODE)(((FLMUNICODE)( pucBuf[ 0] - 0xC0) << 6) + + (FLMUNICODE)(pucBuf[ 1] - 0x80)); + (*ppucBuf) += 2; + return( NE_XFLM_OK); + } + + if( uiMaxLen < 3 || + (pucBuf[ 0] >> 4) != 0x0E || + (pucBuf[ 2] >> 6) != 0x02) + { + return( RC_SET( NE_XFLM_BAD_UTF8)); + } + + *puChar = + (FLMUNICODE)(((FLMUNICODE)(pucBuf[ 0] - 0xE0) << 12) + + ((FLMUNICODE)(pucBuf[ 1] - 0x80) << 6) + + (FLMUNICODE)(pucBuf[ 2] - 0x80)); + (*ppucBuf) += 3; + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Convert a Unicode character to UTF-8 +*****************************************************************************/ +FINLINE RCODE flmUni2UTF8( + FLMUNICODE uChar, + FLMBYTE * pucBuf, + FLMUINT * puiBufSize) +{ + if( uChar <= 0x007F) + { + if( pucBuf) + { + if( *puiBufSize < 1) + { + return( RC_SET( NE_XFLM_CONV_DEST_OVERFLOW)); + } + + *pucBuf = (FLMBYTE)uChar; + } + *puiBufSize = 1; + } + else if( uChar <= 0x07FF) + { + if( pucBuf) + { + if( *puiBufSize < 2) + { + return( RC_SET( NE_XFLM_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_XFLM_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_XFLM_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 +****************************************************************************/ +FINLINE RCODE flmGetUTF8CharFromUTF8Buf( + 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_XFLM_OK); + } + + if( pucBuf[ 0] <= 0x7F) + { + *pucDestBuf = pucBuf[ 0]; + (*ppucBuf)++; + *puiLen = 1; + return( NE_XFLM_OK); + } + + if( uiMaxLen < 2 || (pucBuf[ 1] >> 6) != 0x02) + { + return( RC_SET( NE_XFLM_BAD_UTF8)); + } + + if( (pucBuf[ 0] >> 5) == 0x06) + { + pucDestBuf[ 0] = pucBuf[ 0]; + pucDestBuf[ 1] = pucBuf[ 1]; + (*ppucBuf) += 2; + *puiLen = 2; + return( NE_XFLM_OK); + } + + if( uiMaxLen < 3 || + (pucBuf[ 0] >> 4) != 0x0E || + (pucBuf[ 2] >> 6) != 0x02) + { + return( RC_SET( NE_XFLM_BAD_UTF8)); + } + + pucDestBuf[ 0] = pucBuf[ 0]; + pucDestBuf[ 1] = pucBuf[ 1]; + pucDestBuf[ 2] = pucBuf[ 2]; + (*ppucBuf) += 3; + *puiLen = 3; + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE flmGetUTF8Length( + 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_XFLM_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_XFLM_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_XFLM_OK); +} + +RCODE flmUnicode2UTF8( // Source: funicode.cpp + FLMUNICODE * puzStr, + FLMUINT uiStrLen, + FLMBYTE * pucBuf, + FLMUINT * puiBufLength); + +#endif diff --git a/version5/src/fcomfact.cpp b/version5/src/fcomfact.cpp new file mode 100644 index 0000000..24732c7 --- /dev/null +++ b/version5/src/fcomfact.cpp @@ -0,0 +1,128 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the implementation for the F_DbSystemFactory +// class. +// +// 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: fcomfact.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#define PCOM_INIT_GUID +#include "flaimsys.h" +#include "fcomfact.h" + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_DbSystemFactory::QueryInterface( + RXFLMIID riid, + void ** ppvInt) +{ + RCODE rc = NE_XFLM_OK; + + if( (f_memcmp(&riid, &Internal_IID_XFLMIClassFactory, + sizeof( Internal_IID_XFLMIClassFactory)) == 0) || + (f_memcmp(&riid, &Internal_IID_XFLMIUnknown, + sizeof( Internal_IID_XFLMIUnknown)) == 0) ) + { + *ppvInt = this; + AddRef(); + } + else + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_INTERFACE); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FLMUINT32 F_DbSystemFactory::AddRef(void) +{ + LockModule(); + return 2; +} + +/******************************************************************** +Desc: +*********************************************************************/ +FLMUINT32 F_DbSystemFactory::Release(void) +{ + UnlockModule(); + return 1; +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_DbSystemFactory::LockServer( + bool bLock) +{ + if( bLock) + { + LockModule(); + } + else + { + UnlockModule(); + } + + return( NE_XFLM_OK); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_DbSystemFactory::CreateInstance( + XFLMIUnknown * pUnkOut, + RXFLMIID riid, + void ** ppvOut) +{ + RCODE rc = NE_XFLM_OK; + F_DbSystem * pDbSystem = NULL; + + if (pUnkOut) + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_INTERFACE); + goto Exit; + } + + if( (pDbSystem = f_new F_DbSystem) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pDbSystem->QueryInterface( riid, ppvOut))) + { + goto Exit; + } + +Exit: + + if (pDbSystem) + { + pDbSystem->Release(); + } + + return( rc); +} diff --git a/version5/src/fcomfact.h b/version5/src/fcomfact.h new file mode 100644 index 0000000..85be6af --- /dev/null +++ b/version5/src/fcomfact.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the definition for the F_DbSystemFactory +// class, which is used to create an interface to the F_DbSystem object. +// The factory is only used by COM clients. +// +// 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: fcomfact.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FCOMFACT_H +#define FCOMFACT_H + + class F_DbSystemFactory : public XFLMIClassFactory + { + public: + + RCODE XFLMAPI QueryInterface( + RXFLMIID riid, + void ** ppvInt); + + RCODE XFLMAPI CreateInstance( + XFLMIUnknown * pUnkOut, + RXFLMIID riid, + void ** ppvOut); + + RCODE XFLMAPI LockServer( + bool bLock); + + FLMUINT32 XFLMAPI AddRef( void); + + FLMUINT32 XFLMAPI Release( void); + }; + +#endif // FCOMFACT_H + diff --git a/version5/src/fdbcnfig.cpp b/version5/src/fdbcnfig.cpp new file mode 100644 index 0000000..c1792bb --- /dev/null +++ b/version5/src/fdbcnfig.cpp @@ -0,0 +1,1307 @@ +//------------------------------------------------------------------------------ +// Desc: Database config get/set functions +// +// Tabs: 3 +// +// Copyright (c) 1996-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: fdbcnfig.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Set the RFL keep files flag. +****************************************************************************/ +RCODE XFLMAPI F_Db::setRflKeepFilesFlag( + FLMBOOL bKeepFiles) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDbLocked = FALSE; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Make sure we don't have a transaction going + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Make sure there is no active backup running + + m_pDatabase->lockMutex(); + if (m_pDatabase->m_bBackupActive) + { + m_pDatabase->unlockMutex(); + rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); + goto Exit; + } + m_pDatabase->unlockMutex(); + + // Need to lock the database but not start a transaction yet. + + if (!(m_uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) + { + if (RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, + XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bDbLocked = TRUE; + } + + // If we aren't changing the keep flag, jump to exit without doing + // anything. + + if ((bKeepFiles && + m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles) || + (!bKeepFiles && + !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles)) + { + goto Exit; // Will return NE_XFLM_OK; + } + + // Force a checkpoint and roll to the next RFL file numbers. + // When changing from keep to no-keep or vice versa, we need to + // go to a new RFL file so that the new RFL file gets new + // serial numbers and a new keep or no-keep flag. + + if (RC_BAD( rc = doCheckpoint( XFLM_NO_TIMEOUT))) + { + goto Exit; + } + + f_memcpy( &m_pDatabase->m_uncommittedDbHdr, + &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + m_pDatabase->m_uncommittedDbHdr.ui8RflKeepFiles = + (FLMUINT8)(bKeepFiles + ? (FLMUINT8)1 + : (FLMUINT8)0); + + // Force a new RFL file - this will also write out the entire + // log header - including the changes we made above. + + if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, TRUE))) + { + goto Exit; + } + +Exit: + + if (bDbLocked) + { + dbUnlock(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Set the RFL directory for a database. +****************************************************************************/ +RCODE XFLMAPI F_Db::setRflDir( + const char * pszNewRflDir) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDbLocked = FALSE; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Make sure we don't have a transaction going + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Make sure there is no active backup running + + m_pDatabase->lockMutex(); + if (m_pDatabase->m_bBackupActive) + { + m_pDatabase->unlockMutex(); + rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); + goto Exit; + } + m_pDatabase->unlockMutex(); + + // Make sure the path exists and that it is a directory + // rather than a file. + + if (pszNewRflDir && *pszNewRflDir) + { + if (!gv_pFileSystem->IsDir( pszNewRflDir)) + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + } + + // Need to lock the database because we can't change the RFL + // directory until after the checkpoint has completed. The + // checkpoint code will unlock the transaction, but not the + // file if we have an explicit lock. We need to do this to + // prevent another transaction from beginning before we have + // changed the RFL directory. + + if (!(m_uiFlags & (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED))) + { + if( RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, + XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bDbLocked = TRUE; + } + + // Force a checkpoint and roll to the next RFL file numbers. Both + // of these steps are necessary to ensure that we won't have to do + // any recovery using the current RFL file - because we do not + // move the current RFL file to the new directory. Forcing the + // checkpoint ensures that we have no transactions that will need + // to be recovered if we were to crash. Rolling the RFL file number + // ensures that no more transactions will be logged to the current + // RFL file. + + if (RC_BAD( rc = doCheckpoint( XFLM_NO_TIMEOUT))) + { + goto Exit; + } + + // Force a new RFL file. + + if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, FALSE))) + { + goto Exit; + } + + // Set the RFL directory to the new value now that we have + // finished the checkpoint and rolled to the next RFL file. + + m_pDatabase->lockMutex(); + rc = m_pDatabase->m_pRfl->setRflDir( pszNewRflDir); + m_pDatabase->unlockMutex(); + +Exit: + + if (bDbLocked) + { + dbUnlock(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Set the RFL file size limits for a database. +****************************************************************************/ +RCODE XFLMAPI F_Db::setRflFileSizeLimits( + FLMUINT uiMinRflSize, + FLMUINT uiMaxRflSize) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Make sure the limits are valid. + + // Maximum must be enough to hold at least one packet plus + // the RFL header. Minimum must not be greater than the + // maximum. NOTE: Minimum and maximum are allowed to be + // equal, but in all cases, maximum takes precedence over + // minimum. We will first NOT exceed the maximum. Then, + // if possible, we will go above the minimum. + + if (uiMaxRflSize < RFL_MAX_PACKET_SIZE + 512) + { + uiMaxRflSize = RFL_MAX_PACKET_SIZE + 512; + } + if (uiMaxRflSize > gv_XFlmSysData.uiMaxFileSize) + { + uiMaxRflSize = gv_XFlmSysData.uiMaxFileSize; + } + if (uiMinRflSize > uiMaxRflSize) + { + uiMinRflSize = uiMaxRflSize; + } + + // Start an update transaction. Must not already be one going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Commit the transaction. + + m_pDatabase->m_uncommittedDbHdr.ui32RflMinFileSize = + (FLMUINT32)uiMinRflSize; + m_pDatabase->m_uncommittedDbHdr.ui32RflMaxFileSize = + (FLMUINT32)uiMaxRflSize; + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Roll to the next RFL file for this database +****************************************************************************/ +RCODE XFLMAPI F_Db::rflRollToNextFile( void) +{ + RCODE rc = NE_XFLM_OK; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // NOTE: finishCurrFile will not roll to the next file if the current + // file has not been created. + + if (RC_BAD( rc = m_pDatabase->m_pRfl->finishCurrFile( this, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set keep aborted transactions in RFL flag. +****************************************************************************/ +RCODE XFLMAPI F_Db::setKeepAbortedTransInRflFlag( + FLMBOOL bKeep + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Start an update transaction. Must not already be one going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Change the uncommitted log header + + m_pDatabase->m_uncommittedDbHdr.ui8RflKeepAbortedTrans = + (FLMUINT8)(bKeep + ? (FLMUINT8)1 + : (FLMUINT8)0); + + // Commit the transaction. + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Set auto turn off keep RFL flag. +****************************************************************************/ +RCODE XFLMAPI F_Db::setAutoTurnOffKeepRflFlag( + FLMBOOL bAutoTurnOff + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Start an update transaction. Must not already be one going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Change the uncommitted log header + + m_pDatabase->m_uncommittedDbHdr.ui8RflAutoTurnOffKeep = + (FLMUINT8)(bAutoTurnOff + ? (FLMUINT8)1 + : (FLMUINT8)0); + + // Commit the transaction. + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves the Checkpoint info for the database passed in. This assumes + global mutex has already been locked. +*****************************************************************************/ +void F_Database::getCPInfo( + XFLM_CHECKPOINT_INFO * pCheckpointInfo) +{ + FLMUINT uiElapTime; + FLMUINT uiCurrTime; + + flmAssert( pCheckpointInfo); + + f_memset( pCheckpointInfo, 0, sizeof( XFLM_CHECKPOINT_INFO)); + if (m_pCPInfo) + { + pCheckpointInfo->bRunning = m_pCPInfo->bDoingCheckpoint; + if (pCheckpointInfo->bRunning) + { + if (m_pCPInfo->uiStartTime) + { + uiCurrTime = FLM_GET_TIMER(); + + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, + m_pCPInfo->uiStartTime); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, pCheckpointInfo->uiRunningTime); + } + else + { + pCheckpointInfo->uiRunningTime = 0; + } + pCheckpointInfo->bForcingCheckpoint = + m_pCPInfo->bForcingCheckpoint; + if (m_pCPInfo->uiForceCheckpointStartTime) + { + uiCurrTime = FLM_GET_TIMER(); + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, + m_pCPInfo->uiForceCheckpointStartTime); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, + pCheckpointInfo->uiForceCheckpointRunningTime); + } + else + { + pCheckpointInfo->uiForceCheckpointRunningTime = 0; + } + pCheckpointInfo->iForceCheckpointReason = + m_pCPInfo->iForceCheckpointReason; + pCheckpointInfo->bWritingDataBlocks = + m_pCPInfo->bWritingDataBlocks; + pCheckpointInfo->uiLogBlocksWritten = + m_pCPInfo->uiLogBlocksWritten; + pCheckpointInfo->uiDataBlocksWritten = + m_pCPInfo->uiDataBlocksWritten; + } + pCheckpointInfo->uiBlockSize = m_uiBlockSize; + pCheckpointInfo->uiDirtyCacheBytes = + m_uiDirtyCacheCount * m_uiBlockSize; + if (m_pCPInfo->uiStartWaitTruncateTime) + { + uiCurrTime = FLM_GET_TIMER(); + + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, + m_pCPInfo->uiStartWaitTruncateTime); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, + pCheckpointInfo->uiWaitTruncateTime); + } + else + { + pCheckpointInfo->uiWaitTruncateTime = 0; + } + } +} + +/**************************************************************************** +Desc: Retrieves the Checkpoint info for the database. +*****************************************************************************/ +void XFLMAPI F_Db::getCheckpointInfo( + XFLM_CHECKPOINT_INFO * pCheckpointInfo) +{ + m_pDatabase->lockMutex(); + m_pDatabase->getCPInfo( pCheckpointInfo); + m_pDatabase->unlockMutex(); +} + +/**************************************************************************** +Desc: Returns current RFL file number +****************************************************************************/ +RCODE XFLMAPI F_Db::getRflFileNum( + FLMUINT * puiRflFileNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiLastCPFile; + FLMUINT uiLastTransFile; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // Get the CP and last trans RFL file numbers. Need to + // return the higher of the two. No need to lock the + // mutex because we are in an update transaction. + + uiLastCPFile = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflLastCPFileNum; + + uiLastTransFile = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum; + + *puiRflFileNum = uiLastCPFile > uiLastTransFile + ? uiLastCPFile + : uiLastTransFile; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns highest not used RFL file number +****************************************************************************/ +RCODE XFLMAPI F_Db::getHighestNotUsedRflFileNum( + FLMUINT * puiHighestNotUsedRflFileNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiLastCPFile; + FLMUINT uiLastTransFile; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // Get the CP and last trans RFL file numbers. Need to + // return the lower of the two minus 1. + + uiLastCPFile = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflLastCPFileNum; + + uiLastTransFile = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum; + + *puiHighestNotUsedRflFileNum = + (FLMUINT)((uiLastCPFile < uiLastTransFile + ? uiLastCPFile + : uiLastTransFile) - 1); +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns RFL file size limits for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getRflFileSizeLimits( + FLMUINT * puiRflMinFileSize, + FLMUINT * puiRflMaxFileSize + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + if (puiRflMinFileSize) + { + *puiRflMinFileSize = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflMinFileSize; + } + if (puiRflMaxFileSize) + { + *puiRflMaxFileSize = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflMaxFileSize; + } + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns RFL keep flag for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getRflKeepFlag( + FLMBOOL * pbKeep + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *pbKeep = m_pDatabase->m_uncommittedDbHdr.ui8RflKeepFiles + ? TRUE + : FALSE; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns last backup transaction ID for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getLastBackupTransID( + FLMUINT64 * pui64LastBackupTransID + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *pui64LastBackupTransID = + m_pDatabase->m_uncommittedDbHdr.ui64LastBackupTransID; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns blocks changed since the last backup for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getBlocksChangedSinceBackup( + FLMUINT * puiBlocksChangedSinceBackup + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *puiBlocksChangedSinceBackup = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32BlksChangedSinceBackup; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the auto-turn-off-keep-RFL flag for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getAutoTurnOffKeepRflFlag( + FLMBOOL * pbAutoTurnOff + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *pbAutoTurnOff = m_pDatabase->m_uncommittedDbHdr.ui8RflAutoTurnOffKeep + ? TRUE + : FALSE; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the keep aborted transactions in RFL flag for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getKeepAbortedTransInRflFlag( + FLMBOOL * pbKeep + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *pbKeep = m_pDatabase->m_uncommittedDbHdr.ui8RflKeepAbortedTrans + ? TRUE + : FALSE; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns disk space usage for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getDiskSpaceUsage( + FLMUINT64 * pui64DataSize, + FLMUINT64 * pui64RollbackSize, + FLMUINT64 * pui64RflSize) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiEndAddress; + FLMUINT uiLastFileNumber; + FLMUINT64 ui64LastFileSize; + char szTmpName [F_PATH_MAX_SIZE]; + char szRflDir [F_PATH_MAX_SIZE]; + IF_FileHdl * pFileHdl = NULL; + IF_DirHdl * pDirHdl = NULL; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // See if they want the database files sizes. + + if (pui64DataSize) + { + uiEndAddress = m_uiLogicalEOF; + uiLastFileNumber = FSGetFileNumber( uiEndAddress); + + // Last file number better be in the proper range. + + flmAssert( uiLastFileNumber >= 1 && + uiLastFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER); + + // Get the actual size of the last file. + + if (RC_BAD( rc = m_pSFileHdl->GetFileSize( uiLastFileNumber, + &ui64LastFileSize))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + if (uiLastFileNumber > 1) + { + rc = NE_XFLM_OK; + ui64LastFileSize = 0; + } + else + { + + // Should always be a data file #1 + + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + } + else + { + goto Exit; + } + } + + // One of two situations exists with respect to the last + // file: 1) it has not been fully written out yet (blocks + // are still cached, or 2) it has been written out and + // extended beyond what the logical EOF shows. We want + // the larger of these two possibilities. + + if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) + { + ui64LastFileSize = FSGetFileOffset( uiEndAddress); + } + + if (uiLastFileNumber == 1) + { + + // Only one file - use last file's size. + + *pui64DataSize = ui64LastFileSize; + } + else + { + + // Size is the sum of full size for all files except the last one, + // plus the calculated (or actual) size of the last one. + + (*pui64DataSize) = (FLMUINT64)(uiLastFileNumber - 1) * + (FLMUINT64)m_pDatabase->m_uiMaxFileSize + + ui64LastFileSize; + } + } + + // See if they want the rollback files sizes. + + if (pui64RollbackSize) + { + uiEndAddress = (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RblEOF; + uiLastFileNumber = FSGetFileNumber( uiEndAddress); + + // Last file number better be in the proper range. + + flmAssert( !uiLastFileNumber || + (uiLastFileNumber >= + FIRST_LOG_BLOCK_FILE_NUMBER && + uiLastFileNumber <= + MAX_LOG_BLOCK_FILE_NUMBER)); + + // Get the size of the last file number. + + if (RC_BAD( rc = m_pSFileHdl->GetFileSize( uiLastFileNumber, + &ui64LastFileSize))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + if (uiLastFileNumber) + { + rc = NE_XFLM_OK; + ui64LastFileSize = 0; + } + else + { + + // Should always have rollback file #0 + + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + } + else + { + goto Exit; + } + } + + // If the EOF offset for the last file is greater than the + // actual file size, use it instead of the actual file size. + + if (FSGetFileOffset( uiEndAddress) > ui64LastFileSize) + { + ui64LastFileSize = FSGetFileOffset( uiEndAddress); + } + + // Special case handling here because rollback file numbers start with + // zero and then skip to a file number that is one beyond the + // highest data file number - so the calculation for file size needs + // to account for this. + + if (!uiLastFileNumber) + { + *pui64RollbackSize = ui64LastFileSize; + } + else + { + FLMUINT uiFirstLogFileNum = FIRST_LOG_BLOCK_FILE_NUMBER; + + // Add full size of file zero plus a full size for every file + // except the last one. + + (*pui64RollbackSize) = (FLMUINT64)(uiLastFileNumber - + uiFirstLogFileNum + 1) * + (FLMUINT64)m_pDatabase->m_uiMaxFileSize + + ui64LastFileSize; + } + } + + // See if they want the roll-forward log file sizes. + + if (pui64RflSize) + { + char * pszDbFileName = m_pDatabase->m_pszDbPath; + + *pui64RflSize = 0; + + // Scan the RFL directory for + // RFL files. The call below to rflGetDirAndPrefix is done + // to get the prefix. It will not return the correct + // RFL directory name, because we are passing in a NULL + // RFL directory path (which may or may not be correct). + // That's OK, because we get the RFL directory directly + // from the F_Rfl object anyway. + + if (RC_BAD( rc = rflGetDirAndPrefix( pszDbFileName, + NULL, szRflDir))) + { + goto Exit; + } + + // We need to get the RFL directory from the F_Rfl object. + + m_pDatabase->lockMutex(); + f_strcpy( szRflDir, m_pDatabase->m_pRfl->getRflDirPtr()); + m_pDatabase->unlockMutex(); + + // See if the directory exists. If not, we are done. + + if (gv_pFileSystem->IsDir( szRflDir)) + { + + // Open the directory and scan for RFL files. + + if (RC_BAD( rc = gv_pFileSystem->OpenDir( szRflDir, + "*", &pDirHdl))) + { + goto Exit; + } + for (;;) + { + if (RC_BAD( rc = pDirHdl->Next())) + { + if (rc == NE_XFLM_IO_NO_MORE_FILES) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + pDirHdl->CurrentItemPath( szTmpName); + + // If the item looks like an RFL file name, get + // its size. + + if (!pDirHdl->CurrentItemIsDir() && + rflGetFileNum( szTmpName, &uiLastFileNumber)) + { + + // Open the file and get its size. + + if (RC_BAD( rc = gv_pFileSystem->OpenBlockFile( + szTmpName, XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, + 512, &pFileHdl))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + ui64LastFileSize = 0; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pFileHdl->Size( &ui64LastFileSize))) + { + goto Exit; + } + } + if (pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + (*pui64RflSize) += ui64LastFileSize; + } + } + } + } + +Exit: + + if (pFileHdl) + { + pFileHdl->Release(); + } + + if (pDirHdl) + { + pDirHdl->Release(); + } + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next incremental backup sequence number for the database +****************************************************************************/ +RCODE XFLMAPI F_Db::getNextIncBackupSequenceNum( + FLMUINT * puiNextIncBackupSequenceNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + else if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + *puiNextIncBackupSequenceNum = + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32IncBackupSeqNum; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns list of lock waiters in an object that allows caller to + iterate through the list. +****************************************************************************/ +RCODE XFLMAPI F_Db::getLockWaiters( + IF_LockInfoClient * pLockInfo + ) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pDatabase->m_pDatabaseLockObj) + { + rc = m_pDatabase->m_pDatabaseLockObj->GetLockInfo( pLockInfo); + } + else + { + pLockInfo->setLockCount( 0); + } + return( rc); +} + +/**************************************************************************** +Desc: Returns RFL directory for the database +****************************************************************************/ +void XFLMAPI F_Db::getRflDir( + char * pszRflDir + ) +{ + m_pDatabase->lockMutex(); + f_strcpy( pszRflDir, m_pDatabase->m_pRfl->getRflDirPtr()); + m_pDatabase->unlockMutex(); +} + +/**************************************************************************** +Desc: Returns database serial number +****************************************************************************/ +void XFLMAPI F_Db::getSerialNumber( + char * pucSerialNumber) +{ + m_pDatabase->lockMutex(); + f_memcpy( pucSerialNumber, m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, + XFLM_SERIAL_NUM_SIZE); + m_pDatabase->unlockMutex(); +} diff --git a/version5/src/fdbcopy.cpp b/version5/src/fdbcopy.cpp new file mode 100644 index 0000000..8a118d3 --- /dev/null +++ b/version5/src/fdbcopy.cpp @@ -0,0 +1,888 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_DbSystem::dbCopy method. +// +// 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: fdbcopy.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +// Local prototypes + +typedef struct Copied_Name * COPIED_NAME_p; + +typedef struct Copied_Name +{ + char szPath[ F_PATH_MAX_SIZE]; + COPIED_NAME_p pNext; +} COPIED_NAME; + +typedef struct +{ + FLMUINT64 ui64BytesToCopy; + FLMUINT64 ui64BytesCopied; + FLMBOOL bNewSrcFile; + char szSrcFileName[ F_PATH_MAX_SIZE]; + char szDestFileName[ F_PATH_MAX_SIZE]; +} DB_COPY_INFO, * DB_COPY_INFO_p; + + +FSTATIC RCODE flmCopyFile( + F_FileSystem * pFileSystem, + DB_COPY_INFO * pDbCopyInfo, + COPIED_NAME ** ppCopiedListRV, + FLMBOOL bOkToTruncate, + IF_DbCopyStatus * ifpStatus); + +/**************************************************************************** +Desc: Copies a database, including roll-forward log files. +****************************************************************************/ +RCODE F_DbSystem::dbCopy( + const char * pszSrcDbName, + // [IN] Name of source database to be copied. + const char * pszSrcDataDir, + // [IN] Name of source data directory. + const char * pszSrcRflDir, + // [IN] RFL directory of source database. NULL can be + // passed to indicate that the log files are located + // in the same directory as the other database files. + const char * pszDestDbName, + // [IN] Destination name of database - will be overwritten if it + // already exists. + const char * pszDestDataDir, + // [IN] Name of destination data directory. + const char * pszDestRflDir, + // [IN] RFL directory of destination database. NULL can be + // passed to indicate that the log files are to be located + // in the same directory as the other database files. + IF_DbCopyStatus * ifpStatus) + // [IN] Status callback interface. +{ + RCODE rc = NE_XFLM_OK; + IF_Db * pDb = NULL; + FLMBOOL bDbLocked = FALSE; + + // Make sure the destination database is closed + + if (RC_BAD( rc = checkDatabaseClosed( pszDestDbName, pszDestDataDir))) + { + goto Exit; + } + + // Open the source database so we can force a checkpoint. + + if (RC_BAD( rc = openDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, + NULL, 0, &pDb))) + { + goto Exit; + } + + // Need to lock the database, because we want to do a checkpoint + // and then the copy immediately after without letting other + // threads have the opportunity to get in and update the + // database. + + if (RC_BAD( rc = pDb->dbLock( XFLM_LOCK_EXCLUSIVE, 0, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bDbLocked = TRUE; + + // Force a checkpoint + + if (RC_BAD( rc = pDb->doCheckpoint( XFLM_NO_TIMEOUT))) + { + goto Exit; + } + + // Once we get this far, we have exclusive access to the database + // and we have forced a checkpoint. The database's contents are + // guaranteed to be on disk at this point, and they will not + // change. + + rc = copyDb( pszSrcDbName, pszSrcDataDir, pszSrcRflDir, + pszDestDbName, pszDestDataDir, pszDestRflDir, + ifpStatus); + +Exit: + + // Unlock and close the database + + if (bDbLocked) + { + pDb->dbUnlock(); + } + + if (pDb) + { + pDb->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Copy a database's files, including roll-forward log files. +*****************************************************************************/ +RCODE F_DbSystem::copyDb( + const char * pszSrcDbName, + const char * pszSrcDataDir, + const char * pszSrcRflDir, + const char * pszDestDbName, + const char * pszDestDataDir, + const char * pszDestRflDir, + IF_DbCopyStatus * ifpStatus) +{ + RCODE rc = NE_XFLM_OK; + DB_COPY_INFO DbCopyInfo; + F_SuperFileHdl SrcSFileHdl; + F_SuperFileHdl DestSFileHdl; + FLMUINT uiFileNumber; + FLMUINT uiHighFileNumber; + FLMUINT uiHighLogFileNumber; + FLMUINT64 ui64FileSize; + F_Database * pDatabase = NULL; + FLMBOOL bMutexLocked = FALSE; + IF_FileHdl * pLockFileHdl = NULL; + IF_FileHdl * pTmpFileHdl = NULL; + IF_DirHdl * pDirHdl = NULL; + FLMBOOL bDatabaseLocked = FALSE; + FLMBOOL bWriteLocked = FALSE; + ServerLockObject * pWriteLockObj = NULL; + ServerLockObject * pDatabaseLockObj = NULL; + COPIED_NAME * pCopiedList = NULL; + FLMBOOL bUsedDatabase = FALSE; + eDbLockType eCurrLockType; + FLMUINT uiThreadId; + FLMUINT uiNumExclQueued; + FLMUINT uiNumSharedQueued; + FLMUINT uiPriorityCount; + char * pszActualSrcRflPath = NULL; + char * pszActualDestRflPath = NULL; + FLMBOOL bCreatedDestRflDir = FALSE; + FLMBOOL bWaited; + F_SEM hWaitSem = F_SEM_NULL; + + f_memset( &DbCopyInfo, 0, sizeof( DbCopyInfo)); + + // Should not do anything if the source and destination names + // are the same. + + if (f_stricmp( pszSrcDbName, pszDestDbName) == 0) + { + goto Exit; + } + + // Create a "wait" semaphore + + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + // Allocate memory for paths we don't want to push onto the stack. + + if (RC_BAD( rc = f_calloc( F_PATH_MAX_SIZE * 2, + &pszActualSrcRflPath))) + { + goto Exit; + } + + pszActualDestRflPath = &pszActualSrcRflPath[ F_PATH_MAX_SIZE]; + + // Set up the super file object for the source database. + // Must at least open the control file. + + if (RC_BAD( rc = SrcSFileHdl.Setup( NULL, pszSrcDbName, pszSrcDataDir))) + { + goto Exit; + } + + // Lock the destination database, if not already locked. + // This is so we can overwrite it without necessarily + // deleting it. May unlock and re-lock the global mutex. + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + +retry: + + if (RC_BAD( rc = F_DbSystem::findDatabase( pszDestDbName, + pszDestDataDir, &pDatabase))) + { + goto Exit; + } + + // If we didn't find an FFILE structure, get an + // exclusive lock on the file. + + if (!pDatabase) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Attempt to get an exclusive lock on the file. + + if (RC_BAD( rc = flmCreateLckFile( pszDestDbName, &pLockFileHdl))) + { + goto Exit; + } + } + else + { + // The call to verifyOkToUse will wait if the database is in + // the process of being opened by another thread. + + if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) + { + goto Exit; + } + + if (bWaited) + { + goto retry; + } + + // Increment the open count on the F_Database object so it will not + // disappear while we are copying the database. + + pDatabase->incrOpenCount(); + bUsedDatabase = TRUE; + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Lock the destination file object and transaction + // object, if not already locked. + + pDatabase->m_pDatabaseLockObj->GetLockInfo( (FLMINT)0, + &eCurrLockType, + &uiThreadId, &uiNumExclQueued, + &uiNumSharedQueued, + &uiPriorityCount); + if (eCurrLockType != XFLM_LOCK_EXCLUSIVE || + uiThreadId != f_threadId()) + { + pDatabaseLockObj = pDatabase->m_pDatabaseLockObj; + pDatabaseLockObj->AddRef(); + + if (RC_BAD( rc = pDatabaseLockObj->Lock( + NULL, hWaitSem, TRUE, FALSE, TRUE, 15, 0))) + { + goto Exit; + } + bDatabaseLocked = TRUE; + } + + // Lock the write object, if not already locked + + pDatabase->m_pWriteLockObj->GetLockInfo( (FLMINT)0, + &eCurrLockType, + &uiThreadId, &uiNumExclQueued, + &uiNumSharedQueued, + &uiPriorityCount); + if (eCurrLockType != XFLM_LOCK_EXCLUSIVE || + uiThreadId != (FLMUINT)f_threadId()) + { + pWriteLockObj = pDatabase->m_pWriteLockObj; + pWriteLockObj->AddRef(); + + // Only contention here is with the checkpoint thread - wait + // forever until the checkpoint thread gives it up. + + if (RC_BAD( rc = pDatabase->dbWriteLock( hWaitSem))) + { + goto Exit; + } + bWriteLocked = TRUE; + } + } + + // Set up the super file object for the destination database. + + if (RC_BAD( rc = DestSFileHdl.Setup( NULL, pszDestDbName, + pszDestDataDir))) + { + goto Exit; + } + + // See how many files we have and calculate the total size. + + uiHighFileNumber = 0; + for (;;) + { + if ((RC_BAD( rc = SrcSFileHdl.GetFileSize( + uiHighFileNumber, &ui64FileSize))) || !ui64FileSize ) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME || + !ui64FileSize) + { + // If the control file doesn't exist, we will return + // path not found. + + if (!uiHighFileNumber) + { + goto Exit; + } + uiHighFileNumber--; + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + DbCopyInfo.ui64BytesToCopy += ui64FileSize; + if (uiHighFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) + { + break; + } + uiHighFileNumber++; + } + + // See how many rollback log files we have, and calculate + // their total size. + + uiHighLogFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + for (;;) + { + if ((RC_BAD( rc = SrcSFileHdl.GetFileSize( + uiHighLogFileNumber, &ui64FileSize))) || !ui64FileSize) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME || + !ui64FileSize) + { + if (uiHighLogFileNumber == + FIRST_LOG_BLOCK_FILE_NUMBER) + { + uiHighLogFileNumber = 0; + } + else + { + uiHighLogFileNumber--; + } + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + DbCopyInfo.ui64BytesToCopy += ui64FileSize; + if (uiHighLogFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) + { + break; + } + uiHighLogFileNumber++; + } + + // Get the sizes of the roll-forward log files + + if (RC_BAD( rc = rflGetDirAndPrefix( pszSrcDbName, + pszSrcRflDir, pszActualSrcRflPath))) + { + goto Exit; + } + + if (RC_BAD( rc = gv_pFileSystem->OpenDir( + pszActualSrcRflPath, (char *)"*", &pDirHdl))) + { + goto Exit; + } + + for (;;) + { + if (RC_BAD( rc = pDirHdl->Next())) + { + if (rc == NE_XFLM_IO_NO_MORE_FILES) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + + // If the current file is an RFL file, increment ui64BytesToCopy + + if (rflGetFileNum( pDirHdl->CurrentItemName(), &uiFileNumber)) + { + DbCopyInfo.ui64BytesToCopy += (FLMUINT64)pDirHdl->CurrentItemSize(); + } + } + + pDirHdl->Release(); + pDirHdl = NULL; + + // Close all file handles in the source and destination + + SrcSFileHdl.ReleaseFiles( TRUE); + DestSFileHdl.ReleaseFiles( TRUE); + + // Copy the database files. + + for (uiFileNumber = 0; uiFileNumber <= uiHighFileNumber; uiFileNumber++) + { + + // Get the source file path and destination file path. + + if( RC_BAD( rc = SrcSFileHdl.GetFilePath( + uiFileNumber, DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + if( RC_BAD( rc = DestSFileHdl.GetFilePath( + uiFileNumber, DbCopyInfo.szDestFileName))) + { + goto Exit; + } + + DbCopyInfo.bNewSrcFile = TRUE; + if (RC_BAD( rc = flmCopyFile( gv_pFileSystem, + &DbCopyInfo, &pCopiedList, TRUE, + ifpStatus))) + { + goto Exit; + } + } + + // Copy the additional rollback log files, if any. + + for (uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + uiFileNumber <= uiHighLogFileNumber; uiFileNumber++) + { + + // Get the source file path and destination file path. + + if (RC_BAD( rc = SrcSFileHdl.GetFilePath( uiFileNumber, + DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + + if (RC_BAD( rc = DestSFileHdl.GetFilePath( uiFileNumber, + DbCopyInfo.szDestFileName))) + { + goto Exit; + } + + DbCopyInfo.bNewSrcFile = TRUE; + if (RC_BAD( rc = flmCopyFile( gv_pFileSystem, + &DbCopyInfo, &pCopiedList, TRUE, + ifpStatus))) + { + goto Exit; + } + } + + // Copy the RFL files + + // Create the destination RFL directory, if needed. The purpose of this + // code is two-fold: 1) We want to keep track of the fact that we tried + // to create the destination RFL directory so we can try to remove it + // if the copy fails; 2) If the destination RFL directory path specifies + // a directory with existing files, we want to remove them. + + if( RC_BAD( rc = rflGetDirAndPrefix( pszDestDbName, + pszDestRflDir, pszActualDestRflPath))) + { + goto Exit; + } + + if( RC_OK( gv_pFileSystem->Exists( pszActualDestRflPath))) + { + if( gv_pFileSystem->IsDir( pszActualDestRflPath)) + { + // Remove the existing directory and all files, etc. + + (void)gv_pFileSystem->RemoveDir( + pszActualDestRflPath, TRUE); + } + else + { + (void)gv_pFileSystem->Delete( pszActualDestRflPath); + } + } + + // Try to create the destination RFL directory. This might fail if + // another process was accessing the directory for some reason + // (i.e., from a command prompt), when we tried to remove it above. + // We really don't care if the call to CreateDir is sucessful, because + // when we try to create the destination files (below), the FLAIM file + // file system code will try to create any necessary directories. + + (void)gv_pFileSystem->CreateDir( pszActualDestRflPath); + bCreatedDestRflDir = TRUE; + + // Copy the RFL files. NOTE: We need to copy all of the RFL files + // in the source RFL directory so that they will be available + // when performing a database restore operation. + + if (RC_BAD( rc = gv_pFileSystem->OpenDir( + pszActualSrcRflPath, (char *)"*", &pDirHdl))) + { + goto Exit; + } + + for (;;) + { + if( RC_BAD( rc = pDirHdl->Next())) + { + if (rc == NE_XFLM_IO_NO_MORE_FILES) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + + // If the current file is an RFL file, copy it to the destination + + if( rflGetFileNum( pDirHdl->CurrentItemName(), &uiFileNumber)) + { + // Get the source file path and the destination file path. + + if (RC_BAD( rc = rflGetFileName( pszSrcDbName, + pszSrcRflDir, uiFileNumber, + DbCopyInfo.szSrcFileName))) + { + goto Exit; + } + + if (RC_BAD( rc = rflGetFileName( pszDestDbName, + pszDestRflDir, uiFileNumber, + DbCopyInfo.szDestFileName))) + { + goto Exit; + } + + DbCopyInfo.bNewSrcFile = TRUE; + if (RC_BAD( rc = flmCopyFile( gv_pFileSystem, + &DbCopyInfo, &pCopiedList, TRUE, + ifpStatus))) + { + goto Exit; + } + } + } + + pDirHdl->Release(); + pDirHdl = NULL; + +Exit: + + if (bUsedDatabase) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + pDatabase->decrOpenCount(); + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Unlock the database, if it is locked. + + if (bWriteLocked) + { + pDatabase->dbWriteUnlock(); + bWriteLocked = FALSE; + } + + if (bDatabaseLocked) + { + RCODE rc3; + + if (RC_BAD( rc3 = pDatabaseLockObj->Unlock( TRUE, NULL))) + { + if (RC_OK( rc)) + rc = rc3; + } + bDatabaseLocked = FALSE; + } + + if (pWriteLockObj) + { + pWriteLockObj->Release( FALSE); + pWriteLockObj = NULL; + } + + if (pDatabaseLockObj) + { + pDatabaseLockObj->Release( FALSE); + pDatabaseLockObj = NULL; + } + + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + + if (pTmpFileHdl) + { + pTmpFileHdl->Release(); + } + + if (pDirHdl) + { + pDirHdl->Release(); + } + + // Free all the names of files that were copied. + // If the copy didn't finish, try to delete any files + // that were copied. + + while (pCopiedList) + { + COPIED_NAME_p pNext = pCopiedList->pNext; + + // If the overall copy failed, delete the copied file. + + if (RC_BAD( rc)) + { + (void)gv_pFileSystem->Delete( pCopiedList->szPath); + } + + f_free( &pCopiedList); + pCopiedList = pNext; + } + + if (RC_BAD( rc) && bCreatedDestRflDir) + { + (void)gv_pFileSystem->RemoveDir( pszActualDestRflPath); + } + + if (pszActualSrcRflPath) + { + f_free( &pszActualSrcRflPath); + } + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + return( rc); +} + +/**************************************************************************** +Desc: Copy a file that is one of the files of the database. +*****************************************************************************/ +FSTATIC RCODE flmCopyFile( + F_FileSystem * pFileSystem, + DB_COPY_INFO * pDbCopyInfo, + COPIED_NAME ** ppCopiedListRV, + FLMBOOL bOkToTruncate, + IF_DbCopyStatus * ifpStatus) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = NULL; + IF_FileHdl * pSrcFileHdl = NULL; + IF_FileHdl * pDestFileHdl = NULL; + FLMUINT uiBufferSize = 32768; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + FLMUINT uiBytesWritten; + FLMUINT uiOffset; + FLMBOOL bCreatedDestFile = FALSE; + + // Open the source file. + + if( RC_BAD( rc = pFileSystem->Open( pDbCopyInfo->szSrcFileName, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, + &pSrcFileHdl))) + { + goto Exit; + } + + // Get a file handle for the destination file. + // First attempt to open the destination file. If it does + // not exist, attempt to create it. + + if (RC_BAD( rc = pFileSystem->Open( pDbCopyInfo->szDestFileName, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, + &pDestFileHdl))) + { + if (rc != NE_XFLM_IO_PATH_NOT_FOUND && + rc != NE_XFLM_IO_INVALID_FILENAME) + { + goto Exit; + } + + if( RC_BAD( rc = pFileSystem->Create( pDbCopyInfo->szDestFileName, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYNONE | + XFLM_IO_CREATE_DIR | XFLM_IO_DIRECT, + &pDestFileHdl))) + { + goto Exit; + } + bCreatedDestFile = TRUE; + } + + // Allocate a buffer for reading and writing. + + if (RC_BAD( rc = f_alloc( uiBufferSize, &pucBuffer))) + { + goto Exit; + } + + // Read from source file until we hit EOF in the file or + // we hit the end offset. + + uiOffset = 0; + for (;;) + { + uiBytesToRead = (FLMUINT)((0xFFFFFFFF - uiOffset >= + uiBufferSize) + ? uiBufferSize + : (FLMUINT)(0xFFFFFFFF - uiOffset)); + + // Read data from source file. + + if (RC_BAD( rc = pSrcFileHdl->SectorRead( uiOffset, uiBytesToRead, + pucBuffer, &uiBytesRead))) + { + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + if (!uiBytesRead) + { + break; + } + } + else + { + goto Exit; + } + } + + // Write data to destination file. + + if (RC_BAD( rc = pDestFileHdl->Write( uiOffset, + uiBytesRead, pucBuffer, &uiBytesWritten))) + { + goto Exit; + } + + // Do callback to report progress. + + if (ifpStatus) + { + pDbCopyInfo->ui64BytesCopied += (FLMUINT64)uiBytesWritten; + if (RC_BAD( rc = ifpStatus->dbCopyStatus( + pDbCopyInfo->ui64BytesToCopy, + pDbCopyInfo->ui64BytesCopied, + pDbCopyInfo->bNewSrcFile, + pDbCopyInfo->szSrcFileName, + pDbCopyInfo->szDestFileName))) + { + goto Exit; + } + pDbCopyInfo->bNewSrcFile = FALSE; + } + + if (0xFFFFFFFF - uiBytesWritten < uiOffset) + { + uiOffset = 0xFFFFFFFF; + break; + } + + uiOffset += uiBytesWritten; + + // Quit once we reach the end offset or we read fewer bytes + // than we asked for. + + if (uiBytesRead < uiBytesToRead) + { + break; + } + } + + // If we overwrote the destination file, as opposed to creating + // it, truncate it in case it was larger than the number of + // bytes we actually copied. + + if (!bCreatedDestFile && bOkToTruncate) + { + if (RC_BAD( rc = pDestFileHdl->Truncate( uiOffset))) + { + goto Exit; + } + } + + // If the copy succeeded, add the destination name to a list + // of destination files. This is done so we can clean up + // copied files if we fail somewhere in the overall database + // copy. + + if (ppCopiedListRV) + { + COPIED_NAME_p pCopyName; + + if( RC_BAD( rc = f_alloc( (FLMUINT)sizeof( COPIED_NAME), &pCopyName))) + { + goto Exit; + } + f_strcpy( pCopyName->szPath, pDbCopyInfo->szDestFileName); + pCopyName->pNext = *ppCopiedListRV; + *ppCopiedListRV = pCopyName; + } + +Exit: + + if (pucBuffer) + { + f_free( &pucBuffer); + } + + if (pSrcFileHdl) + { + pSrcFileHdl->Release(); + } + + if (pDestFileHdl) + { + pDestFileHdl->Flush(); + pDestFileHdl->Release(); + } + + // Attempt to delete the destination file if + // we didn't successfully copy it. + + if (RC_BAD( rc)) + { + (void)pFileSystem->Delete( pDbCopyInfo->szDestFileName); + } + + return( rc); +} diff --git a/version5/src/fdbremov.cpp b/version5/src/fdbremov.cpp new file mode 100644 index 0000000..3fbdaae --- /dev/null +++ b/version5/src/fdbremov.cpp @@ -0,0 +1,321 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_DbSystem::dbRemove method. +// +// 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: fdbremov.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Deletes all files of a database +****************************************************************************/ +RCODE F_DbSystem::dbRemove( + const char * pszDbName, + // [IN] Name of source database to be deleted. + const char * pszDataDir, + // [IN] Directory where data files are located. + const char * pszRflDir, + // [IN] RFL directory of database. NULL can be + // passed to indicate that the log files are located + // in the same directory as the other database files. + FLMBOOL bRemoveRflFiles) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNumber; + char * pszTmpName = NULL; + char * pszRflDirName; + char * pszDataName; + char * pszBaseName; + char * pszExt; + char * pszDataExt; + IF_DirHdl * pDirHdl = NULL; + + // Cannot handle empty database name. + + if( !pszDbName || !(*pszDbName)) + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + + // Allocate memory, so as to not consume stack. + + if( RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 3 + F_FILENAME_SIZE, + &pszTmpName))) + { + goto Exit; + } + + pszRflDirName = pszTmpName + F_PATH_MAX_SIZE; + pszDataName = pszRflDirName + F_PATH_MAX_SIZE; + pszBaseName = pszDataName + F_PATH_MAX_SIZE; + + // First make sure we have closed this database and gotten rid of + // it from our internal memory tables - in case it had been open. + + if (RC_BAD( rc = checkDatabaseClosed( pszDbName, pszDataDir))) + { + goto Exit; + } + + if (pszDataDir && *pszDataDir) + { + if (RC_BAD( rc = gv_pFileSystem->pathReduce( pszDbName, pszDataName, pszBaseName))) + { + goto Exit; + } + f_strcpy( pszDataName, pszDataDir); + if (RC_BAD( rc = gv_pFileSystem->pathAppend( pszDataName, pszBaseName))) + { + goto Exit; + } + } + else + { + f_strcpy( pszDataName, pszDbName); + } + f_strcpy( pszTmpName, pszDbName); + + // Start deleting files, beginning with the main DB file. + + if (RC_BAD( rc = gv_pFileSystem->Delete( pszDbName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // Find where the extension of the database name is + + pszExt = pszTmpName + f_strlen( pszTmpName) - 1; + pszDataExt = pszDataName + f_strlen( pszDataName) - 1; + while (pszExt != pszTmpName && *pszExt != '.') + { + pszExt--; + + // Both the db name and data name have the same + // base name, so we can decrement pszDataExt + // at the same time we decrement pszExt. + + pszDataExt--; + } + if (*pszExt != '.') + { + pszExt = pszTmpName + f_strlen( pszTmpName); + pszDataExt = pszDataName + f_strlen( pszDataName); + } + + // Delete the .lck file, if any + + f_strcpy( pszExt, ".lck"); + if (RC_BAD( rc = gv_pFileSystem->Delete( pszTmpName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // Delete block (data) files. + + uiFileNumber = 1; + for (;;) + { + bldSuperFileExtension( uiFileNumber, pszDataExt); + + if (RC_BAD( rc = gv_pFileSystem->Delete( pszDataName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) + { + break; + } + uiFileNumber++; + } + + // Delete rollback log files. + + uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + for (;;) + { + bldSuperFileExtension( uiFileNumber, pszExt); + + if (RC_BAD( rc = gv_pFileSystem->Delete( pszTmpName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) + { + break; + } + uiFileNumber++; + } + + if (bRemoveRflFiles) + { + + // Delete roll-forward log files. + + FLMBOOL bCanDeleteDir; + + // Scan the RFL directory for RFL files. + + if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszRflDirName))) + { + goto Exit; + } + + // See if the directory exists. If not, we are done. + + if (!gv_pFileSystem->IsDir( pszRflDirName)) + { + goto Exit; // Should return NE_XFLM_OK + } + + // Open the directory and scan for RFL files. + // NOTE: DO NOT just call RemoveDir. There may be other + // things in the directory that we do not want to delete. + // Look specifically for files that match our expected + // name format for RFL files. + + if (RC_BAD( rc = gv_pFileSystem->OpenDir( pszRflDirName, + "*", &pDirHdl))) + { + goto Exit; + } + + // Assume that we can delete the directory. This will only + // be set to FALSE if we can't delete all of the files in + // the directory - i.e., some don't look like RFL log files. + + bCanDeleteDir = TRUE; + for (;;) + { + if (RC_BAD( rc = pDirHdl->Next())) + { + if (rc == NE_XFLM_IO_NO_MORE_FILES) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + pDirHdl->CurrentItemPath( pszTmpName); + if (pDirHdl->CurrentItemIsDir()) + { + bCanDeleteDir = FALSE; + } + else if (!rflGetFileNum( pszTmpName, &uiFileNumber)) + { + bCanDeleteDir = FALSE; + } + else + { + if( RC_BAD( rc = + gv_pFileSystem->Delete( pszTmpName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + } + + // Attempt to delete the directory - if allowed. + + if (bCanDeleteDir) + { + + // Need to release the directory handle so the + // directory will be closed when we try to delete it + // below. + + if (pDirHdl) + { + pDirHdl->Release(); + pDirHdl = NULL; + } + if ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pDirHdl->RemoveDir( pszRflDirName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + } + +Exit: + + if( pszTmpName) + { + f_free( &pszTmpName); + } + + if( pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} diff --git a/version5/src/fdbrenam.cpp b/version5/src/fdbrenam.cpp new file mode 100644 index 0000000..9d409e7 --- /dev/null +++ b/version5/src/fdbrenam.cpp @@ -0,0 +1,440 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_DbSystem::dbRename method. +// +// 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: fdbrenam.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +typedef struct +{ + char szSrcFileName [F_PATH_MAX_SIZE]; + char szDstFileName [F_PATH_MAX_SIZE]; +} DB_RENAME_INFO, * DB_RENAME_INFO_p; + +typedef struct DBRenameInfoTag +{ + DB_RENAME_INFO Info; + DBRenameInfoTag * pNext; +} DBRenameInfo; + +FSTATIC RCODE flmRenameFile( + const char * pszSrcFileName, + const char * pszDstFileName, + FLMBOOL bOverwriteDestOk, + FLMBOOL bPathNotFoundOk, + DBRenameInfo ** ppRenameList, + FLMBOOL * pbFileFound, + IF_DbRenameStatus * ifpStatus); + +/**************************************************************************** +Desc: Renames all files of a database +****************************************************************************/ +RCODE F_DbSystem::dbRename( + const char * pszDbName, + // [IN] Database to be renamed. + const char * pszDataDir, + // [IN] Directory for data files. + const char * pszRflDir, + // [IN] RFL directory of database. NULL can be + // passed to indicate that the log files are located + // in the same directory as the other database files. + const char * pszNewDbName, + // [IN] New name to be given to the database. May be + // the short name only, or include a directory. If it + // includes a directory, it must be the same directory + // as the directory given in pszDbName. + FLMBOOL bOverwriteDestOk, + // [IN] Ok to overwrite existing file with rename? + IF_DbRenameStatus * ifpStatus) + // [IN] Status callback function. +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNumber; + DBRenameInfo * pRenameList = NULL; + FLMBOOL bFileFound; + char * pszOldName = NULL; + char * pszNewName; + char * pszOldDataName; + char * pszNewDataName; + char * pszFullNewName; + char szOldBase [F_FILENAME_SIZE]; + char szNewBase [F_FILENAME_SIZE]; + char * pszExtOld; + char * pszExtNew; + char * pszDataExtOld; + char * pszDataExtNew; + + // Cannot handle empty database name. + + flmAssert( pszDbName && *pszDbName); + flmAssert( pszNewDbName && *pszNewDbName); + + // Allocate memory for a read buffer, the log header, and various + // file names. + + if (RC_BAD( rc = f_alloc( F_PATH_MAX_SIZE * 5, &pszOldName))) + { + goto Exit; + } + pszNewName = pszOldName + F_PATH_MAX_SIZE; + pszOldDataName = pszNewName + F_PATH_MAX_SIZE; + pszNewDataName = pszOldDataName + F_PATH_MAX_SIZE; + pszFullNewName = pszNewDataName + F_PATH_MAX_SIZE; + + // There must be either no directory specified for the new name, or + // it must be identical to the old directory. + + if (RC_BAD( rc = gv_pFileSystem->pathReduce( pszDbName, pszOldName, szOldBase))) + { + goto Exit; + } + if (RC_BAD( rc = gv_pFileSystem->pathReduce( pszNewDbName, pszNewName, szNewBase))) + { + goto Exit; + } + + // Directories must be the same. + + if (*pszNewName && f_stricmp( pszOldName, pszNewName) != 0) + { + rc = RC_SET( NE_XFLM_INVALID_PARM); + goto Exit; + } + f_strcpy( pszNewName, pszOldName); + if (RC_BAD( rc = gv_pFileSystem->pathAppend( pszNewName, szNewBase))) + { + goto Exit; + } + + f_strcpy( pszFullNewName, pszNewName); + f_strcpy( pszOldName, pszDbName); + + if (pszDataDir && *pszDataDir) + { + f_strcpy( pszOldDataName, pszDataDir); + f_strcpy( pszNewDataName, pszDataDir); + if (RC_BAD( rc = gv_pFileSystem->pathAppend( pszOldDataName, szOldBase))) + { + goto Exit; + } + if (RC_BAD( rc = gv_pFileSystem->pathAppend( pszNewDataName, szNewBase))) + { + goto Exit; + } + } + else + { + f_strcpy( pszNewDataName, pszNewName); + f_strcpy( pszOldDataName, pszOldName); + } + + // First make sure we have closed the databases and gotten rid of + // them from our internal memory tables - in case they had been open. + + if (RC_BAD( rc = checkDatabaseClosed( pszDbName, pszDataDir))) + { + goto Exit; + } + if (RC_BAD( rc = checkDatabaseClosed( pszFullNewName, pszDataDir))) + { + goto Exit; + } + + // Start renaming files, beginning with the main DB file. + + if (RC_BAD( rc = flmRenameFile( pszDbName, pszFullNewName, + bOverwriteDestOk, FALSE, + &pRenameList, &bFileFound, + ifpStatus))) + { + goto Exit; + } + + // Find where the extension of the old and new database names are + + pszExtOld = pszOldName + f_strlen( pszOldName) - 1; + pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName) - 1; + while (pszExtOld != pszOldName && *pszExtOld != '.') + { + pszExtOld--; + + // Both the old db name and old data name have the same + // base name, so we can decrement pszDataExtOld + // at the same time we decrement pszExtOld. + + pszDataExtOld--; + } + if (*pszExtOld != '.') + { + pszExtOld = pszOldName + f_strlen( pszOldName); + pszDataExtOld = pszOldDataName + f_strlen( pszOldDataName); + } + + pszExtNew = pszNewName + f_strlen( pszNewName) - 1; + pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName) - 1; + while (pszExtNew != pszOldName && *pszExtNew != '.') + { + pszExtNew--; + + // Both the new db name and new data name have the same + // base name, so we can decrement pszDataExtNew + // at the same time we decrement pszExtNew. + + pszDataExtNew--; + } + if (*pszExtNew != '.') + { + pszExtNew = pszNewName + f_strlen( pszNewName); + pszDataExtNew = pszNewDataName + f_strlen( pszNewDataName); + } + + // Rename the .lck file, if any. This is necessary for UNIX. + + f_strcpy( pszExtOld, ".lck"); + f_strcpy( pszExtNew, ".lck"); + if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, + bOverwriteDestOk, TRUE, + &pRenameList, &bFileFound, + ifpStatus))) + { + goto Exit; + } + + // Rename block (data) files. + + uiFileNumber = 1; + for (;;) + { + bldSuperFileExtension( uiFileNumber, pszDataExtOld); + bldSuperFileExtension( uiFileNumber, pszDataExtNew); + + if (RC_BAD( rc = flmRenameFile( pszOldDataName, pszNewDataName, + bOverwriteDestOk, TRUE, + &pRenameList, &bFileFound, + ifpStatus))) + { + goto Exit; + } + if (!bFileFound) + { + break; + } + if (uiFileNumber == MAX_DATA_BLOCK_FILE_NUMBER) + { + break; + } + uiFileNumber++; + } + + // Rename rollback log files. + + uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + for (;;) + { + bldSuperFileExtension( uiFileNumber, pszExtOld); + bldSuperFileExtension( uiFileNumber, pszExtNew); + + if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, + bOverwriteDestOk, TRUE, + &pRenameList, &bFileFound, + ifpStatus))) + { + goto Exit; + } + if (!bFileFound) + { + break; + } + if (uiFileNumber == MAX_LOG_BLOCK_FILE_NUMBER) + { + break; + } + uiFileNumber++; + } + + // Rename the RFL directory. + + if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, pszOldName))) + { + goto Exit; + } + + if (RC_BAD( rc = rflGetDirAndPrefix( pszFullNewName, pszRflDir, + pszNewName))) + { + goto Exit; + } + + if (RC_BAD( rc = flmRenameFile( pszOldName, pszNewName, + bOverwriteDestOk, TRUE, + &pRenameList, &bFileFound, + ifpStatus))) + { + goto Exit; + } + +Exit: + if (pszOldName) + { + f_free( &pszOldName); + } + + // Free the list of renamed files. + + while (pRenameList) + { + DBRenameInfo * pRenameFile; + + pRenameFile = pRenameList; + pRenameList = pRenameList->pNext; + + // If we had an error of some sort, attempt to un-rename + // the file that had been renamed. + + if (RC_BAD( rc)) + { + gv_pFileSystem->Rename( pRenameFile->Info.szDstFileName, + pRenameFile->Info.szSrcFileName); + } + f_free( &pRenameFile); + } + return( rc); +} + +/**************************************************************************** +Desc: Rename a database file and add to list of renamed files. +****************************************************************************/ +FSTATIC RCODE flmRenameFile( + const char * pszSrcFileName, + const char * pszDstFileName, + FLMBOOL bOverwriteDestOk, + FLMBOOL bPathNotFoundOk, + DBRenameInfo ** ppRenameList, + FLMBOOL * pbFileFound, + IF_DbRenameStatus * ifpStatus) +{ + RCODE rc = NE_XFLM_OK; + DBRenameInfo * pRenameFile = NULL; + + *pbFileFound = FALSE; + + // Should not do anything if the source and destination names + // are the same. + + if (f_stricmp( pszSrcFileName, pszDstFileName) == 0) + { + if (gv_pFileSystem->Exists( pszSrcFileName) == NE_XFLM_OK) + { + *pbFileFound = TRUE; + } + goto Exit; + } + + if (RC_BAD( rc = f_alloc( sizeof( DBRenameInfo), &pRenameFile))) + { + goto Exit; + } + + // If a destination file exists, and it is OK to overwrite + // it, it must be deleted. + + if (bOverwriteDestOk) + { + if (gv_pFileSystem->IsDir( pszDstFileName)) + { + if (RC_BAD( rc = gv_pFileSystem->RemoveDir( + pszDstFileName, TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = gv_pFileSystem->Delete( + pszDstFileName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + } + + // If names are the same, no need to actually do the + // rename. + + if (RC_BAD( rc = gv_pFileSystem->Rename( pszSrcFileName, + pszDstFileName))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + if (bPathNotFoundOk) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + *pbFileFound = TRUE; + pRenameFile->pNext = *ppRenameList; + *ppRenameList = pRenameFile; + + // Do user callback. User could choose to stop the rename + // from continuing. + if (ifpStatus) + { + f_strcpy( pRenameFile->Info.szSrcFileName, pszSrcFileName); + f_strcpy( pRenameFile->Info.szDstFileName, pszDstFileName); + if (RC_BAD( rc = ifpStatus->dbRenameStatus( + pRenameFile->Info.szSrcFileName, + pRenameFile->Info.szDstFileName))) + { + goto Exit; + } + } + + // So it won't get deallocated at exit. + + pRenameFile = NULL; + } +Exit: + if (pRenameFile) + { + f_free( &pRenameFile); + } + return( rc); +} diff --git a/version5/src/fdict.cpp b/version5/src/fdict.cpp new file mode 100644 index 0000000..558c851 --- /dev/null +++ b/version5/src/fdict.cpp @@ -0,0 +1,9221 @@ +//------------------------------------------------------------------------------ +// Desc: Routines to access anything in the dictionary +// +// Tabs: 3 +// +// Copyright (c) 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: fdict.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +// Data type strings + +char * fdictDataTypes[ XFLM_NUM_OF_TYPES + 1] = +{ + XFLM_NODATA_OPTION_STR, + XFLM_STRING_OPTION_STR, + XFLM_INTEGER_OPTION_STR, + XFLM_BINARY_OPTION_STR, + NULL +}; + +extern RESERVED_TAG_NAME FlmReservedElementTags[]; +extern RESERVED_TAG_NAME FlmReservedAttributeTags[]; + +FSTATIC void fdictInsertInIcdChain( + ICD ** ppFirstIcd, + ICD * pIcd); + +FSTATIC void fdictRemoveFromIcdChain( + ICD ** ppFirstIcd, + ICD * pIcd); + +FSTATIC RCODE fdictCopyCollection( + F_Pool * pDictPool, + F_COLLECTION ** ppDestCollection, + F_COLLECTION * pSrcCollection); + +FSTATIC RCODE fdictCopyPrefix( + F_Pool * pDictPool, + F_PREFIX ** ppDestPrefix, + F_PREFIX * pSrcPrefix); + +FSTATIC RCODE fdictCopyEncDef( + F_Pool * pDictPool, + F_ENCDEF ** ppDestEncDef, + F_ENCDEF * pSrcEncDef); + +FSTATIC char * fdictGetOption( + char ** ppszSrc); + +FSTATIC RCODE isIndexComponent( + F_Db * pDb, + F_DOMNode * pNode, + FLMBOOL * pbIsIndexComponent, + FLMUINT * puiElementId); + +FSTATIC FLMBOOL indexDefsSame( + IXD * pOldIxd, + IXD * pNewIxd); + + +#define MAX_ENC_TYPES 2 +// NOTE: If you change the arrangement of the values in this array, make sure +// you search the entire codebase for references to DDEncOpts and fdictLegalEncDefTypes +// and verify that the changes won't cause problems. +char * DDEncOpts[MAX_ENC_TYPES] = { + XFLM_ENC_AES_OPTION_STR, /* AES */ + XFLM_ENC_DES3_OPTION_STR /* Triple DES */ + }; + +/*************************************************************************** +Desc: Constructor +***************************************************************************/ +F_Dict::F_Dict() +{ + m_pNext = NULL; + m_pPrev = NULL; + m_pDatabase = NULL; + m_uiDictSeq = 0; + m_dictPool.poolInit( 1024); + + m_pElementDefTbl = NULL; + m_uiLowestElementNum = 0; + m_uiHighestElementNum = 0; + + m_pReservedElementDefTbl = NULL; + + m_pExtElementDefTbl = NULL; + m_uiExtElementDefTblSize = 0; + m_hExtElementDefMutex = F_MUTEX_NULL; + + m_pIxElementTbl = NULL; + m_uiIxElementTblSize = 0; + m_uiNumIxElements = 0; + + m_pAttributeDefTbl = NULL; + m_uiLowestAttributeNum = 0; + m_uiHighestAttributeNum = 0; + + m_pReservedAttributeDefTbl = NULL; + + m_pExtAttributeDefTbl = NULL; + m_uiExtAttributeDefTblSize = 0; + m_hExtAttributeDefMutex = F_MUTEX_NULL; + + m_pIxAttributeTbl = NULL; + m_uiIxAttributeTblSize = 0; + m_uiNumIxAttributes = 0; + + m_pDictCollection = NULL; + m_pDataCollection = NULL; + m_pMaintCollection = NULL; + + m_ppCollectionTbl = NULL; + m_uiLowestCollectionNum = 0; + m_uiHighestCollectionNum = 0; + + m_ppPrefixTbl = NULL; + m_uiLowestPrefixNum = 0; + m_uiHighestPrefixNum = 0; + + m_ppEncDefTbl = NULL; + m_uiLowestEncDefNum = 0; + m_uiHighestEncDefNum = 0; + + m_pNameIndex = NULL; + m_pNumberIndex = NULL; + + m_ppIxdTbl = NULL; + m_uiLowestIxNum = 0; + m_uiHighestIxNum = 0; + + m_pNameTable = NULL; + m_pRootIcdList = NULL; + + // Whenever an F_Dict is allocated, it is always immediately + // used by an F_Db. + + m_uiUseCount = 1; +} + +/*************************************************************************** +Desc: Destructor +***************************************************************************/ +F_Dict::~F_Dict() +{ + resetDict(); +} + +/*************************************************************************** +Desc: Clear the dictionary object so it can be reused. NOTE: This function + also needs to do all the freeing up that the destructor would do + because it is called by the destructor. +***************************************************************************/ +void F_Dict::resetDict( void) +{ + FLMUINT uiLoop; + + f_free( &m_pElementDefTbl); + m_uiLowestElementNum = 0; + m_uiHighestElementNum = 0; + + f_free( &m_pExtElementDefTbl); + m_uiExtElementDefTblSize = 0; + if (m_hExtElementDefMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hExtElementDefMutex); + } + + f_free( &m_pReservedElementDefTbl); + + f_free( &m_pIxElementTbl); + m_uiIxElementTblSize = 0; + m_uiNumIxElements = 0; + + f_free( &m_pAttributeDefTbl); + m_uiLowestAttributeNum = 0; + m_uiHighestAttributeNum = 0; + + f_free( &m_pExtAttributeDefTbl); + m_uiExtAttributeDefTblSize = 0; + if (m_hExtAttributeDefMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hExtAttributeDefMutex); + } + + f_free( &m_pReservedAttributeDefTbl); + + f_free( &m_pIxAttributeTbl); + m_uiIxAttributeTblSize = 0; + m_uiNumIxAttributes = 0; + + f_free( &m_ppIxdTbl); + m_uiLowestIxNum = 0; + m_uiHighestIxNum = 0; + + m_pNameIndex = NULL; + m_pNumberIndex = NULL; + + f_free( &m_ppCollectionTbl); + m_uiLowestCollectionNum = 0; + m_uiHighestCollectionNum = 0; + + f_free( &m_ppPrefixTbl); + m_uiLowestPrefixNum = 0; + m_uiHighestPrefixNum = 0; + + for ( uiLoop = 0; + uiLoop <= (m_uiHighestEncDefNum - m_uiLowestEncDefNum); + uiLoop++ ) + { + if (m_ppEncDefTbl && + m_ppEncDefTbl[ uiLoop] && + (*m_ppEncDefTbl[ uiLoop]).pCcs) + { + (*m_ppEncDefTbl[ uiLoop]).pCcs->Release(); + } + } + f_free( &m_ppEncDefTbl); + m_uiLowestEncDefNum = 0; + m_uiHighestEncDefNum = 0; + + m_pDictCollection = NULL; + m_pDataCollection = NULL; + m_pMaintCollection = NULL; + + m_dictPool.poolFree(); + m_dictPool.poolInit( 1024); + + if (m_pNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + m_pRootIcdList = NULL; +} + +/*************************************************************************** +Desc: +***************************************************************************/ +RCODE F_Dict::allocNameTable( void) +{ + if ((m_pNameTable = f_new F_NameTable) == NULL) + { + return( RC_SET( NE_XFLM_MEM)); + } + else + { + return( m_pNameTable->setupNameTable()); + } +} + +/*************************************************************************** +Desc: Get the first ICD, if any for an element. NOTE: This is only called + for element numbers greater or equal to FLM_LOW_EXT_ELEMENT_NUM, so + we have to look in the m_pIxElementTbl table. +***************************************************************************/ +IX_ITEM * F_Dict::findIxItem( + IX_ITEM * pIxTbl, + FLMUINT uiNumItems, + FLMUINT uiDictNum, + FLMUINT * puiInsertPos + ) +{ + IX_ITEM * pIxItem = NULL; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblDictNum; + + // Do binary search in the table + + if (!uiNumItems) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + + uiHigh = --uiNumItems; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + + uiTblDictNum = pIxTbl [uiMid].uiDictNum; + if (uiTblDictNum == uiDictNum) + { + + // Found Match + + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + pIxItem = &pIxTbl [uiMid]; + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + if (puiInsertPos) + { + *puiInsertPos = (uiDictNum < uiTblDictNum) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (uiDictNum < uiTblDictNum) + { + if (uiMid == 0) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiNumItems) + { + if (puiInsertPos) + { + *puiInsertPos = uiMid + 1; + } + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( pIxItem); +} + +/*************************************************************************** +Desc: Get information for an extended element - which are elements whose tag + number is greater than or equal to FLM_LOW_EXT_ELEMENT_NUM +Note: Populates data type, first Icd, and state +***************************************************************************/ +RCODE F_Dict::getExtElement( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiElementNum, + F_AttrElmInfo * pElmInfo) +{ + RCODE rc = NE_XFLM_OK; + EXT_ATTR_ELM_DEF * pExtElementDef; + IX_ITEM * pIxItem; + FLMBOOL bMutexLocked; + + // See if the element is in our extended list. If not, bring it in. + + f_mutexLock( m_hExtElementDefMutex); + bMutexLocked = TRUE; + pExtElementDef = getExtElementDef( uiElementNum); + if (pExtElementDef->uiDictNum != uiElementNum) + { + f_mutexUnlock( m_hExtElementDefMutex); + bMutexLocked = FALSE; + + if (!ui64DocumentID) + { + F_DataVector searchKey; + F_DataVector foundKey; + + // Find and read the element definition document + // Need to lookup the document's ID, using the element number + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ELEMENT_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUINT( 1, uiElementNum))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + ui64DocumentID = foundKey.getDocumentID(); + } + + // Must read the element definition document + + if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ELEMENT_TAG, + ui64DocumentID, pElmInfo, TRUE, FALSE))) + { + goto Exit; + } + + // See if the element is indexed. + + pIxItem = findIxElement( uiElementNum); + + // Relock the mutex and populate the extended structure + // with the information we found. + + f_mutexLock( m_hExtElementDefMutex); + bMutexLocked = TRUE; + pExtElementDef->uiDictNum = uiElementNum; + pExtElementDef->attrElmDef.uiFlags = + (pElmInfo->m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (pElmInfo->m_uiState & ATTR_ELM_STATE_MASK); + pExtElementDef->attrElmDef.pFirstIcd = pIxItem + ? pIxItem->pFirstIcd + : NULL; + } + + pElmInfo->m_uiDataType = attrElmGetType( &pExtElementDef->attrElmDef); + pElmInfo->m_pFirstIcd = pExtElementDef->attrElmDef.pFirstIcd; + pElmInfo->m_uiState = attrElmGetState( &pExtElementDef->attrElmDef); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( m_hExtElementDefMutex); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get information for an extended attribute - which are attributes whose + tag number is greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM +***************************************************************************/ +RCODE F_Dict::getExtAttribute( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiAttributeNum, + F_AttrElmInfo * pAttrInfo) +{ + RCODE rc = NE_XFLM_OK; + EXT_ATTR_ELM_DEF * pExtAttributeDef; + IX_ITEM * pIxItem; + FLMBOOL bMutexLocked; + + // See if the attribute is in our extended list. If not, bring it in. + + f_mutexLock( m_hExtAttributeDefMutex); + bMutexLocked = TRUE; + pExtAttributeDef = getExtAttributeDef( uiAttributeNum); + if (pExtAttributeDef->uiDictNum != uiAttributeNum) + { + f_mutexUnlock( m_hExtAttributeDefMutex); + bMutexLocked = FALSE; + + if (!ui64DocumentID) + { + F_DataVector searchKey; + F_DataVector foundKey; + + // Find and read the element definition document + + // Need to lookup the document's ID, using the element number + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ATTRIBUTE_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUINT( 1, uiAttributeNum))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + ui64DocumentID = foundKey.getDocumentID(); + } + + // Must read the attribute definition document + + if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ATTRIBUTE_TAG, + ui64DocumentID, pAttrInfo, TRUE, FALSE))) + { + goto Exit; + } + + // See if the attribute is indexed. + + pIxItem = findIxAttribute( uiAttributeNum); + + // Relock the mutex and populate the extended structure + // with the information we found. + + f_mutexLock( m_hExtAttributeDefMutex); + bMutexLocked = TRUE; + pExtAttributeDef->uiDictNum = uiAttributeNum; + pExtAttributeDef->attrElmDef.uiFlags = + (pAttrInfo->m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (pAttrInfo->m_uiState & ATTR_ELM_STATE_MASK); + pExtAttributeDef->attrElmDef.pFirstIcd = pIxItem + ? pIxItem->pFirstIcd + : NULL; + } + + pAttrInfo->m_uiDataType = attrElmGetType( &pExtAttributeDef->attrElmDef); + pAttrInfo->m_pFirstIcd = pExtAttributeDef->attrElmDef.pFirstIcd; + pAttrInfo->m_uiState = attrElmGetState( &pExtAttributeDef->attrElmDef); + pAttrInfo->m_uiFlags = attrElmGetFlags( &pExtAttributeDef->attrElmDef); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( m_hExtAttributeDefMutex); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the element information. +Note: Populates data type, first ICD, and state +***************************************************************************/ +RCODE F_Dict::getElement( + F_Db * pDb, + FLMUINT uiElementNum, // [in] Element Number to look up + F_AttrElmInfo * pElmInfo) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pElementDef; + + if (elementIsReservedTag( uiElementNum)) + { + if ((pElementDef = getReservedElementDef( uiElementNum)) != NULL) + { + goto Get_Type_And_State; + } + else + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + } + else if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + if ((pElementDef = getElementDef( uiElementNum)) != NULL) + { +Get_Type_And_State: + + pElmInfo->m_uiDataType = attrElmGetType( pElementDef); + pElmInfo->m_pFirstIcd = pElementDef->pFirstIcd; + pElmInfo->m_uiState = attrElmGetState( pElementDef); + pElmInfo->m_uiFlags = attrElmGetFlags( pElementDef); + } + else + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + } + else if (uiElementNum >= FLM_LOW_EXT_ELEMENT_NUM && m_pExtElementDefTbl) + { + if (RC_BAD( rc = getExtElement( pDb, 0, uiElementNum, pElmInfo))) + { + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the attribute information. +Note: Populates data type, first ICD, and state +***************************************************************************/ +RCODE F_Dict::getAttribute( + F_Db * pDb, + FLMUINT uiAttributeNum, + F_AttrElmInfo * pAttrInfo) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pAttributeDef; + + if( attributeIsReservedTag( uiAttributeNum)) + { + if ((pAttributeDef = getReservedAttributeDef( uiAttributeNum)) != NULL) + { + goto Get_Type_And_State; + } + else + { + rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); + goto Exit; + } + } + else if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) + { +Get_Type_And_State: + pAttrInfo->m_uiDataType = attrElmGetType( pAttributeDef); + pAttrInfo->m_pFirstIcd = pAttributeDef->pFirstIcd; + pAttrInfo->m_uiState = attrElmGetState( pAttributeDef); + pAttrInfo->m_uiFlags = attrElmGetFlags( pAttributeDef); + } + else + { + rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); + goto Exit; + } + } + else if (uiAttributeNum >= FLM_LOW_EXT_ATTRIBUTE_NUM && m_pExtAttributeDefTbl) + { + if (RC_BAD( rc = getExtAttribute( pDb, 0, uiAttributeNum, pAttrInfo))) + { + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the data type for any given element or attribute in the + current db +***************************************************************************/ +RCODE F_Db::getDataType( + FLMUINT uiDictType, + FLMUINT uiNameId, + FLMUINT * puiDataType) +{ + RCODE rc = NE_XFLM_OK; + F_AttrElmInfo defInfo; + + flmAssert( uiDictType == ELM_ELEMENT_TAG || + uiDictType == ELM_ATTRIBUTE_TAG); + + if( RC_BAD( rc = + (uiDictType == ELM_ELEMENT_TAG + ? m_pDict->getElement( this, uiNameId, &defInfo) + : m_pDict->getAttribute( this, uiNameId, &defInfo)))) + { + goto Exit; + } + + *puiDataType = defInfo.m_uiDataType; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next element defined in the dictionary after the one that + is passed in. +Note: Populates data type, first ICD, and state +***************************************************************************/ +RCODE F_Dict::getNextElement( + F_Db * pDb, + FLMUINT * puiElementNum, + F_AttrElmInfo * pElmInfo) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pElementDef = NULL; + FLMUINT uiElementNum = *puiElementNum; + + if (uiElementNum < m_uiLowestElementNum) + { + uiElementNum = m_uiLowestElementNum; + } + else + { + uiElementNum++; + } + + while (uiElementNum >= m_uiLowestElementNum && + uiElementNum <= m_uiHighestElementNum) + { + pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; + if (attrElmGetState( pElementDef)) + { + break; + } + else + { + pElementDef = NULL; + } + uiElementNum++; + } + + if (pElementDef) + { + + // At this point we know we have an element. + + *puiElementNum = uiElementNum; + pElmInfo->m_uiDataType = attrElmGetType( pElementDef); + pElmInfo->m_pFirstIcd = pElementDef->pFirstIcd; + pElmInfo->m_uiState = attrElmGetState( pElementDef); + } + else + { + F_DataVector searchKey; + F_DataVector foundKey; + FLMUINT uiType; + + // See if it is perchance in the extended list, if there + // is one + + if (!m_pExtElementDefTbl) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + searchKey.reset(); + foundKey.reset(); + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ELEMENT_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUINT( 1, uiElementNum))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXCL, &foundKey))) + { + if (rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_BOF_HIT || + rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + + // If it is not an element definition document, skip it + // we are done - there are no more. + + if (RC_BAD( rc = foundKey.getUINT( 0, &uiType))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + if (uiType != ELM_ELEMENT_TAG) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // Get the element number. + + if (RC_BAD( rc = foundKey.getUINT( 1, &uiElementNum))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + + // At this point, we know we found one, so we will populate + // the extended element structure. + + if (RC_BAD( rc = getExtElement( pDb, foundKey.getDocumentID(), + uiElementNum, pElmInfo))) + { + goto Exit; + } + *puiElementNum = uiElementNum; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next attribute defined in the dictionary after the one that + is passed in. +Note: Populates data type, first ICD, state, and flags +***************************************************************************/ +RCODE F_Dict::getNextAttribute( + F_Db * pDb, + FLMUINT * puiAttributeNum, + F_AttrElmInfo * pAttrInfo) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pAttributeDef = NULL; + FLMUINT uiAttributeNum = *puiAttributeNum; + + if (uiAttributeNum < m_uiLowestAttributeNum) + { + uiAttributeNum = m_uiLowestAttributeNum; + } + else + { + uiAttributeNum++; + } + + while (uiAttributeNum >= m_uiLowestAttributeNum && + uiAttributeNum <= m_uiHighestAttributeNum) + { + pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - m_uiLowestAttributeNum]; + if (attrElmGetState( pAttributeDef)) + { + break; + } + else + { + pAttributeDef = NULL; + } + uiAttributeNum++; + } + + if (pAttributeDef) + { + + // At this point we know we have an attribute. + + *puiAttributeNum = uiAttributeNum; + pAttrInfo->m_uiDataType = attrElmGetType( pAttributeDef); + pAttrInfo->m_pFirstIcd = pAttributeDef->pFirstIcd; + pAttrInfo->m_uiState = attrElmGetState( pAttributeDef); + pAttrInfo->m_uiFlags = attrElmGetFlags( pAttributeDef); + } + else + { + F_DataVector searchKey; + F_DataVector foundKey; + FLMUINT uiType; + + // See if it is perchance in the extended list, if there + // is one + + if (!m_pExtAttributeDefTbl) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + searchKey.reset(); + foundKey.reset(); + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ATTRIBUTE_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUINT( 1, uiAttributeNum))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXCL, &foundKey))) + { + if (rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_BOF_HIT || + rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + + // If it is not an attribute definition document, skip it + // we are done - there are no more. + + if (RC_BAD( rc = foundKey.getUINT( 0, &uiType))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + if (uiType != ELM_ATTRIBUTE_TAG) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // Get the attribute number. + + if (RC_BAD( rc = foundKey.getUINT( 1, &uiAttributeNum))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + + // At this point, we know we found one, so we will populate + // the extended attribute structure. + + if (RC_BAD( rc = getExtAttribute( pDb, foundKey.getDocumentID(), + uiAttributeNum, pAttrInfo))) + { + goto Exit; + } + *puiAttributeNum = uiAttributeNum; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the collection given a collection number. +***************************************************************************/ +RCODE F_Dict::getCollection( + FLMUINT uiCollectionNum, + F_COLLECTION ** ppCollection, // [out] optional + FLMBOOL bOfflineOk + ) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection = NULL; + + // Try most commonly used one first - default data collection + + if( uiCollectionNum == XFLM_DATA_COLLECTION) + { + pCollection = m_pDataCollection; + } + else if( uiCollectionNum && + uiCollectionNum >= m_uiLowestCollectionNum && + uiCollectionNum <= m_uiHighestCollectionNum) + { + pCollection = m_ppCollectionTbl [uiCollectionNum - + m_uiLowestCollectionNum]; + } + else + { + switch (uiCollectionNum) + { + case XFLM_DICT_COLLECTION: + pCollection = m_pDictCollection; + break; + + case XFLM_MAINT_COLLECTION: + pCollection = m_pMaintCollection; + break; + } + } + + // If the collection is encrypted, and we are in limited mode, then it must + // be offline. + + if (!pCollection) + { + if (ppCollection) + { + *ppCollection = NULL; + } + rc = RC_SET( NE_XFLM_BAD_COLLECTION); + goto Exit; + } + else + { + if ((pCollection->lfInfo.uiEncId && m_bInLimitedMode) && !bOfflineOk) + { + rc = RC_SET( NE_XFLM_COLLECTION_OFFLINE); + } + + if (ppCollection) + { + *ppCollection = pCollection; + } + } + +Exit: + + return rc; +} + +/*************************************************************************** +Desc: Returns the ID of a prefix (unicode buffer) +***************************************************************************/ +RCODE F_Dict::getPrefixId( + F_Db * pDb, + const FLMUNICODE * puzPrefix, + FLMUINT * puiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey; + F_DataVector foundKey; + + // Get the prefix number - look up in the index + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_PREFIX_TAG))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUnicode( 1, puzPrefix))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + // Data part of the retrieved key has the dictionary number + // for this prefix + + if (RC_BAD( rc = foundKey.getUINT( 3, puiPrefixId))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + *puiPrefixId = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Returns the ID of a prefix (native buffer) +***************************************************************************/ +RCODE F_Dict::getPrefixId( + F_Db * pDb, + const char * pszPrefix, + FLMUINT * puiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey; + F_DataVector foundKey; + + // Get the prefix number - look up in the index + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_PREFIX_TAG))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszPrefix))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + // Data part of the retrieved key has the dictionary number + // for this prefix + + if (RC_BAD( rc = foundKey.getUINT( 3, puiPrefixId))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + *puiPrefixId = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Returns the ID of an encryption definition (unicode buffer) +***************************************************************************/ +RCODE F_Dict::getEncDefId( + F_Db * pDb, + const FLMUNICODE * puzEncDef, + FLMUINT * puiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey; + F_DataVector foundKey; + + // Get the encdef number - look up in the name index + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ENCDEF_TAG))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUnicode( 1, puzEncDef))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + // Data part of the retrieved key has the dictionary number + // for this encdef + + if (RC_BAD( rc = foundKey.getUINT( 3, puiEncDefId))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + *puiEncDefId = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Returns the ID of an encryption definition (native buffer) +***************************************************************************/ +RCODE F_Dict::getEncDefId( + F_Db * pDb, + const char * pszEncDef, + FLMUINT * puiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey; + F_DataVector foundKey; + + // Get the encdef number - look up in the name index + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_ENCDEF_TAG))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszEncDef))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + // Data part of the retrieved key has the dictionary number + // for this encdef + + if (RC_BAD( rc = foundKey.getUINT( 3, puiEncDefId))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + *puiEncDefId = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Returns the name of a prefix, either Unicode or Native. +***************************************************************************/ +RCODE F_Dict::getPrefix( + FLMBOOL bUnicode, + FLMUINT uiPrefixId, + void * pvPrefixBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_PREFIX * pPrefix; + FLMUNICODE * puzName; + FLMUINT uiOffset = 0; + FLMUNICODE * puzPrefixBuf = (FLMUNICODE *)pvPrefixBuf; + char * pszPrefixBuf = (char *)pvPrefixBuf; + FLMUINT uiBufChars = (bUnicode ? uiBufSize / sizeof(FLMUNICODE) : uiBufSize); + + if( RC_BAD( rc = getPrefix( uiPrefixId, &pPrefix))) + { + goto Exit; + } + + if( (puzName = pPrefix->puzPrefixName) != NULL) + { + if( bUnicode && puzPrefixBuf) + { + if( !uiBufChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + while( *puzName) + { + if( uiBufChars == 1) + { + puzPrefixBuf[ uiOffset] = 0; + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + puzPrefixBuf[ uiOffset++] = *puzName; + puzName++; + uiBufChars--; + } + + puzPrefixBuf[ uiOffset] = 0; + + } + else if (pszPrefixBuf) + { + if (!uiBufChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + while ( *puzName) + { + if (uiBufChars == 1) + { + pszPrefixBuf[ uiOffset] = 0; + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Is the character convertable? + if (*puzName > 127) + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + else + { + pszPrefixBuf[ uiOffset] = (char)*puzName; + uiOffset++; + } + puzName++; + } + + pszPrefixBuf[ uiOffset] = 0; + } + else if( puiCharsReturned) + { + uiOffset = f_unilen( puzName); + } + } + +Exit: + + if( puiCharsReturned) + { + *puiCharsReturned = uiOffset; + } + + return( rc); +} + +/*************************************************************************** +Desc: Returns prefix object +***************************************************************************/ +RCODE F_Dict::getPrefix( + FLMUINT uiPrefixNum, + F_PREFIX ** ppPrefix) +{ + F_PREFIX * pPrefix = NULL; + + if( uiPrefixNum) + { + if( uiPrefixNum >= m_uiLowestPrefixNum && + uiPrefixNum <= m_uiHighestPrefixNum) + { + pPrefix = m_ppPrefixTbl[ uiPrefixNum - m_uiLowestPrefixNum]; + } + } + + if( ppPrefix) + { + *ppPrefix = pPrefix; + } + + return( pPrefix ? NE_XFLM_OK : RC_SET( NE_XFLM_BAD_PREFIX)); +} + +/*************************************************************************** +Desc: Returns the name of an encdef, either Unicode or Native. +***************************************************************************/ +RCODE F_Dict::getEncDef( + FLMBOOL bUnicode, + FLMUINT uiEncDefId, + void * pvEncDefBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_ENCDEF * pEncDef; + FLMUNICODE * puzName; + FLMUINT uiOffset = 0; + FLMUNICODE * puzEncDefBuf = (FLMUNICODE *)pvEncDefBuf; + char * pszEncDefBuf = (char *)pvEncDefBuf; + FLMUINT uiBufChars = (bUnicode ? uiBufSize / sizeof(FLMUNICODE) : uiBufSize); + + if( RC_BAD( rc = getEncDef( uiEncDefId, &pEncDef))) + { + goto Exit; + } + + if( (puzName = pEncDef->puzEncDefName) != NULL) + { + if( bUnicode && puzEncDefBuf) + { + if( !uiBufChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + while( *puzName) + { + if( uiBufChars == 1) + { + puzEncDefBuf[ uiOffset] = 0; + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + puzEncDefBuf[ uiOffset++] = *puzName; + puzName++; + uiBufChars--; + } + + puzEncDefBuf[ uiOffset] = 0; + + } + else if (pszEncDefBuf) + { + if (!uiBufChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + while ( *puzName) + { + if (uiBufChars == 1) + { + pszEncDefBuf[ uiOffset] = 0; + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Is the character convertable? + if (*puzName > 127) + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + else + { + pszEncDefBuf[ uiOffset] = (char)*puzName; + uiOffset++; + } + puzName++; + } + + pszEncDefBuf[ uiOffset] = 0; + } + else if( puiCharsReturned) + { + uiOffset = f_unilen( puzName); + } + } + +Exit: + + if( puiCharsReturned) + { + *puiCharsReturned = uiOffset; + } + + return( rc); +} + +/*************************************************************************** +Desc: Returns encdef object +***************************************************************************/ +RCODE F_Dict::getEncDef( + FLMUINT uiEncDefNum, + F_ENCDEF ** ppEncDef) +{ + F_ENCDEF * pEncDef = NULL; + + if( uiEncDefNum) + { + if( uiEncDefNum >= m_uiLowestEncDefNum && + uiEncDefNum <= m_uiHighestEncDefNum) + { + pEncDef = m_ppEncDefTbl[ uiEncDefNum - m_uiLowestEncDefNum]; + } + } + + if( ppEncDef) + { + *ppEncDef = pEncDef; + } + + return( pEncDef ? NE_XFLM_OK : RC_SET( NE_XFLM_BAD_ENCDEF_NUM)); +} + +/*************************************************************************** +Desc: Returns the root node of the specified attribute definition +***************************************************************************/ +RCODE F_Dict::getDefinitionDoc( + F_Db * pDb, + FLMUINT uiTag, + FLMUINT uiDictId, + F_DOMNode ** ppDoc) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey; + F_DataVector foundKey; + + // Need to lookup the document's ID, using its dictionary number + // attribute + + if (RC_BAD( rc = searchKey.setUINT( 0, uiTag))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUINT( 1, uiDictId))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, + foundKey.getDocumentID(), ppDoc))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the IXD and LFILE information given an index number. +***************************************************************************/ +RCODE F_Dict::getIndex( + FLMUINT uiIndexNum, + LFILE ** ppLFile, // [out] optional + IXD ** ppIxd, // [out] optional + FLMBOOL bOfflineOk) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd = NULL; + + if (uiIndexNum >= m_uiLowestIxNum && + uiIndexNum <= m_uiHighestIxNum) + { + pIxd = m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum]; + } + else + { + switch (uiIndexNum) + { + case XFLM_DICT_NUMBER_INDEX: + pIxd = m_pNumberIndex; + break; + case XFLM_DICT_NAME_INDEX: + pIxd = m_pNameIndex; + break; + } + } + + if (ppIxd) + { + *ppIxd = pIxd; + } + + if (!pIxd) + { + if (ppLFile) + { + *ppLFile = NULL; + } + rc = RC_SET( NE_XFLM_BAD_IX); + goto Exit; + } + else + { + if (ppLFile) + { + *ppLFile = &pIxd->lfInfo; + } + + // If the index is suspended the IXD_OFFLINE flag + // will be set, so it is sufficient to just test + // the IXD_OFFLINE for both suspended and offline + // conditions. + + if ((pIxd->uiFlags & IXD_OFFLINE) && !bOfflineOk) + { + rc = RC_SET( NE_XFLM_INDEX_OFFLINE); + goto Exit; + } + + // An encrypted index is offline if we are in limited mode. + + if (pIxd->lfInfo.uiEncId && m_bInLimitedMode && !bOfflineOk) + { + rc = RC_SET( NE_XFLM_INDEX_OFFLINE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Given an index number, returns the next index after that. These + should be returned in ascending numeric order ALWAYS - there are + routines that are depending on this. +***************************************************************************/ +IXD * F_Dict::getNextIndex( + FLMUINT uiIndexNum, + FLMBOOL bOkToGetPredefined) +{ + IXD * pIxd = NULL; + + if (uiIndexNum < m_uiLowestIxNum) + { + uiIndexNum = m_uiLowestIxNum; + } + else + { + uiIndexNum++; + } + while (uiIndexNum >= m_uiLowestIxNum && + uiIndexNum <= m_uiHighestIxNum) + { + if ((pIxd = m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum]) != NULL) + { + goto Exit; + } + uiIndexNum++; + } + + // pIxd better be NULL at this point + + flmAssert( pIxd == NULL); + + if (bOkToGetPredefined) + { + if (uiIndexNum <= XFLM_DICT_NUMBER_INDEX) + { + pIxd = m_pNumberIndex; + } + else if (uiIndexNum <= XFLM_DICT_NAME_INDEX) + { + pIxd = m_pNameIndex; + } + } + +Exit: + + return( pIxd); +} + +/*************************************************************************** +Desc: Given a collection number, returns the next collection after that. + These should be returned in ascending numeric order ALWAYS - there + are routines that are depending on this. +***************************************************************************/ +F_COLLECTION * F_Dict::getNextCollection( + FLMUINT uiCollectionNum, + FLMBOOL bOkToGetPredefined) +{ + F_COLLECTION * pCollection = NULL; + + if (uiCollectionNum < m_uiLowestCollectionNum) + { + uiCollectionNum = m_uiLowestCollectionNum; + } + else + { + uiCollectionNum++; + } + while (uiCollectionNum >= m_uiLowestCollectionNum && + uiCollectionNum <= m_uiHighestCollectionNum) + { + if ((pCollection = m_ppCollectionTbl [uiCollectionNum - + m_uiLowestCollectionNum]) != NULL) + { + goto Exit; // Will return pLFile + } + uiCollectionNum++; + } + + // pCollection better be NULL at this point + + flmAssert( pCollection == NULL); + + if (bOkToGetPredefined) + { + if( uiCollectionNum <= XFLM_MAINT_COLLECTION) + { + pCollection = m_pMaintCollection; + } + else if( uiCollectionNum <= XFLM_DATA_COLLECTION) + { + pCollection = m_pDataCollection; + } + else if (uiCollectionNum <= XFLM_DICT_COLLECTION) + { + pCollection = m_pDictCollection; + } + } + +Exit: + + return( pCollection); +} + +/**************************************************************************** +Desc: Link the dictionary to an F_Database object + NOTE: This routine assumes the global mutex is locked. +****************************************************************************/ +void F_Dict::linkToDatabase( + F_Database * pDatabase) +{ + if ((m_pNext = pDatabase->m_pDictList) != NULL) + { + m_uiDictSeq = m_pNext->m_uiDictSeq + 1; + m_pNext->m_pPrev = this; + } + else + { + m_uiDictSeq = 1; + } + pDatabase->m_pDictList = this; + m_pDatabase = pDatabase; +} + +/**************************************************************************** +Desc: Unlink the dictionary from its F_Database object + NOTE: This routine assumes the database mutex is locked. +****************************************************************************/ +void F_Dict::unlinkFromDatabase( void) +{ + + // Unlink the local dictionary from its database - if it is connected + // to one. + + if (m_pDatabase) + { + if (m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + else + { + m_pDatabase->m_pDictList = m_pNext; + } + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + } + + // Free the local dictionary and its associated tables. + + Release(); +} + +/**************************************************************************** +Desc: Insert an IFD into the chain of IFDs that is headed by ppFirstIfd. + Alter ppFirstIfd if linking at head of chain. +****************************************************************************/ +FSTATIC void fdictInsertInIcdChain( + ICD ** ppFirstIcd, + ICD * pIcd) +{ + ICD * pTempIcd; + ICD * pPrevInChain; + + if (!(*ppFirstIcd)) + { + *ppFirstIcd = pIcd; + } + else + { + + // Follow the chain and index at the front or rear depending on + // if the attribute or element is required within the set. + + pTempIcd = *ppFirstIcd; + if ((pIcd->uiFlags & ICD_REQUIRED_IN_SET) || + !(pTempIcd->uiFlags & ICD_REQUIRED_IN_SET)) + { + pIcd->pNextInChain = pTempIcd; + *ppFirstIcd = pIcd; + } + else + { + + // Not required in set and first ICD is required in set. + // Look for first not required ICD in the chain. + + pPrevInChain = pTempIcd; + pTempIcd = pTempIcd->pNextInChain; + for (; pTempIcd; pTempIcd = pTempIcd->pNextInChain) + { + if (!(pTempIcd->uiFlags & ICD_REQUIRED_IN_SET)) + { + break; + } + pPrevInChain = pTempIcd; + } + pIcd->pNextInChain = pPrevInChain->pNextInChain; + pPrevInChain->pNextInChain = pIcd; + } + } +} + +/**************************************************************************** +Desc: Remove an ICD from the chain of ICDs that is headed by ppFirstIcd. + Alter ppFirstIcd if it was at the head of the chain. +****************************************************************************/ +FSTATIC void fdictRemoveFromIcdChain( + ICD ** ppFirstIcd, + ICD * pIcd) +{ + ICD * pTmpIcd; + ICD * pPrevInChain; + + pPrevInChain = NULL; + pTmpIcd = *ppFirstIcd; + while (pTmpIcd != pIcd) + { + pPrevInChain = pTmpIcd; + pTmpIcd = pTmpIcd->pNextInChain; + } + + // Better have found it! + + flmAssert( pTmpIcd == pIcd); + + // Unlink from the chain + + if (!pPrevInChain) + { + *ppFirstIcd = pIcd->pNextInChain; + } + else + { + pPrevInChain->pNextInChain = pIcd->pNextInChain; + } +} + +/**************************************************************************** +Desc: Set the first ICD pointer for an extended element - if the element + is currently in memory. Should not need to lock the mutex to do this + because the dictionary should not be in a shared state - only one + F_Db should be pointing to it, and it should be in the middle of + an update transaction where an index definition is being added, + modified, or deleted. +****************************************************************************/ +void F_Dict::setExtElementFirstIcd( + FLMUINT uiElementNum, + ICD * pFirstIcd) +{ + EXT_ATTR_ELM_DEF * pExtElementDef; + + pExtElementDef = getExtElementDef( uiElementNum); + if (pExtElementDef->uiDictNum == uiElementNum) + { + pExtElementDef->attrElmDef.pFirstIcd = pFirstIcd; + } +} + +/**************************************************************************** +Desc: Set the first ICD pointer for an extended attribute - if the attribute + is currently in memory. Should not need to lock the mutex to do this + because the dictionary should not be in a shared state - only one + F_Db should be pointing to it, and it should be in the middle of + an update transaction where an index definition is being added, + modified, or deleted. +****************************************************************************/ +void F_Dict::setExtAttributeFirstIcd( + FLMUINT uiAttributeNum, + ICD * pFirstIcd) +{ + EXT_ATTR_ELM_DEF * pExtAttributeDef; + + pExtAttributeDef = getExtAttributeDef( uiAttributeNum); + if (pExtAttributeDef->uiDictNum == uiAttributeNum) + { + pExtAttributeDef->attrElmDef.pFirstIcd = pFirstIcd; + } +} + +/**************************************************************************** +Desc: Link an ICD into its ICD chain. +****************************************************************************/ +RCODE F_Dict::linkIcdInChain( + ICD * pIcd) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT uiInsertPos; + IX_ITEM * pIxItem; + + if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + ATTR_ELM_DEF * pAttributeDef; + + if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + + // Link the ICD into its chain, making sure that required attributes + // are first in the chain. + + pAttributeDef = getAttributeDef( pIcd->uiDictNum); + + // Attribute had better be defined at this point. We should have already + // verified them. + + flmAssert( pAttributeDef != NULL); + + fdictInsertInIcdChain( &pAttributeDef->pFirstIcd, pIcd); + } + else if (attributeIsReservedTag( pIcd->uiDictNum)) + { + + // Link the ICD into its chain, making sure that required attributes + // are first in the chain. + + pAttributeDef = getReservedAttributeDef( pIcd->uiDictNum); + + // Attribute had better be defined at this point. We should have already + // verified them. + + flmAssert( pAttributeDef); + + fdictInsertInIcdChain( &pAttributeDef->pFirstIcd, pIcd); + } + else + { + + // Better be one of our extended attributes + + flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); + + if ((pIxItem = findIxAttribute( pIcd->uiDictNum, + &uiInsertPos)) == NULL) + { + + // Expand the table size if necessary - by 50 elements at a time. + + if (m_uiNumIxAttributes == m_uiIxAttributeTblSize) + { + FLMUINT uiNewTblSize = m_uiIxAttributeTblSize + 50; + IX_ITEM * pNewTbl; + + if (RC_BAD( rc = f_calloc( sizeof( IX_ITEM) * uiNewTblSize, + &pNewTbl))) + { + goto Exit; + } + if (m_uiIxAttributeTblSize) + { + f_memcpy( pNewTbl, m_pIxAttributeTbl, + sizeof( IX_ITEM) * m_uiIxAttributeTblSize); + f_free( &m_pIxAttributeTbl); + } + m_pIxAttributeTbl = pNewTbl; + m_uiIxAttributeTblSize = uiNewTblSize; + } + + // Insert a new IX_ITEM at the specified position. + + uiLoop = m_uiNumIxAttributes; + while (uiLoop > uiInsertPos) + { + f_memcpy( &m_pIxAttributeTbl [uiLoop], + &m_pIxAttributeTbl [uiLoop - 1], sizeof( IX_ITEM)); + uiLoop--; + } + pIxItem = &m_pIxAttributeTbl [uiInsertPos]; + pIxItem->uiDictNum = pIcd->uiDictNum; + pIxItem->pFirstIcd = NULL; + m_uiNumIxAttributes++; + } + fdictInsertInIcdChain( &pIxItem->pFirstIcd, pIcd); + setExtAttributeFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); + } + } + else if (pIcd->uiDictNum == ELM_ROOT_TAG) + { + fdictInsertInIcdChain( &m_pRootIcdList, pIcd); + } + else + { + ATTR_ELM_DEF * pElementDef; + + if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + + // Link the ICD into its chain, making sure that required elements + // are first in the chain. + + pElementDef = getElementDef( pIcd->uiDictNum); + + // Element had better be defined at this point. We should have already + // verified them. + + flmAssert( pElementDef != NULL); + + fdictInsertInIcdChain( &pElementDef->pFirstIcd, pIcd); + } + else if (elementIsReservedTag( pIcd->uiDictNum)) + { + + // Link the ICD into its chain, making sure that required elements + // are first in the chain. + + pElementDef = getReservedElementDef( pIcd->uiDictNum); + + // Element had better be defined at this point. We should have already + // verified them. + + flmAssert( pElementDef); + + fdictInsertInIcdChain( &pElementDef->pFirstIcd, pIcd); + } + else + { + + // Better be one of our extended elements + + flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ELEMENT_NUM); + + if ((pIxItem = findIxElement( pIcd->uiDictNum, + &uiInsertPos)) == NULL) + { + + // Expand the table size if necessary - by 50 elements at a time. + + if (m_uiNumIxElements == m_uiIxElementTblSize) + { + FLMUINT uiNewTblSize = m_uiIxElementTblSize + 50; + IX_ITEM * pNewTbl; + + if (RC_BAD( rc = f_calloc( sizeof( IX_ITEM) * uiNewTblSize, + &pNewTbl))) + { + goto Exit; + } + if (m_uiIxElementTblSize) + { + f_memcpy( pNewTbl, m_pIxElementTbl, + sizeof( IX_ITEM) * m_uiIxElementTblSize); + f_free( &m_pIxElementTbl); + } + m_pIxElementTbl = pNewTbl; + m_uiIxElementTblSize = uiNewTblSize; + } + + // Insert a new IX_ITEM at the specified position. + + uiLoop = m_uiNumIxElements; + while (uiLoop > uiInsertPos) + { + f_memcpy( &m_pIxElementTbl [uiLoop], + &m_pIxElementTbl [uiLoop - 1], sizeof( IX_ITEM)); + uiLoop--; + } + pIxItem = &m_pIxElementTbl [uiInsertPos]; + pIxItem->uiDictNum = pIcd->uiDictNum; + pIxItem->pFirstIcd = NULL; + m_uiNumIxElements++; + } + fdictInsertInIcdChain( &pIxItem->pFirstIcd, pIcd); + setExtElementFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Link all of the ICDs of a tree into their proper chains from the + IX_ITEM array or ATTR_ELM_DEF array. +***************************************************************************/ +RCODE F_Dict::linkIcds( + ICD * pIcdTree) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd = pIcdTree; + + while (pIcd) + { + if (RC_BAD( rc = linkIcdInChain( pIcd))) + { + goto Exit; + } + if (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + else + { + while (pIcd && !pIcd->pNextSibling) + { + pIcd = pIcd->pParent; + } + if (!pIcd) + { + break; + } + pIcd = pIcd->pNextSibling; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Unlink an ICD from the ICD chain it is in. +****************************************************************************/ +void F_Dict::unlinkIcdFromChain( + ICD * pIcd) +{ + IX_ITEM * pIxItem; + + if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + ATTR_ELM_DEF * pAttributeDef; + + // Unlink the ICD from its chain - must find its previous ICD. + + if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + pAttributeDef = getAttributeDef( pIcd->uiDictNum); + + // Attribute had better be defined at this point. + + flmAssert( pAttributeDef != NULL && pAttributeDef->pFirstIcd); + + fdictRemoveFromIcdChain( &pAttributeDef->pFirstIcd, pIcd); + } + else if (attributeIsReservedTag( pIcd->uiDictNum)) + { + pAttributeDef = getReservedAttributeDef( pIcd->uiDictNum); + + // Attribute had better be defined at this point. + + flmAssert( pAttributeDef != NULL && pAttributeDef->pFirstIcd); + + fdictRemoveFromIcdChain( &pAttributeDef->pFirstIcd, pIcd); + } + else + { + + // Better be one of our extended attributes + + flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); + + pIxItem = findIxAttribute( pIcd->uiDictNum); + + // If there is an ICD, it better be in the IX_ITEM list! + + flmAssert( pIxItem && pIxItem->pFirstIcd); + + fdictRemoveFromIcdChain( &pIxItem->pFirstIcd, pIcd); + setExtAttributeFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); + } + } + else if (pIcd->uiDictNum == ELM_ROOT_TAG) + { + fdictRemoveFromIcdChain( &m_pRootIcdList, pIcd); + } + else + { + ATTR_ELM_DEF * pElementDef; + + // Unlink the ICD from its chain - must find its previous ICD. + + if (pIcd->uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + pElementDef = getElementDef( pIcd->uiDictNum); + + // Element had better be defined at this point. + + flmAssert( pElementDef != NULL && pElementDef->pFirstIcd); + + fdictRemoveFromIcdChain( &pElementDef->pFirstIcd, pIcd); + } + else if (elementIsReservedTag( pIcd->uiDictNum)) + { + pElementDef = getReservedElementDef( pIcd->uiDictNum); + + // Element had better be defined at this point. + + flmAssert( pElementDef != NULL && pElementDef->pFirstIcd); + + fdictRemoveFromIcdChain( &pElementDef->pFirstIcd, pIcd); + } + else + { + + // Better be one of our extended elements + + flmAssert( pIcd->uiDictNum >= FLM_LOW_EXT_ATTRIBUTE_NUM); + + pIxItem = findIxElement( pIcd->uiDictNum); + + // If there is an ICD, it better be in the IX_ITEM list! + + flmAssert( pIxItem && pIxItem->pFirstIcd); + + fdictRemoveFromIcdChain( &pIxItem->pFirstIcd, pIcd); + setExtElementFirstIcd( pIcd->uiDictNum, pIxItem->pFirstIcd); + } + } +} + +/**************************************************************************** +Desc: Unlink all ICDs in an ICD tree from the chains they are in. +****************************************************************************/ +void F_Dict::unlinkIcds( + ICD * pIcdTree) +{ + ICD * pIcd = pIcdTree; + + while (pIcd) + { + unlinkIcdFromChain( pIcd); + if (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + else + { + while (pIcd && !pIcd->pNextSibling) + { + pIcd = pIcd->pParent; + } + if (!pIcd) + { + break; + } + pIcd = pIcd->pNextSibling; + } + } +} + +/**************************************************************************** +Desc: Copies an IXD and all of its ICDs. +****************************************************************************/ +RCODE F_Dict::copyIXD( + IXD ** ppDestIxd, + IXD * pSrcIxd) +{ + RCODE rc = NE_XFLM_OK; + IXD * pDestIxd; + ICD * pSrcIcd; + ICD * pDestIcd; + ICD * pLastIcd = NULL; + ICD * pTmpIcd; + FLMBOOL bLinkAsChild = FALSE; + + if (!pSrcIxd) + { + *ppDestIxd = NULL; + goto Exit; + } + + // Allocate the IXD structure + + if (RC_BAD( rc = m_dictPool.poolAlloc( sizeof( IXD), (void **)&pDestIxd))) + { + goto Exit; + } + + *ppDestIxd = pDestIxd; + f_memcpy( pDestIxd, pSrcIxd, sizeof( IXD)); + + // Null out the pointers to the ICDs. + + pDestIxd->pIcdTree = NULL; + pDestIxd->pFirstKey = NULL; + pDestIxd->pLastKey = NULL; + pDestIxd->pFirstData = NULL; + pDestIxd->pLastData = NULL; + pDestIxd->pFirstContext = NULL; + pDestIxd->pLastContext = NULL; + + // Copy the ICDs + + pSrcIcd = pSrcIxd->pIcdTree; + while (pSrcIcd) + { + if (RC_BAD( rc = m_dictPool.poolAlloc( sizeof( ICD), + (void **)&pDestIcd))) + { + goto Exit; + } + f_memcpy( pDestIcd, pSrcIcd, sizeof( ICD)); + pDestIcd->pNextInChain = NULL; + pDestIcd->pIxd = pDestIxd; + + // Link into key component list + + if (pDestIcd->uiKeyComponent) + { + pTmpIcd = pDestIxd->pFirstKey; + while (pTmpIcd && + pDestIcd->uiKeyComponent > pTmpIcd->uiKeyComponent) + { + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + + if ((pDestIcd->pNextKeyComponent = pTmpIcd) == NULL) + { + // Link at end of list. + + if ((pDestIcd->pPrevKeyComponent = pDestIxd->pLastKey) != NULL) + { + pDestIxd->pLastKey->pNextKeyComponent = pDestIcd; + } + else + { + pDestIxd->pFirstKey = pDestIcd; + } + pDestIxd->pLastKey = pDestIcd; + } + else + { + // Link in front of pTmpIcd + + flmAssert( pDestIcd->uiKeyComponent < pTmpIcd->uiKeyComponent); + if ((pDestIcd->pPrevKeyComponent = + pTmpIcd->pPrevKeyComponent) == NULL) + { + pDestIxd->pFirstKey = pDestIcd; + } + else + { + pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pDestIcd; + } + pTmpIcd->pPrevKeyComponent = pDestIcd; + } + } + + // Link into the data component list + + if (pDestIcd->uiDataComponent) + { + pTmpIcd = pDestIxd->pFirstData; + while (pTmpIcd && pDestIcd->uiDataComponent > pTmpIcd->uiDataComponent) + { + pTmpIcd = pTmpIcd->pNextDataComponent; + } + if ((pDestIcd->pNextDataComponent = pTmpIcd) == NULL) + { + + // Link at end of list + + if ((pDestIcd->pPrevDataComponent = pDestIxd->pLastData) != NULL) + { + pDestIxd->pLastData->pNextDataComponent = pDestIcd; + } + else + { + pDestIxd->pFirstData = pDestIcd; + } + pDestIxd->pLastData = pDestIcd; + } + else + { + + // Link in front of pTmpIcd + + flmAssert( pDestIcd->uiDataComponent != pTmpIcd->uiDataComponent); + if ((pDestIcd->pPrevDataComponent = + pTmpIcd->pPrevDataComponent) == NULL) + { + + // Link at very front of list + + pDestIxd->pFirstData = pDestIcd; + } + else + { + pTmpIcd->pPrevDataComponent->pNextDataComponent = pDestIcd; + } + pTmpIcd->pPrevDataComponent = pDestIcd; + } + } + + // Link into the context component list if not a key or data component + + if (!pDestIcd->uiDataComponent && !pDestIcd->uiKeyComponent) + { + pTmpIcd = pDestIxd->pFirstContext; + while (pTmpIcd && pDestIcd->uiCdl > pTmpIcd->uiCdl) + { + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + if ((pDestIcd->pNextKeyComponent = pTmpIcd) == NULL) + { + + // Link at end of list + + if ((pDestIcd->pPrevKeyComponent = pDestIxd->pLastContext) != NULL) + { + pDestIxd->pLastContext->pNextKeyComponent = pDestIcd; + } + else + { + pDestIxd->pFirstContext = pDestIcd; + } + pDestIxd->pLastContext = pDestIcd; + } + else + { + + // Link in front of pTmpIcd + + flmAssert( pDestIcd->uiCdl != pTmpIcd->uiCdl); + if ((pDestIcd->pPrevKeyComponent = + pTmpIcd->pPrevKeyComponent) == NULL) + { + + // Link at very front of list + + pDestIxd->pFirstContext = pDestIcd; + } + else + { + pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pDestIcd; + } + pTmpIcd->pPrevKeyComponent = pDestIcd; + } + } + + // Link the ICD into the destination ICD tree + + pDestIcd->pFirstChild = NULL; + if (!pDestIxd->pIcdTree) + { + pDestIxd->pIcdTree = pDestIcd; + pDestIcd->pParent = NULL; + pDestIcd->pPrevSibling = NULL; + pDestIcd->pNextSibling = NULL; + } + else if (bLinkAsChild) + { + + // link as child + + pLastIcd->pFirstChild = pDestIcd; + pDestIcd->pParent = pLastIcd; + pDestIcd->pPrevSibling = NULL; + pDestIcd->pNextSibling = NULL; + } + else + { + + // link as sibling + + pLastIcd->pNextSibling = pDestIcd; + pDestIcd->pPrevSibling = pLastIcd; + pDestIcd->pNextSibling = NULL; + pDestIcd->pParent = pLastIcd->pParent; + } + + // Go to the next source ICD + + pLastIcd = pDestIcd; + if (pSrcIcd->pFirstChild) + { + pSrcIcd = pSrcIcd->pFirstChild; + bLinkAsChild = TRUE; + } + else + { + while (pSrcIcd && !pSrcIcd->pNextSibling) + { + pLastIcd = pLastIcd->pParent; + pSrcIcd = pSrcIcd->pParent; + } + if (!pSrcIcd) + { + break; + } + bLinkAsChild = FALSE; + pSrcIcd = pSrcIcd->pNextSibling; + } + } + + // Put the ICDs into their appropriate ICD chains. + + if (RC_BAD( rc = linkIcds( pDestIxd->pIcdTree))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copies a collection +****************************************************************************/ +FSTATIC RCODE fdictCopyCollection( + F_Pool * pDictPool, + F_COLLECTION ** ppDestCollection, + F_COLLECTION * pSrcCollection) +{ + RCODE rc = NE_XFLM_OK; + + if (!pSrcCollection) + { + *ppDestCollection = NULL; + } + else + { + if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_COLLECTION), + (void **)ppDestCollection))) + { + goto Exit; + } + f_memcpy( *ppDestCollection, pSrcCollection, sizeof( F_COLLECTION)); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copies a prefix +****************************************************************************/ +FSTATIC RCODE fdictCopyPrefix( + F_Pool * pDictPool, + F_PREFIX ** ppDestPrefix, + F_PREFIX * pSrcPrefix) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPrefixLen; + + if (!pSrcPrefix) + { + *ppDestPrefix = NULL; + } + else + { + if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_PREFIX), + (void **)ppDestPrefix))) + { + goto Exit; + } + + (*ppDestPrefix)->ui64PrefixId = pSrcPrefix->ui64PrefixId; + uiPrefixLen = f_unilen( pSrcPrefix->puzPrefixName); + + if( RC_BAD( rc = pDictPool->poolAlloc( + sizeof( FLMUNICODE) * (uiPrefixLen + 1), + (void **)&(*ppDestPrefix)->puzPrefixName))) + { + goto Exit; + } + + f_memcpy( (*ppDestPrefix)->puzPrefixName, + pSrcPrefix->puzPrefixName, + (uiPrefixLen + 1) * sizeof( FLMUNICODE)); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copies an encryption def (F_ENCDEF) +****************************************************************************/ +FSTATIC RCODE fdictCopyEncDef( + F_Pool * pDictPool, + F_ENCDEF ** ppDestEncDef, + F_ENCDEF * pSrcEncDef) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiEncDefLen; + + if (!pSrcEncDef) + { + *ppDestEncDef = NULL; + } + else + { + if (RC_BAD( rc = pDictPool->poolAlloc( sizeof( F_ENCDEF), + (void **)ppDestEncDef))) + { + goto Exit; + } + + (*ppDestEncDef)->ui64EncDefId = pSrcEncDef->ui64EncDefId; + (*ppDestEncDef)->ui64DocumentId = pSrcEncDef->ui64DocumentId; + uiEncDefLen = f_unilen( pSrcEncDef->puzEncDefName); + + if( RC_BAD( rc = pDictPool->poolAlloc( + sizeof( FLMUNICODE) * (uiEncDefLen + 1), + (void **)&(*ppDestEncDef)->puzEncDefName))) + { + goto Exit; + } + + f_memcpy( (*ppDestEncDef)->puzEncDefName, + pSrcEncDef->puzEncDefName, + (uiEncDefLen + 1) * sizeof( FLMUNICODE)); + + (*ppDestEncDef)->pCcs = pSrcEncDef->pCcs; + (*ppDestEncDef)->uiEncKeySize = pSrcEncDef->uiEncKeySize; + (*ppDestEncDef)->pCcs->AddRef(); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Clones a dictionary from another one. +****************************************************************************/ +RCODE F_Dict::cloneDict( + F_Dict * pSrcDict) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + FLMUINT uiLoop; + FLMBOOL bElementMutexLocked = FALSE; + FLMBOOL bAttributeMutexLocked = FALSE; + + resetDict(); + + // Copy the element definitions. + + m_uiLowestElementNum = pSrcDict->m_uiLowestElementNum; + m_uiHighestElementNum = pSrcDict->m_uiHighestElementNum; + if (m_uiHighestElementNum) + { + uiCount = m_uiHighestElementNum - m_uiLowestElementNum + 1; + if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pElementDefTbl))) + { + goto Exit; + } + f_memcpy( m_pElementDefTbl, pSrcDict->m_pElementDefTbl, + uiCount * sizeof( ATTR_ELM_DEF)); + + // NULL out every element's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + m_pElementDefTbl[ uiLoop].pFirstIcd = NULL; + } + } + + // Copy the reserved element definitions. + + uiCount = XFLM_LAST_RESERVED_ELEMENT_TAG - + XFLM_FIRST_RESERVED_ELEMENT_TAG + 1; + if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pReservedElementDefTbl))) + { + goto Exit; + } + f_memcpy( m_pReservedElementDefTbl, pSrcDict->m_pReservedElementDefTbl, + uiCount * sizeof( ATTR_ELM_DEF)); + + // NULL out every element's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + m_pReservedElementDefTbl [uiLoop].pFirstIcd = NULL; + } + + // Copy the extended element definitions, if any + + if (pSrcDict->m_pExtElementDefTbl) + { + + // Source table's mutex must be locked while copying + + f_mutexLock( pSrcDict->m_hExtElementDefMutex); + bElementMutexLocked = TRUE; + + m_uiExtElementDefTblSize = pSrcDict->m_uiExtElementDefTblSize; + + // Allocate a mutex + + if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) + { + goto Exit; + } + + // Allocate the array + + if (RC_BAD( rc = f_alloc( m_uiExtElementDefTblSize * + sizeof( EXT_ATTR_ELM_DEF), &m_pExtElementDefTbl))) + { + goto Exit; + } + f_memcpy( m_pExtElementDefTbl, pSrcDict->m_pExtElementDefTbl, + m_uiExtElementDefTblSize * sizeof( EXT_ATTR_ELM_DEF)); + + f_mutexUnlock( pSrcDict->m_hExtElementDefMutex); + bElementMutexLocked = FALSE; + + // NULL out every element's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < m_uiExtElementDefTblSize; uiLoop++) + { + m_pExtElementDefTbl [uiLoop].attrElmDef.pFirstIcd = NULL; + } + } + + // Copy the extended element IX_ITEM structures for indexing. + + if (pSrcDict->m_pIxElementTbl) + { + m_uiIxElementTblSize = pSrcDict->m_uiIxElementTblSize; + m_uiNumIxElements = pSrcDict->m_uiNumIxElements; + if (RC_BAD( rc = f_alloc( sizeof( IX_ITEM) * m_uiIxElementTblSize, + &m_pIxElementTbl))) + { + goto Exit; + } + f_memcpy( m_pIxElementTbl, pSrcDict->m_pIxElementTbl, + sizeof( IX_ITEM) * m_uiIxElementTblSize); + + // NULL out every element's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < m_uiNumIxElements; uiLoop++) + { + m_pIxElementTbl [uiLoop].pFirstIcd = NULL; + } + } + + // Copy the attribute definitions. + + m_uiLowestAttributeNum = pSrcDict->m_uiLowestAttributeNum; + m_uiHighestAttributeNum = pSrcDict->m_uiHighestAttributeNum; + if (m_uiHighestAttributeNum) + { + uiCount = m_uiHighestAttributeNum - m_uiLowestAttributeNum + 1; + if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pAttributeDefTbl))) + { + goto Exit; + } + f_memcpy( m_pAttributeDefTbl, pSrcDict->m_pAttributeDefTbl, + uiCount * sizeof( ATTR_ELM_DEF)); + + // NULL out every attribute's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + m_pAttributeDefTbl[ uiLoop].pFirstIcd = NULL; + } + } + + // Copy the reserved attribute definitions. + + uiCount = XFLM_LAST_RESERVED_ATTRIBUTE_TAG - + XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1; + if (RC_BAD( rc = f_alloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pReservedAttributeDefTbl))) + { + goto Exit; + } + f_memcpy( m_pReservedAttributeDefTbl, pSrcDict->m_pReservedAttributeDefTbl, + uiCount * sizeof( ATTR_ELM_DEF)); + + // NULL out every attribute's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + m_pReservedAttributeDefTbl [uiLoop].pFirstIcd = NULL; + } + + // Copy the extended attribute definitions, if any + + if (pSrcDict->m_pExtAttributeDefTbl) + { + + // Source table's mutex must be locked while copying + + f_mutexLock( pSrcDict->m_hExtAttributeDefMutex); + bAttributeMutexLocked = TRUE; + + m_uiExtAttributeDefTblSize = pSrcDict->m_uiExtAttributeDefTblSize; + + // Allocate a mutex + + if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) + { + goto Exit; + } + + // Allocate the array + + if (RC_BAD( rc = f_alloc( m_uiExtAttributeDefTblSize * + sizeof( EXT_ATTR_ELM_DEF), + &m_pExtAttributeDefTbl))) + { + goto Exit; + } + f_memcpy( m_pExtAttributeDefTbl, pSrcDict->m_pExtAttributeDefTbl, + m_uiExtAttributeDefTblSize * sizeof( EXT_ATTR_ELM_DEF)); + + f_mutexUnlock( pSrcDict->m_hExtAttributeDefMutex); + bAttributeMutexLocked = FALSE; + + // NULL out every attribute's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < m_uiExtAttributeDefTblSize; uiLoop++) + { + m_pExtAttributeDefTbl[ uiLoop].attrElmDef.pFirstIcd = NULL; + } + } + + // Copy the extended attribute IX_ITEM structures for indexing. + + if (pSrcDict->m_pIxAttributeTbl) + { + m_uiIxAttributeTblSize = pSrcDict->m_uiIxAttributeTblSize; + m_uiNumIxAttributes = pSrcDict->m_uiNumIxAttributes; + if (RC_BAD( rc = f_alloc( sizeof( IX_ITEM) * m_uiIxAttributeTblSize, + &m_pIxAttributeTbl))) + { + goto Exit; + } + f_memcpy( m_pIxAttributeTbl, pSrcDict->m_pIxAttributeTbl, + sizeof( IX_ITEM) * m_uiIxAttributeTblSize); + + // NULL out every attribute's pFirstIcd pointer. These will + // be fixed up as indexes are added. + + for (uiLoop = 0; uiLoop < m_uiNumIxAttributes; uiLoop++) + { + m_pIxAttributeTbl [uiLoop].pFirstIcd = NULL; + } + } + + // Copy the pre-defined collections + + if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, + &m_pDictCollection, + pSrcDict->m_pDictCollection))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, + &m_pDataCollection, + pSrcDict->m_pDataCollection))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, + &m_pMaintCollection, + pSrcDict->m_pMaintCollection))) + { + goto Exit; + } + + // Collection Table + + if ((uiCount = pSrcDict->getCollectionCount( FALSE)) > 0) + { + m_uiLowestCollectionNum = pSrcDict->m_uiLowestCollectionNum; + m_uiHighestCollectionNum = pSrcDict->m_uiHighestCollectionNum; + + if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_COLLECTION *), + &m_ppCollectionTbl))) + { + goto Exit; + } + + // Copy each F_COLLECTION in the table. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + if (RC_BAD( rc = fdictCopyCollection( &m_dictPool, + &m_ppCollectionTbl [uiLoop], + pSrcDict->m_ppCollectionTbl [uiLoop]))) + { + goto Exit; + } + } + } + + // Copy the user-defined prefixes + + if ((uiCount = pSrcDict->getPrefixCount()) > 0) + { + m_uiLowestPrefixNum = pSrcDict->m_uiLowestPrefixNum; + m_uiHighestPrefixNum = pSrcDict->m_uiHighestPrefixNum; + + if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_PREFIX *), + &m_ppPrefixTbl))) + { + goto Exit; + } + + // Copy each F_PREFIX in the table. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + if (RC_BAD( rc = fdictCopyPrefix( &m_dictPool, + &m_ppPrefixTbl [uiLoop], + pSrcDict->m_ppPrefixTbl [uiLoop]))) + { + goto Exit; + } + } + } + + // Copy the encryption definitions + + if ((uiCount = pSrcDict->getEncDefCount()) > 0) + { + m_uiLowestEncDefNum = pSrcDict->m_uiLowestEncDefNum; + m_uiHighestEncDefNum = pSrcDict->m_uiHighestEncDefNum; + + if (RC_BAD( rc = f_alloc( uiCount * sizeof( F_ENCDEF *), + &m_ppEncDefTbl))) + { + goto Exit; + } + + // Copy each F_ENCDEF in the table. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + if (RC_BAD( rc = fdictCopyEncDef( &m_dictPool, + &m_ppEncDefTbl [uiLoop], + pSrcDict->m_ppEncDefTbl [uiLoop]))) + { + goto Exit; + } + } + } + + // Copy the hard-coded indexes + + if (RC_BAD( rc = copyIXD( &m_pNameIndex, pSrcDict->m_pNameIndex))) + { + goto Exit; + } + if (RC_BAD( rc = copyIXD( &m_pNumberIndex, pSrcDict->m_pNumberIndex))) + { + goto Exit; + } + + // IXD Table + + m_uiLowestIxNum = pSrcDict->m_uiLowestIxNum; + m_uiHighestIxNum = pSrcDict->m_uiHighestIxNum; + + // Array of IXD pointers - for user defined indexes only + + if ((uiCount = pSrcDict->getIndexCount( FALSE)) > 0) + { + if (RC_BAD( rc = f_alloc( uiCount * sizeof( IXD *), + &m_ppIxdTbl))) + { + goto Exit; + } + + // Copy each IXD in the table. + + for (uiLoop = 0; uiLoop < uiCount; uiLoop++) + { + if (RC_BAD( rc = copyIXD( &m_ppIxdTbl [uiLoop], + pSrcDict->m_ppIxdTbl [uiLoop]))) + { + goto Exit; + } + } + } + + // Copy the name table + + if (RC_BAD( rc = allocNameTable())) + { + goto Exit; + } + + if (RC_BAD( rc = m_pNameTable->cloneNameTable( pSrcDict->m_pNameTable))) + { + goto Exit; + } + + // See if we can get the limited mode flag from the database file instead + // of copying it from the source dictionary. + + if (pSrcDict->m_pDatabase) + { + m_bInLimitedMode = pSrcDict->m_pDatabase->inLimitedMode(); + } + else + { + m_bInLimitedMode = pSrcDict->m_bInLimitedMode; + } + +Exit: + + if (bElementMutexLocked) + { + f_mutexUnlock( pSrcDict->m_hExtElementDefMutex); + } + + if (bAttributeMutexLocked) + { + f_mutexUnlock( pSrcDict->m_hExtAttributeDefMutex); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine checks to see if an element is referenced in an index + definition. +***************************************************************************/ +RCODE F_Dict::checkElementReferences( + FLMUINT uiElementNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIxdCount; + FLMUINT uiIxdLoop; + IXD * pIxd; + ICD * pIcd; + + // Look through all ICDs in all IXDs. + + uiIxdCount = getIndexCount( FALSE); + for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) + { + if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) + { + + // Loop through all of the index's ICDs. + + pIcd = pIxd->pIcdTree; + while (pIcd) + { + if (!(pIcd->uiFlags & ICD_IS_ATTRIBUTE) && + pIcd->uiDictNum == uiElementNum) + { + rc = RC_SET( NE_XFLM_CANNOT_DEL_ELEMENT); + goto Exit; + } + if (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + else + { + while (pIcd && !pIcd->pNextSibling) + { + pIcd = pIcd->pParent; + } + if (!pIcd) + { + break; + } + pIcd = pIcd->pNextSibling; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine checks to see if an attribute is referenced in an index + definition. +***************************************************************************/ +RCODE F_Dict::checkAttributeReferences( + FLMUINT uiAttributeNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIxdCount; + FLMUINT uiIxdLoop; + IXD * pIxd; + ICD * pIcd; + + // Look through all ICDs in all IXDs. + + uiIxdCount = getIndexCount( FALSE); + for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) + { + if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) + { + + // Loop through all of the index's ICDs. + + pIcd = pIxd->pIcdTree; + while (pIcd) + { + if ((pIcd->uiFlags & ICD_IS_ATTRIBUTE) && + pIcd->uiDictNum == uiAttributeNum) + { + rc = RC_SET( NE_XFLM_CANNOT_DEL_ATTRIBUTE); + goto Exit; + } + if (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + else + { + while (pIcd && !pIcd->pNextSibling) + { + pIcd = pIcd->pParent; + } + if (!pIcd) + { + break; + } + pIcd = pIcd->pNextSibling; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine checks to see if a collection is referenced in an index + definition. +***************************************************************************/ +RCODE F_Dict::checkCollectionReferences( + FLMUINT uiCollectionNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIxdCount; + FLMUINT uiIxdLoop; + IXD * pIxd; + + // Look through all IXDs + + uiIxdCount = getIndexCount( FALSE); + for (uiIxdLoop = 0; uiIxdLoop < uiIxdCount; uiIxdLoop++) + { + if ((pIxd = m_ppIxdTbl [uiIxdLoop]) != NULL) + { + if (pIxd->uiCollectionNum == uiCollectionNum) + { + rc = RC_SET( NE_XFLM_MUST_DELETE_INDEXES); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Reallocate a field, index, or collection table. Note that any new + slots in the table will be zeroed out. +***************************************************************************/ +RCODE F_Dict::reallocTbl( + FLMUINT uiNewId, + FLMUINT uiElementSize, + void ** ppvTbl, + FLMUINT * puiLowest, + FLMUINT * puiHighest, + FLMUINT uiAdjustFactor, + FLMUINT uiMaxId + ) +{ + RCODE rc = NE_XFLM_OK; + void * pvNewTbl; + FLMUINT uiLowest = *puiLowest; + FLMUINT uiOldLowest = uiLowest; + FLMUINT uiHighest = *puiHighest; + FLMUINT uiOldCount; + FLMUINT uiNewCount; + + if (!uiHighest) + { + uiOldCount = 0; + if (uiNewId <= uiAdjustFactor) + { + uiLowest = 1; + } + else + { + uiLowest = uiNewId - uiAdjustFactor; + } + if (uiNewId >= uiMaxId - uiAdjustFactor) + { + uiHighest = uiMaxId; + } + else + { + uiHighest = uiNewId + uiAdjustFactor; + } + } + else + { + uiOldCount = uiHighest - uiLowest + 1; + if (uiNewId < uiLowest) + { + if (uiNewId <= uiAdjustFactor) + { + uiLowest = 1; + } + else + { + uiLowest = uiNewId - uiAdjustFactor; + } + } + else + { + if (uiNewId >= uiMaxId - uiAdjustFactor) + { + uiHighest = uiMaxId; + } + else + { + uiHighest = uiNewId + uiAdjustFactor; + } + } + } + + // Reallocate the table. + + uiNewCount = uiHighest - uiLowest + 1; + if (RC_BAD( rc = f_calloc( uiNewCount * uiElementSize, + &pvNewTbl))) + { + goto Exit; + } + if (uiOldCount) + { + f_memcpy( (FLMBYTE *)pvNewTbl + + uiElementSize * (uiOldLowest - uiLowest), + *ppvTbl, uiOldCount * uiElementSize); + } + f_free( ppvTbl); + *ppvTbl = pvNewTbl; + *puiLowest = uiLowest; + *puiHighest = uiHighest; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Maps a string to an element or attribute data type. +***************************************************************************/ +RCODE fdictGetDataType( + char * pszDataType, + FLMUINT * puiDataType + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + + // Parse the type keyword - only one type allowed. + + for (uiDataType = 0; uiDataType < XFLM_NUM_OF_TYPES; uiDataType++) + { + if (f_stricmp( pszDataType, fdictDataTypes [uiDataType]) == 0) + { + *puiDataType = uiDataType; + goto Exit; // Will return NE_XFLM_OK + } + } + + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_DATA_TYPE); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Maps a string to an element or attribute data type. +***************************************************************************/ +char * fdictGetDataTypeStr( + FLMUINT uiDataType + ) +{ + if( uiDataType > XFLM_NUM_OF_TYPES) + { + return( NULL); + } + + return( fdictDataTypes[ uiDataType]); +} + +/*************************************************************************** +Desc: Maps a string to an element or attribute state. +***************************************************************************/ +RCODE fdictGetState( + const char * pszState, + FLMUINT * puiState) +{ + RCODE rc = NE_XFLM_OK; + + if (f_stricmp( pszState, XFLM_CHECKING_OPTION_STR) == 0) + { + *puiState = ATTR_ELM_STATE_CHECKING; + } + else if (f_stricmp( pszState, XFLM_PURGE_OPTION_STR) == 0) + { + *puiState = ATTR_ELM_STATE_PURGE; + } + else if (f_stricmp( pszState, XFLM_ACTIVE_OPTION_STR) == 0) + { + *puiState = ATTR_ELM_STATE_ACTIVE; + } + else + { + rc = RC_SET( NE_XFLM_ILLEGAL_STATE); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Maps a string to an index state. +***************************************************************************/ +RCODE fdictGetIndexState( + const char * pszState, + FLMUINT * puiState) +{ + RCODE rc = NE_XFLM_OK; + + if (f_stricmp( pszState, XFLM_INDEX_SUSPENDED_STR) == 0) + { + *puiState = IXD_SUSPENDED | IXD_OFFLINE; + } + else if (f_stricmp( pszState, XFLM_INDEX_OFFLINE_STR) == 0) + { + *puiState = IXD_OFFLINE; + } + else if (!pszState [0] || + f_stricmp( pszState, XFLM_INDEX_ONLINE_STR) == 0) + { + *puiState = 0; + } + else + { + rc = RC_SET( NE_XFLM_ILLEGAL_STATE); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Determine if an element name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalElementName( + FLMUNICODE * // puzElementName + ) +{ + //VISIT: Need to fill this out - could return NE_XFLM_ILLEGAL_ELEMENT_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if an attribute name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalAttributeName( + FLMUNICODE * puzAttributeName + ) +{ + if (*puzAttributeName == '/' && *(puzAttributeName + 1) == 0) + { + return( RC_SET( NE_XFLM_ILLEGAL_ATTRIBUTE_NAME)); + } + //VISIT: Need to fill this out - could return NE_XFLM_ILLEGAL_ATTRIBUTE_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if an element number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalElementNumber( + FLMUINT uiElementNumber, + FLMBOOL bAllowReserved) +{ + return( (uiElementNumber > 0 && uiElementNumber <= XFLM_MAX_ELEMENT_NUM) || + (bAllowReserved && + uiElementNumber >= XFLM_FIRST_RESERVED_ELEMENT_TAG && + uiElementNumber <= XFLM_LAST_RESERVED_ELEMENT_TAG) + ? NE_XFLM_OK + : RC_SET( NE_XFLM_ILLEGAL_ELEMENT_NUMBER)); +} + +/*************************************************************************** +Desc: Determine if an attribute number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalAttributeNumber( + FLMUINT uiAttributeNumber, + FLMBOOL bAllowReserved) +{ + return( (uiAttributeNumber > 0 && + uiAttributeNumber <= XFLM_MAX_ATTRIBUTE_NUM) || + (bAllowReserved && + uiAttributeNumber >= XFLM_FIRST_RESERVED_ATTRIBUTE_TAG && + uiAttributeNumber <= XFLM_LAST_RESERVED_ATTRIBUTE_TAG) + ? NE_XFLM_OK + : RC_SET( NE_XFLM_ILLEGAL_ATTRIBUTE_NUMBER)); +} + +/*************************************************************************** +Desc: Retrieve a dictionary. If there is no dictionary, open it first. +***************************************************************************/ +RCODE F_Db::getDictionary( + F_Dict ** ppDict) +{ + RCODE rc = NE_XFLM_OK; + + if (!m_pDict) + { + if (RC_BAD( rc = dictOpen())) + { + goto Exit; + } + } + *ppDict = m_pDict; +Exit: + + return rc; +} + +/*************************************************************************** +Desc: Change an element's or attribute's state. +***************************************************************************/ +RCODE F_Db::changeItemState( + FLMUINT uiDictType, + FLMUINT uiDictNum, + const char * pszState) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNewState; + FLMBOOL bStartedTrans = FALSE; + F_DataVector srchKey; + F_DataVector foundKey; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttr = NULL; + F_DOMNode * pChangeCountAttr = NULL; + F_DOMNode * pTrackerDoc = NULL; + FLMUINT64 ui64DocumentId; + FLMUINT64 ui64Count; + FLMBOOL bMustAbortOnError = FALSE; + F_AttrElmInfo defInfo; + + if (uiDictType != ELM_ELEMENT_TAG && uiDictType != ELM_ATTRIBUTE_TAG) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + if (RC_BAD( rc = fdictGetState( pszState, &uiNewState))) + { + goto Exit; + } + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + else if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + } + else + { + // Need to have an update transaction going. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // Commit any keys in the KREF buffers. + + if (RC_BAD( rc = keysCommit( FALSE))) + { + goto Exit; + } + + // See if the element or attribute is defined, and get its + // current state + + if (uiDictType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = m_pDict->getElement( this, uiDictNum, &defInfo))) + { + goto Exit; + } + + // Nothing to change if the state does not change. + + if (uiNewState == defInfo.m_uiState) + { + goto Exit; + } + + if (elementIsReservedTag( uiDictNum)) + { + rc = RC_SET( NE_XFLM_CANNOT_MOD_ELEMENT_STATE); + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDict->getAttribute( this, uiDictNum, &defInfo))) + { + goto Exit; + } + + // Nothing to change if the state does not change. + + if (uiNewState == defInfo.m_uiState) + { + goto Exit; + } + + if (attributeIsReservedTag( uiDictNum)) + { + rc = RC_SET( NE_XFLM_CANNOT_MOD_ATTRIBUTE_STATE); + goto Exit; + } + } + + // Check the legal state changes that can be made by external callers + // of this routine + + if (!m_bItemStateUpdOk) + { + switch (defInfo.m_uiState) + { + case ATTR_ELM_STATE_ACTIVE: + { + if( uiNewState != ATTR_ELM_STATE_CHECKING && + uiNewState != ATTR_ELM_STATE_PURGE) + { + rc = RC_SET( NE_XFLM_ILLEGAL_STATE_CHANGE); + goto Exit; + } + + // Add a task to the tracker container so the maintenance thread + // will sweep the database + + if( !(m_uiFlags & FDB_SWEEP_SCHEDULED)) + { + if( RC_BAD( rc = createRootNode( XFLM_MAINT_COLLECTION, + ELM_SWEEP_TAG, ELEMENT_NODE, &pTrackerDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = pTrackerDoc->setAttributeValueUINT64( this, + ATTR_TRANSACTION_TAG, m_ui64CurrTransID))) + { + goto Exit; + } + + if( RC_BAD( rc = pTrackerDoc->addModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = documentDone( pTrackerDoc))) + { + goto Exit; + } + + f_semSignal( m_pDatabase->m_hMaintSem); + m_uiFlags |= FDB_SWEEP_SCHEDULED; + } + + break; + } + + case ATTR_ELM_STATE_CHECKING: + case ATTR_ELM_STATE_PURGE: + { + if (uiNewState != ATTR_ELM_STATE_ACTIVE) + { + rc = RC_SET( NE_XFLM_ILLEGAL_STATE_CHANGE); + goto Exit; + } + break; + } + + default: + { + // Only other old state is ATTR_ELM_STATE_UNUSED, and it + // can be changed to any other state. + + break; + } + } + } + + bMustAbortOnError = TRUE; + + // To do a state change, must create a new dictionary + + if( !(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if( RC_BAD( rc = dictClone())) + { + goto Exit; + } + } + + // Find the element's document + + if( RC_BAD( rc = srchKey.setUINT( 0, uiDictType))) + { + goto Exit; + } + + if( RC_BAD( rc = srchKey.setUINT( 1, uiDictNum))) + { + goto Exit; + } + + if( RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &srchKey, XFLM_EXACT, &foundKey))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + // We should have found the thing! It should have + // already been indexed! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Read the root node of the document + + ui64DocumentId = foundKey.getDocumentID(); + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentId, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // We should have found the thing! It should have + // already been indexed! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + // Find the state attribute + + if (RC_BAD( rc = pNode->getAttribute( this, ATTR_STATE_TAG, + (IF_DOMNode **)&pAttr))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + // Create the attribute since it was not found + + if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + } + else + { + // Unfreeze the attribute state + + if (RC_BAD( rc = pAttr->removeModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + // Set the attribute state + + if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pszState))) + { + goto Exit; + } + + // Freeze the attribute state + + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Find the state change count attribute + + if (RC_BAD( rc = pNode->getAttribute( this, ATTR_STATE_CHANGE_COUNT_TAG, + (IF_DOMNode **)&pChangeCountAttr))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + // Create the attribute since it was not found + + if (RC_BAD( rc = pNode->createAttribute( this, + ATTR_STATE_CHANGE_COUNT_TAG, + (IF_DOMNode **)&pChangeCountAttr))) + { + goto Exit; + } + ui64Count = 0; + } + else + { + // Unfreeze the attribute state + + if (RC_BAD( rc = pChangeCountAttr->removeModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if (RC_BAD( rc = pChangeCountAttr->getUINT64( this, &ui64Count))) + { + goto Exit; + } + } + + // Increment the state change count. + + if (RC_BAD( rc = pChangeCountAttr->setUINT64( this, ui64Count + 1))) + { + goto Exit; + } + + // Freeze the attribute state change count. + + if( RC_BAD( rc = pChangeCountAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Set the state in the dictionary + + if (uiDictType == ELM_ELEMENT_TAG) + { + if (uiDictNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + ATTR_ELM_DEF * pElementDef; + + if ((pElementDef = m_pDict->getElementDef( uiDictNum)) != NULL) + { + attrElmSetState( pElementDef, uiNewState); + } + } + else + { + EXT_ATTR_ELM_DEF * pExtElementDef; + + // Better be one of our extended elements - cannot change + // state on reserved elements. + + flmAssert( elementIsUserDefined( uiDictNum)); + + // No need to lock the mutex, because no other threads can + // be accessing it right now. + + if (m_pDict->m_pExtElementDefTbl) + { + pExtElementDef = m_pDict->getExtElementDef( uiDictNum); + if (pExtElementDef->uiDictNum == uiDictNum) + { + attrElmSetState( &pExtElementDef->attrElmDef, uiNewState); + } + } + } + } + else + { + if (uiDictNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + ATTR_ELM_DEF * pAttributeDef; + + if ((pAttributeDef = m_pDict->getAttributeDef( uiDictNum)) != NULL) + { + attrElmSetState( pAttributeDef, uiNewState); + } + } + else + { + EXT_ATTR_ELM_DEF * pExtAttributeDef; + + // Better be one of our extended attributes - cannot change + // state on reserved attributes. + + flmAssert( attributeIsUserDefined( uiDictNum)); + + // No need to lock the mutex, because no other threads can + // be accessing it right now. + + if (m_pDict->m_pExtAttributeDefTbl) + { + pExtAttributeDef = m_pDict->getExtAttributeDef( uiDictNum); + if (pExtAttributeDef->uiDictNum == uiDictNum) + { + attrElmSetState( &pExtAttributeDef->attrElmDef, uiNewState); + } + } + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( pTrackerDoc) + { + pTrackerDoc->Release(); + } + + if( pChangeCountAttr) + { + pChangeCountAttr->Release(); + } + + if( bStartedTrans) + { + rc = commitTrans( 0, FALSE); + } + else if( RC_BAD( rc) && bMustAbortOnError) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/*************************************************************************** +Desc: Retrieve an element or attribute definition - get name, data type, + state, and namespace. +***************************************************************************/ +RCODE F_Db::getElmAttrInfo( + FLMUINT uiType, + FLMUINT64 ui64DocumentID, + F_AttrElmInfo * pDefInfo, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDocNode = NULL; + F_DOMNode * pAttr = NULL; + F_CachedNode * pCachedDocNode; + FLMBYTE szTmpBuf[ 80]; + FLMUINT uiNameId; + FLMUNICODE * puzName = NULL; + FLMBOOL bNamespaceDecl = FALSE; + FLMBOOL bHadUniqueSubElementTag = FALSE; + + // Retrieve the root element of the definition + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pDocNode))) + { + goto Exit; + } + + flmAssert( pDocNode->getNodeType() == ELEMENT_NODE); + + pDefInfo->m_pDocNode = (IF_DOMNode *)pDocNode; + pDocNode->AddRef(); + pCachedDocNode = pDocNode->m_pCachedNode; + uiNameId = pCachedDocNode->getNameId(); + + if( uiType != uiNameId) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Cycle through the attributes on the root of the definition + + if( pCachedDocNode->hasAttributes()) + { + if( RC_BAD( rc = pDocNode->getFirstAttribute( + this, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch( uiNameId) + { + case ATTR_NAME_TAG: + { + pDefInfo->m_pNameAttr = (IF_DOMNode *)pAttr; + pAttr->AddRef(); + + if( RC_BAD( rc = pAttr->getUnicode( this, &puzName))) + { + goto Exit; + } + + if( isXMLNS( puzName) && + (puzName [5] == 0 || + (puzName [5] == ':' && puzName [6] != 0))) + { + if( uiType == ELM_ATTRIBUTE_TAG) + { + pDefInfo->m_uiFlags |= ATTR_ELM_NS_DECL; + bNamespaceDecl = TRUE; + } + else + { + rc = RC_SET( NE_XFLM_ILLEGAL_ELEMENT_NAME); + goto Exit; + } + } + + if (uiType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = fdictLegalElementName( puzName))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = fdictLegalAttributeName( puzName))) + { + goto Exit; + } + } + + // Put a freeze on name if we are not deleting. + // Modify is allowed, but delete is not. + + if( !bDeleting && !bOpeningDict) + { + if( RC_BAD( rc = pAttr->addModeFlags( + this, FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + break; + } + + case ATTR_TARGET_NAMESPACE_TAG: + { + pDefInfo->m_pTargetNamespaceAttr = (IF_DOMNode *)pAttr; + pAttr->AddRef(); + break; + } + + case ATTR_TYPE_TAG: + { + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictGetDataType( + (char *)szTmpBuf, &pDefInfo->m_uiDataType))) + { + goto Exit; + } + + if (uiType == ELM_ATTRIBUTE_TAG && + pDefInfo->m_uiDataType == XFLM_NODATA_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_DATA_TYPE); + goto Exit; + } + + break; + } + + case ATTR_STATE_TAG: + { + if (RC_BAD( rc = pAttr->getUTF8( + this, szTmpBuf, sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictGetState( (char *)szTmpBuf, + &pDefInfo->m_uiState))) + { + goto Exit; + } + + // Freeze the state - can only be changed by an explicit + // call to changeItemState. + + if( !bOpeningDict) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + break; + } + + case ATTR_DICT_NUMBER_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, &pDefInfo->m_uiDictNum))) + { + goto Exit; + } + + if (uiType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = fdictLegalElementNumber( + pDefInfo->m_uiDictNum, FALSE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = fdictLegalAttributeNumber( + pDefInfo->m_uiDictNum, FALSE))) + { + goto Exit; + } + } + break; + } + + case ATTR_UNIQUE_SUB_ELEMENTS_TAG: + { + // This one is only allowed on element definitions. + if (uiType != ELM_ELEMENT_TAG) + { + rc = RC_SET( NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE); + goto Exit; + } + bHadUniqueSubElementTag = TRUE; + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + if (f_stricmp( szTmpBuf, "yes") == 0 || + f_stricmp( szTmpBuf, "true") == 0 || + f_stricmp( szTmpBuf, "1") == 0 || + f_stricmp( szTmpBuf, "on") == 0 || + f_stricmp( szTmpBuf, "enable") == 0) + { + pDefInfo->m_uiFlags |= ATTR_ELM_UNIQUE_SUBELMS; + } + else if (f_stricmp( szTmpBuf, "no") != 0 && + f_stricmp( szTmpBuf, "false") != 0 && + f_stricmp( szTmpBuf, "0") != 0 && + f_stricmp( szTmpBuf, "off") != 0 && + f_stricmp( szTmpBuf, "disable") != 0) + { + rc = RC_SET( NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE); + goto Exit; + } + + // Freeze the state - cannot be changed or deleted. + + if( !bDeleting && !bOpeningDict) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + break; + } + + default: + { + // Ignore all other attributes + + break; + } + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + } + + // Make sure we had both a name and number specified + + if (!pDefInfo->m_pNameAttr) + { + rc = (RCODE)(uiType == ELM_ELEMENT_TAG + ? RC_SET( NE_XFLM_MISSING_ELEMENT_NAME) + : RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NAME)); + goto Exit; + } + + if (!pDefInfo->m_uiDictNum) + { + rc = (RCODE)(uiType == ELM_ELEMENT_TAG + ? RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER) + : RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER)); + goto Exit; + } + + if (!bDeleting && bNamespaceDecl && + pDefInfo->m_uiDataType != XFLM_TEXT_TYPE) + { + rc = RC_SET( NE_XFLM_ILLEGAL_NAMESPACE_DECL_DATATYPE); + goto Exit; + } + + // If we didn't have a unique sub-element tag, set one - better only + // be missing when we first add the element definition. + + if (!bHadUniqueSubElementTag && uiType == ELM_ELEMENT_TAG) + { + flmAssert( !bOpeningDict && !bDeleting); + + // Set attribute and freeze it if it was missing. + + if (RC_BAD( rc = pDocNode->createAttribute( + this, ATTR_UNIQUE_SUB_ELEMENTS_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)"no"))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + else if (pDefInfo->m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) + { + // If sub-elements must be unique, the data type has to be + // nodata. + + if (pDefInfo->m_uiDataType != XFLM_NODATA_TYPE) + { + rc = RC_SET( NE_XFLM_DATA_TYPE_MUST_BE_NO_DATA); + goto Exit; + } + } + +Exit: + + if( pDocNode) + { + pDocNode->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( puzName) + { + f_free( &puzName); + } + + return( rc); +} + +/*************************************************************************** +Desc: Add, modify, or delete an element definition in the dictionary. +***************************************************************************/ +RCODE F_Dict::updateElementDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiElementNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting + ) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pElementDef; + EXT_ATTR_ELM_DEF * pExtElementDef; + FLMUNICODE * puzElementName = NULL; + FLMUNICODE * puzNamespace = NULL; + IX_ITEM * pIxElement; + F_AttrElmInfo defInfo; + F_DOMNode * pTmpNode = NULL; + + if (bDeleting) + { + flmAssert( uiElementNum); + + // NOTE: It is possible that the element may not be in the + // element table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + m_pNameTable->removeTag( ELM_ELEMENT_TAG, uiElementNum); + + if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + if ((pElementDef = getElementDef( uiElementNum)) != NULL) + { + pElementDef->uiFlags = 0; // Unused slot + pElementDef->pFirstIcd = NULL; + } + } + else + { + + // Better be one of our extended elements - cannot delete + // reserved elements. + + flmAssert( elementIsUserDefined( uiElementNum)); + + // No need to lock the mutex, because no other threads can + // be accessing it right now. + + if (m_pExtElementDefTbl) + { + pExtElementDef = getExtElementDef( uiElementNum); + if (pExtElementDef->uiDictNum == uiElementNum) + { + pExtElementDef->uiDictNum = 0; + pExtElementDef->attrElmDef.uiFlags = 0; + pExtElementDef->attrElmDef.pFirstIcd = NULL; + } + } + } + goto Exit; + } + + if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ELEMENT_TAG, + ui64DocumentID, &defInfo, + bOpeningDict, bDeleting))) + { + goto Exit; + } + + if (!uiElementNum) + { + uiElementNum = defInfo.m_uiDictNum; + } + + flmAssert( uiElementNum == defInfo.m_uiDictNum); + + // Get the element name + + if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( + pDb, &puzElementName))) + { + goto Exit; + } + + if( RC_BAD( rc = fdictLegalElementName( puzElementName))) + { + goto Exit; + } + + // Get the element's namespace + + if( defInfo.m_pTargetNamespaceAttr) + { + if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( + pDb, &puzNamespace))) + { + goto Exit; + } + } + + // Add to the tag table, unless we already have our quota of + // element names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_ELEMENT_TAG, uiElementNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_ELEMENT_TAG, + puzElementName, NULL, + uiElementNum, defInfo.m_uiDataType, puzNamespace, + bOpeningDict ? FALSE : TRUE))) + { + goto Exit; + } + + if (uiElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + // See if we are updating an existing element, in which case the + // data type cannot be changed. + + if ((pElementDef = getElementDef( uiElementNum)) != NULL) + { + if (attrElmGetType( pElementDef) != defInfo.m_uiDataType) + { + rc = RC_SET( NE_XFLM_CANNOT_MOD_DATA_TYPE); + goto Exit; + } + + // Still need to assign uiFldType because state could change. + + pElementDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + else + { + // If it will fit in the range of elements, simply set it. + // Otherwise, we will need to reallocate the array to make + // room for it. + + if (uiElementNum < m_uiLowestElementNum || + uiElementNum > m_uiHighestElementNum) + { + if (RC_BAD( rc = reallocTbl( uiElementNum, sizeof( ATTR_ELM_DEF), + (void **)&m_pElementDefTbl, + &m_uiLowestElementNum, + &m_uiHighestElementNum, 200, + XFLM_MAX_ELEMENT_NUM))) + { + goto Exit; + } + } + + pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; + pElementDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + } + else + { + // Cannot be modifying or adding in the reserved range. Element + // number must be user defined. + + flmAssert( elementIsUserDefined( uiElementNum)); + + // See if we should increase the extended table size. Do so if: + // 1. It has not yet been allocated, or + // 2. It has not yet reached its maximum size and this field + // number would hash to a slot beyond that maximum. + + if (!m_pExtElementDefTbl || + (m_uiExtElementDefTblSize < MAX_EXT_ATTR_ELM_ARRAY_SIZE && + uiElementNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE > + m_uiExtElementDefTblSize - 1)) + { + FLMUINT uiNewSize = uiElementNum % + MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; + FLMUINT uiOldSize = m_uiExtElementDefTblSize; + EXT_ATTR_ELM_DEF * pNewTbl; + EXT_ATTR_ELM_DEF * pOldTbl = m_pExtElementDefTbl; + FLMUINT uiLoop; + + if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) + { + uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; + } + + // If we had no array, allocate a mutex too. + + if (!pOldTbl) + { + + // Better not be a mutex allocated at this point either. + + flmAssert( m_hExtElementDefMutex == F_MUTEX_NULL); + if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) + { + goto Exit; + } + } + + // Allocate a new array + + if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, + &pNewTbl))) + { + goto Exit; + } + m_pExtElementDefTbl = pNewTbl; + m_uiExtElementDefTblSize = uiNewSize; + + // Rehash everything from the old table into the new table and then + // free the old one. + + if (pOldTbl) + { + for (uiLoop = 0; uiLoop < uiOldSize; uiLoop++) + { + if (pOldTbl [uiLoop].uiDictNum) + { + pExtElementDef = getExtElementDef( + pOldTbl [uiLoop].uiDictNum); + f_memcpy( pExtElementDef, &pOldTbl [uiLoop], + sizeof( EXT_ATTR_ELM_DEF)); + } + } + f_free( &pOldTbl); + } + } + + // Put the new or modified field into the table. No need to lock the + // mutex to do this because no other threads can be accessing this table + // at this time. + + pExtElementDef = getExtElementDef( uiElementNum); + pExtElementDef->uiDictNum = uiElementNum; + pIxElement = findIxElement( uiElementNum); + pExtElementDef->attrElmDef.pFirstIcd = pIxElement + ? pIxElement->pFirstIcd + : NULL; + pExtElementDef->attrElmDef.uiFlags = + (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + +Exit: + + if (puzElementName) + { + f_free( &puzElementName); + } + + if (puzNamespace) + { + f_free( &puzNamespace); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Add, modify, or delete an attribute definition in the dictionary. +***************************************************************************/ +RCODE F_Dict::updateAttributeDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiAttributeNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting + ) +{ + RCODE rc = NE_XFLM_OK; + ATTR_ELM_DEF * pAttributeDef; + EXT_ATTR_ELM_DEF * pExtAttributeDef; + FLMUNICODE * puzAttributeName = NULL; + FLMUNICODE * puzNamespace = NULL; + IX_ITEM * pIxAttribute; + F_AttrElmInfo defInfo; + + if (bDeleting) + { + flmAssert( uiAttributeNum); + + // NOTE: It is possible that the attribute may not be in the + // attribute table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + m_pNameTable->removeTag( ELM_ATTRIBUTE_TAG, uiAttributeNum); + + if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) + { + pAttributeDef->uiFlags = 0; // Unused slot + pAttributeDef->pFirstIcd = NULL; + } + } + else + { + // Better be one of our extended attributes - cannot delete + // reserved attributes. + + flmAssert( attributeIsUserDefined( uiAttributeNum)); + + // No need to lock the mutex, because no other threads can + // be accessing it right now. + + if (m_pExtAttributeDefTbl) + { + pExtAttributeDef = getExtAttributeDef( uiAttributeNum); + if (pExtAttributeDef->uiDictNum == uiAttributeNum) + { + pExtAttributeDef->uiDictNum = 0; + pExtAttributeDef->attrElmDef.uiFlags = 0; + pExtAttributeDef->attrElmDef.pFirstIcd = NULL; + } + } + } + goto Exit; + } + + if (RC_BAD( rc = pDb->getElmAttrInfo( ELM_ATTRIBUTE_TAG, + ui64DocumentID, &defInfo, + bOpeningDict, bDeleting))) + { + goto Exit; + } + + if (!uiAttributeNum) + { + uiAttributeNum = defInfo.m_uiDictNum; + } + + flmAssert( uiAttributeNum == defInfo.m_uiDictNum); + + // Get the attribute name + + if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( + pDb, &puzAttributeName))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalAttributeName( puzAttributeName))) + { + goto Exit; + } + + // Get the attribute's namespace + + if( defInfo.m_pTargetNamespaceAttr) + { + if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( + pDb, &puzNamespace))) + { + goto Exit; + } + } + + // Add to the tag table, unless we already have our quota of + // attribute names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_ATTRIBUTE_TAG, uiAttributeNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_ATTRIBUTE_TAG, + puzAttributeName, NULL, + uiAttributeNum, defInfo.m_uiDataType, puzNamespace, + bOpeningDict ? FALSE : TRUE))) + { + goto Exit; + } + + if (uiAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + // See if we are updating an existing attribute, in which case the + // data type cannot be changed. + + if ((pAttributeDef = getAttributeDef( uiAttributeNum)) != NULL) + { + if ( attrElmGetType( pAttributeDef) != defInfo.m_uiDataType) + { + rc = RC_SET( NE_XFLM_CANNOT_MOD_DATA_TYPE); + goto Exit; + } + + // Still need to assign uiFldType because state could change. + + pAttributeDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + else + { + + // If it will fit in the range of attributes, simply set it. + // Otherwise, we will need to reallocate the array to make + // room for it. + + if (uiAttributeNum < m_uiLowestAttributeNum || + uiAttributeNum > m_uiHighestAttributeNum) + { + if (RC_BAD( rc = reallocTbl( uiAttributeNum, sizeof( ATTR_ELM_DEF), + (void **)&m_pAttributeDefTbl, + &m_uiLowestAttributeNum, + &m_uiHighestAttributeNum, 200, + XFLM_MAX_ATTRIBUTE_NUM))) + { + goto Exit; + } + } + + pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - + m_uiLowestAttributeNum]; + pAttributeDef->uiFlags = (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + } + else + { + // Cannot be modifying or adding in the reserved range. Attribute + // number must be user defined. + + flmAssert( attributeIsUserDefined( uiAttributeNum)); + + // See if we should increase the extended table size. Do so if: + // 1. It has not yet been allocated, or + // 2. It has not yet reached its maximum size and this field + // number would hash to a slot beyond that maximum. + + if (!m_pExtAttributeDefTbl || + (m_uiExtAttributeDefTblSize < MAX_EXT_ATTR_ELM_ARRAY_SIZE && + uiAttributeNum % MAX_EXT_ATTR_ELM_ARRAY_SIZE > + m_uiExtAttributeDefTblSize - 1)) + { + FLMUINT uiNewSize = uiAttributeNum % + MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; + FLMUINT uiOldSize = m_uiExtAttributeDefTblSize; + EXT_ATTR_ELM_DEF * pNewTbl; + EXT_ATTR_ELM_DEF * pOldTbl = m_pExtAttributeDefTbl; + FLMUINT uiLoop; + + if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) + { + uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; + } + + // If we had no array, allocate a mutex too. + + if (!pOldTbl) + { + // Better not be a mutex allocated at this point either. + + flmAssert( m_hExtAttributeDefMutex == F_MUTEX_NULL); + if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) + { + goto Exit; + } + } + + // Allocate a new array + + if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, + &pNewTbl))) + { + goto Exit; + } + m_pExtAttributeDefTbl = pNewTbl; + m_uiExtAttributeDefTblSize = uiNewSize; + + // Rehash everything from the old table into the new table and then + // free the old one. + + if (pOldTbl) + { + for (uiLoop = 0; uiLoop < uiOldSize; uiLoop++) + { + if (pOldTbl [uiLoop].uiDictNum) + { + pExtAttributeDef = getExtAttributeDef( + pOldTbl [uiLoop].uiDictNum); + f_memcpy( pExtAttributeDef, &pOldTbl [uiLoop], + sizeof( EXT_ATTR_ELM_DEF)); + } + } + f_free( &pOldTbl); + } + } + + // Put the new or modified field into the table. No need to lock the + // mutex to do this because no other threads can be accessing this table + // at this time. + + pExtAttributeDef = getExtAttributeDef( uiAttributeNum); + pExtAttributeDef->uiDictNum = uiAttributeNum; + pIxAttribute = findIxAttribute( uiAttributeNum); + pExtAttributeDef->attrElmDef.pFirstIcd = pIxAttribute + ? pIxAttribute->pFirstIcd + : NULL; + pExtAttributeDef->attrElmDef.uiFlags = + (defInfo.m_uiDataType & ATTR_ELM_DATA_TYPE_MASK) | + (defInfo.m_uiState & ATTR_ELM_STATE_MASK) | + (defInfo.m_uiFlags & ATTR_ELM_FLAGS_MASK); + } + +Exit: + + if (puzAttributeName) + { + f_free( &puzAttributeName); + } + + if (puzNamespace) + { + f_free( &puzNamespace); + } + + return( rc); +} + +/*************************************************************************** +Desc: Determine if an index name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalIndexName( + FLMUNICODE * // puzIndexName + ) +{ + //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_INDEX_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if an index number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalIndexNumber( + FLMUINT uiIndexNumber + ) +{ + return( (uiIndexNumber > 0 && uiIndexNumber <= XFLM_MAX_INDEX_NUM) + ? NE_XFLM_OK + : RC_SET( NE_XFLM_ILLEGAL_INDEX_NUMBER)); +} + +/*************************************************************************** +Desc: Determine if a collection name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalCollectionName( + FLMUNICODE * // puzCollectionName + ) +{ + //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_COLLECTION_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if a collection number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalCollectionNumber( + FLMUINT uiCollectionNumber + ) +{ + return( ( (uiCollectionNumber > 0 && + uiCollectionNumber <= XFLM_MAX_COLLECTION_NUM) || + (uiCollectionNumber == XFLM_DATA_COLLECTION)) + ? NE_XFLM_OK + : RC_SET( NE_XFLM_ILLEGAL_COLLECTION_NUMBER)); +} + +/*************************************************************************** +Desc: Determine if a prefix name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalPrefixName( + FLMUNICODE * // puzPrefixName + ) +{ + //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_PREFIX_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if a prefix number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalPrefixNumber( + FLMUINT uiPrefixNumber + ) +{ + return( (uiPrefixNumber > 0 && + uiPrefixNumber <= XFLM_MAX_PREFIX_NUM) + ? NE_XFLM_OK + : RC_SET( NE_XFLM_ILLEGAL_PREFIX_NUMBER)); +} + +/*************************************************************************** +Desc: Determine if an encryption def name is legal - formatwise +***************************************************************************/ +FINLINE RCODE fdictLegalEncDefName( + FLMUNICODE * // puzEncDefName + ) +{ + //VISIT: Need to fill this out - may need to return NE_XFLM_ILLEGAL_ENCDEF_NAME + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Determine if an encryption def number is legal +***************************************************************************/ +FINLINE RCODE fdictLegalEncDefNumber( + FLMUINT uiEncDefNumber + ) +{ + return( (uiEncDefNumber > 0 && + uiEncDefNumber <= XFLM_MAX_ENCDEF_NUM) + ? NE_XFLM_OK + : RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_ENCDEF_NUMBER)); +} + +/*************************************************************************** +Desc: Determine if an encryption def type (algorithm) is legal +***************************************************************************/ +FINLINE RCODE fdictLegalEncDefType( + char * pszEncDefType, + FLMUINT * puiEncDefType + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiType; + + // Parse the type keyword - only one type allowed. + + for( uiType = 0; + uiType < MAX_ENC_TYPES ; + uiType++) + { + if( f_strnicmp( pszEncDefType, DDEncOpts[ uiType], f_strlen(DDEncOpts[ uiType])) == 0) + { + *puiEncDefType = uiType; + goto Exit; + } + } + + rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); + +Exit: + return( rc); +} + +/*************************************************************************** +Desc: Determine if a specified encryption key size is legal +***************************************************************************/ +FINLINE RCODE fdictLegalEncKeySize( + FLMUINT uiEncType, + FLMUINT uiEncKeySize + ) +{ + RCODE rc = NE_XFLM_OK; + + switch( uiEncType) + { + case FLM_NICI_AES: + { + if (uiEncKeySize != XFLM_NICI_AES128 && + uiEncKeySize != XFLM_NICI_AES192 && + uiEncKeySize != XFLM_NICI_AES256) + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + break; + } + case FLM_NICI_DES3: + { + if (uiEncKeySize != XFLM_NICI_DES3X) + { + rc = RC_SET( NE_XFLM_INVALID_ENC_KEY_SIZE); + goto Exit; + } + break; + } + default: + { + rc = RC_SET( NE_XFLM_INVALID_ENC_ALGORITHM); + goto Exit; + } + } + +Exit: + + return rc; + +} + +/*************************************************************************** +Desc: return a legal key size, based on the encryption algorithm. +***************************************************************************/ +FINLINE FLMBOOL fdictGetLegalKeySize( + FLMUINT uiEncType, + FLMUINT * puiEncKeySize + ) +{ + FLMBOOL bSizeOk = FALSE; + + // Note: The uiEncType should have already been validated. + + switch( uiEncType) + { + case FLM_NICI_AES: + { + if ( *puiEncKeySize == 0) + { + *puiEncKeySize = XFLM_NICI_AES256; + bSizeOk = TRUE; + } + else if ( *puiEncKeySize == XFLM_NICI_AES256) + { + *puiEncKeySize = XFLM_NICI_AES192; + bSizeOk = TRUE; + } + else if ( *puiEncKeySize == XFLM_NICI_AES192) + { + *puiEncKeySize = XFLM_NICI_AES128; + bSizeOk = TRUE; + } + break; + } + case FLM_NICI_DES3: + { + if ( *puiEncKeySize == 0) + { + *puiEncKeySize = XFLM_NICI_DES3X; + bSizeOk = TRUE; + } + break; + } + } + + return bSizeOk; + +} + +/*************************************************************************** +Desc: Parse an option from a string. Function returns pointer to + beginning of option. Parameter is moved to just after where that + option started. +***************************************************************************/ +FSTATIC char * fdictGetOption( + char ** ppszSrc + ) +{ + char * pszMatch = NULL; + char * pszSrc = *ppszSrc; + + while (*pszSrc == NATIVE_SPACE) + { + pszSrc++; + } + + // See if at end of string. + + if (!(*pszSrc)) + { + goto Exit; + } + + pszMatch = pszSrc; + while (*pszSrc && *pszSrc != NATIVE_SPACE) + { + pszSrc++; + } + if (*pszSrc) + { + *pszSrc = 0; + pszSrc++; + } + +Exit: + + *ppszSrc = pszSrc; + return( pszMatch); +} + +/*************************************************************************** +Desc: Retrieve an index definition. +***************************************************************************/ +RCODE F_Db::getIndexDef( + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzIndexName, + FLMUINT * puiIndexNumber, + FLMUINT * puiCollectionNumber, + FLMUINT * puiLanguage, + FLMUINT * puiFlags, + FLMUINT64 * pui64LastDocIndexed, + FLMUINT * puiEncId, + F_DOMNode ** ppNode, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode; + F_DOMNode * pAttr = NULL; + FLMBOOL bHadIndexNumber = FALSE; + FLMBOOL bHadIndexName = FALSE; + FLMBYTE szTmpBuf[ 80]; + FLMUNICODE * puzCollectionName = NULL; + FLMUINT uiNameCollectionNum = 0; + FLMUINT uiNameId; + FLMBOOL bHadLastDocIndexed = FALSE; + FLMBOOL bHadState = FALSE; + + // Set up defaults + + *ppuzIndexName = NULL; + *puiIndexNumber = 0; + *puiCollectionNumber = XFLM_DATA_COLLECTION; + *puiLanguage = m_pDatabase->m_uiDefaultLanguage; + *puiFlags = 0; + *puiEncId = 0; + *ppNode = NULL; + + // Retrieve the root element of the index definition + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) + { + goto Exit; + } + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + pCachedNode = pNode->m_pCachedNode; + + // Cycle through the attributes + + if( !pCachedNode->hasAttributes()) + { + rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); + goto Exit; + } + + if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch (uiNameId) + { + case ATTR_NAME_TAG: + { + if (RC_BAD( rc = pAttr->getUnicode( this, ppuzIndexName))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalIndexName( *ppuzIndexName))) + { + goto Exit; + } + bHadIndexName = TRUE; + break; + } + + case ATTR_DICT_NUMBER_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, puiIndexNumber))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalIndexNumber( *puiIndexNumber))) + { + goto Exit; + } + + bHadIndexNumber = TRUE; + break; + } + + case ATTR_COLLECTION_NUMBER_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalCollectionNumber( + *puiCollectionNumber))) + { + goto Exit; + } + + if (uiNameCollectionNum && + *puiCollectionNumber != uiNameCollectionNum) + { + rc = RC_SET( NE_XFLM_COLLECTION_NAME_MISMATCH); + goto Exit; + } + break; + } + + case ATTR_COLLECTION_NAME_TAG: + { + F_DataVector searchKey; + F_DataVector dataPart; + + if (RC_BAD( rc = pAttr->getUnicode( this, &puzCollectionName))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalCollectionName( puzCollectionName))) + { + goto Exit; + } + + // Get the collection number - look up in the index + + if (RC_BAD( rc = searchKey.setUINT( 0, ELM_COLLECTION_TAG))) + { + goto Exit; + } + + if (RC_BAD( rc = searchKey.setUnicode( 1, puzCollectionName))) + { + goto Exit; + } + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &dataPart))) + { + goto Exit; + } + + // Data part of the retrieved key has the dictionary number + // for this collection. + + if (RC_BAD( rc = dataPart.getUINT( 3, &uiNameCollectionNum))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + uiNameCollectionNum = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + flmAssert( uiNameCollectionNum); + + if (*puiCollectionNumber && + uiNameCollectionNum != *puiCollectionNumber) + { + rc = RC_SET( NE_XFLM_COLLECTION_NAME_MISMATCH); + goto Exit; + } + + *puiCollectionNumber = uiNameCollectionNum; + break; + } + + case ATTR_LANGUAGE_TAG: + { + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + *puiLanguage = F_DbSystem::languageToNum( (char *)szTmpBuf); + break; + } + + case ATTR_INDEX_OPTIONS_TAG: + { + FLMBYTE * pszTmp; + FLMBYTE * pszOption; + + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + pszTmp = &szTmpBuf [0]; + while ((pszOption = + (FLMBYTE *)fdictGetOption( (char **)&pszTmp)) != NULL) + { + if (f_stricmp( pszOption, + XFLM_ABS_POS_OPTION_STR) == 0) + { + (*puiFlags) |= IXD_ABS_POS; + } + else + { + rc = RC_SET( NE_XFLM_INVALID_INDEX_OPTION); + goto Exit; + } + } + break; + } + + case ATTR_STATE_TAG: + { + FLMUINT uiState; + + if (RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictGetIndexState( (char *)szTmpBuf, &uiState))) + { + goto Exit; + } + + *puiFlags = (*puiFlags & (~(IXD_SUSPENDED | IXD_OFFLINE))) | + uiState; + + // Freeze the state - can only be changed by an explicit + // call to indexSuspend or indexResume. + + if (!bOpeningDict) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + bHadState = TRUE; + break; + } + + case ATTR_LAST_DOC_INDEXED_TAG: + { + if (RC_BAD( rc = pAttr->getUINT64( this, pui64LastDocIndexed))) + { + goto Exit; + } + + bHadLastDocIndexed = TRUE; + break; + } + + case ATTR_ENCRYPTION_ID_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, puiEncId))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalEncDefNumber( *puiEncId))) + { + goto Exit; + } + + break; + } + + default: + { + // Ignore all other attributes + + break; + } + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + if (!bHadLastDocIndexed) + { + *pui64LastDocIndexed = + (FLMUINT64)(((*puiFlags) & (IXD_SUSPENDED | IXD_OFFLINE)) + ? (FLMUINT64)0 + : ~((FLMUINT64)0)); + } + + // Make sure we had both a name and number specified + + if (!bHadIndexName) + { + rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); + goto Exit; + } + + if (!bHadIndexNumber) + { + rc = RC_SET( NE_XFLM_MISSING_INDEX_NUMBER); + goto Exit; + } + + // Set a state attribute and freeze it if it was missing. + + if (!bOpeningDict && !bDeleting && !bHadState) + { + if (RC_BAD( rc = pNode->createAttribute( this, ATTR_STATE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUTF8( this, + (FLMBYTE *)XFLM_INDEX_ONLINE_STR))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + *ppNode = pNode; + + // Set to NULL so it will not be released at Exit. + + pNode = NULL; + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pAttr) + { + pAttr->Release(); + } + + if (puzCollectionName) + { + f_free( &puzCollectionName); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the information for an index component. +***************************************************************************/ +RCODE F_Db::getIndexComponentDef( + F_Dict * pDict, + F_DOMNode * pElementNode, + FLMUINT uiElementId, + IXD * pIxd, + ICD * pIcd + ) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pAttr = NULL; + F_CachedNode * pCachedNode; + ICD * pTmpIcd; + ICD * pPrevIcd; + FLMBYTE szTmpBuf[ 200]; + FLMUNICODE * puzName = NULL; + FLMUNICODE * puzNamespace = NULL; + FLMUINT uiKeyComponent; + FLMUINT uiDataComponent; + FLMUINT uiNameDictNumber = 0; + FLMUINT uiDataType; + FLMBYTE * pszTmp; + FLMBYTE * pszOption; + FLMBOOL bLimitSet = FALSE; + FLMBOOL bRequiredSet = FALSE; + FLMBOOL bIndexOnSet = FALSE; + FLMBOOL bCompareRuleSet = FALSE; + FLMBOOL bIsAttr = FALSE; + FLMBOOL bHadDictNumber = FALSE; + F_NameTable * pNameTable = NULL; + FLMUINT uiNameId; + + if (uiElementId == ELM_ATTRIBUTE_COMPONENT_TAG) + { + bIsAttr = TRUE; + pIcd->uiFlags |= ICD_IS_ATTRIBUTE; + } + + pCachedNode = pElementNode->m_pCachedNode; + + if( !pCachedNode->hasAttributes()) + { + rc = (RCODE)(bIsAttr + ? RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER) + : RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER)); + goto Exit; + } + + if( RC_BAD( rc = pElementNode->getFirstAttribute( this, &pAttr))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch (uiNameId) + { + case ATTR_DICT_NUMBER_TAG: + if (RC_BAD( rc = pAttr->getUINT( this, &pIcd->uiDictNum))) + { + goto Exit; + } + if (bIsAttr) + { + if (RC_BAD( rc = fdictLegalAttributeNumber( pIcd->uiDictNum, TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = fdictLegalElementNumber( pIcd->uiDictNum, TRUE))) + { + goto Exit; + } + } + bHadDictNumber = TRUE; + break; + + case ATTR_NAME_TAG: + if (RC_BAD( rc = pAttr->getUnicode( this, &puzName))) + { + goto Exit; + } + if (bIsAttr) + { + if (RC_BAD( rc = fdictLegalAttributeName( puzName))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = fdictLegalElementName( puzName))) + { + goto Exit; + } + } + break; + + case ATTR_TARGET_NAMESPACE_TAG: + if (RC_BAD( rc = pAttr->getUnicode( this, &puzNamespace))) + { + goto Exit; + } + break; + + case ATTR_INDEX_ON_TAG: + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + if (f_stricmp( szTmpBuf, XFLM_VALUE_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_VALUE; + pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_SUBSTRING | + ICD_EACHWORD | ICD_METAPHONE)); + } + else if (f_stricmp( szTmpBuf, XFLM_PRESENCE_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_PRESENCE; + pIcd->uiFlags &= (~(ICD_VALUE | ICD_SUBSTRING | + ICD_EACHWORD | ICD_METAPHONE)); + } + else if (f_stricmp( szTmpBuf, XFLM_SUBSTRING_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_SUBSTRING; + pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | + ICD_EACHWORD | ICD_METAPHONE)); + } + else if (f_stricmp( szTmpBuf, XFLM_EACHWORD_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_EACHWORD; + pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | + ICD_SUBSTRING | ICD_METAPHONE)); + } + else if (f_stricmp( szTmpBuf, XFLM_METAPHONE_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_METAPHONE; + pIcd->uiFlags &= (~(ICD_PRESENCE | ICD_VALUE | + ICD_SUBSTRING | ICD_EACHWORD)); + } + else + { + rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_ON); + goto Exit; + } + bIndexOnSet = TRUE; + break; + + case ATTR_REQUIRED_TAG: + if( RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + if (f_stricmp( szTmpBuf, "yes") == 0 || + f_stricmp( szTmpBuf, "true") == 0 || + f_stricmp( szTmpBuf, "1") == 0 || + f_stricmp( szTmpBuf, "on") == 0 || + f_stricmp( szTmpBuf, "enable") == 0) + { + pIcd->uiFlags |= ICD_REQUIRED_PIECE; + } + else if (f_stricmp( szTmpBuf, "no") != 0 && + f_stricmp( szTmpBuf, "false") != 0 && + f_stricmp( szTmpBuf, "0") != 0 && + f_stricmp( szTmpBuf, "off") != 0 && + f_stricmp( szTmpBuf, "disable") != 0) + { + rc = RC_SET( NE_XFLM_ILLEGAL_REQUIRED_VALUE); + goto Exit; + } + bRequiredSet = TRUE; + break; + + case ATTR_LIMIT_TAG: + if (RC_BAD( rc = pAttr->getUINT( this, &pIcd->uiLimit))) + { + goto Exit; + } + bLimitSet = TRUE; + break; + + case ATTR_COMPARE_RULES_TAG: + if (RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + pszTmp = &szTmpBuf [0]; + while ((pszOption = + (FLMBYTE *)fdictGetOption( (char **)&pszTmp)) != NULL) + { + if (f_stricmp( pszOption, + XFLM_CASE_INSENSITIVE_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_CASE_INSENSITIVE; + } + else if (f_stricmp( pszOption, + XFLM_MINSPACES_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_COMPRESS_WHITESPACE; + } + else if (f_stricmp( pszOption, + XFLM_WHITESPACE_AS_SPACE_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_WHITESPACE_AS_SPACE; + } + else if (f_stricmp( pszOption, + XFLM_IGNORE_LEADINGSPACES_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_IGNORE_LEADING_SPACE; + } + else if (f_stricmp( pszOption, + XFLM_IGNORE_TRAILINGSPACES_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_IGNORE_TRAILING_SPACE; + } + else if (f_stricmp( pszOption, + XFLM_NOUNDERSCORE_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_NO_UNDERSCORES; + } + else if (f_stricmp( pszOption, + XFLM_NOSPACE_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_NO_WHITESPACE; + } + else if (f_stricmp( pszOption, + XFLM_NODASH_OPTION_STR) == 0) + { + pIcd->uiCompareRules |= XFLM_COMP_NO_DASHES; + } + else if (f_stricmp( pszOption, + XFLM_DESCENDING_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_DESCENDING; + } + else if (f_stricmp( pszOption, + XFLM_MISSING_HIGH_OPTION_STR) == 0) + { + pIcd->uiFlags |= ICD_MISSING_HIGH; + } + else + { + rc = RC_SET( NE_XFLM_INVALID_COMPARE_RULE); + goto Exit; + } + } + bCompareRuleSet = TRUE; + break; + + case ATTR_KEY_COMPONENT_TAG: + if( RC_BAD( rc = pAttr->getUINT( this, &uiKeyComponent))) + { + goto Exit; + } + if (!uiKeyComponent) + { + rc = RC_SET( NE_XFLM_ILLEGAL_KEY_COMPONENT_NUM); + goto Exit; + } + + // Insert into its place in the list of key components + + pPrevIcd = NULL; + pTmpIcd = pIxd->pFirstKey; + while (pTmpIcd && uiKeyComponent > pTmpIcd->uiKeyComponent) + { + pPrevIcd = pTmpIcd; + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + + // Make sure not already in the list + + if (pTmpIcd && pTmpIcd->uiKeyComponent == uiKeyComponent) + { + rc = RC_SET( NE_XFLM_DUPLICATE_KEY_COMPONENT); + goto Exit; + } + + // Link after pPrevIcd + + if (pPrevIcd) + { + if ((pIcd->pNextKeyComponent = + pPrevIcd->pNextKeyComponent) != NULL) + { + pIcd->pNextKeyComponent->pPrevKeyComponent = pIcd; + } + else + { + pIxd->pLastKey = pIcd; + } + pPrevIcd->pNextKeyComponent = pIcd; + pIcd->pPrevKeyComponent = pPrevIcd; + } + else + { + if ((pIcd->pNextKeyComponent = pIxd->pFirstKey) != NULL) + { + pIxd->pFirstKey->pPrevKeyComponent = pIcd; + } + else + { + pIxd->pLastKey = pIcd; + } + pIxd->pFirstKey = pIcd; + } + pIcd->uiKeyComponent = uiKeyComponent; + pIxd->uiNumKeyComponents++; + + break; + + case ATTR_DATA_COMPONENT_TAG: + if( RC_BAD( rc = pAttr->getUINT( this, &uiDataComponent))) + { + goto Exit; + } + if (!uiDataComponent) + { + rc = RC_SET( NE_XFLM_ILLEGAL_DATA_COMPONENT_NUM); + goto Exit; + } + + // Insert into its place in the list of data components + + pPrevIcd = NULL; + pTmpIcd = pIxd->pFirstData; + while (pTmpIcd && uiDataComponent > pTmpIcd->uiDataComponent) + { + pPrevIcd = pTmpIcd; + pTmpIcd = pTmpIcd->pNextDataComponent; + } + + // Make sure not already in the list + + if (pTmpIcd && pTmpIcd->uiDataComponent == uiDataComponent) + { + rc = RC_SET( NE_XFLM_DUPLICATE_DATA_COMPONENT); + goto Exit; + } + + // Link after pPrevIcd + + if (pPrevIcd) + { + if ((pIcd->pNextDataComponent = + pPrevIcd->pNextDataComponent) != NULL) + { + pIcd->pNextDataComponent->pPrevDataComponent = pIcd; + } + else + { + pIxd->pLastData = pIcd; + } + pPrevIcd->pNextDataComponent = pIcd; + pIcd->pPrevDataComponent = pPrevIcd; + } + else + { + if ((pIcd->pNextDataComponent = pIxd->pFirstData) != NULL) + { + pIxd->pFirstData->pPrevDataComponent = pIcd; + } + else + { + pIxd->pLastData = pIcd; + } + pIxd->pFirstData = pIcd; + } + pIcd->uiDataComponent = uiDataComponent; + pIxd->uiNumDataComponents++; + + break; + + default: + + // Ignore all other attributes + + break; + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, &pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + // If they specified a name, see if we can find it + + if (puzName) + { + if (*puzName == '/' && *(puzName + 1) == 0) + { + uiNameDictNumber = ELM_ROOT_TAG; + + // For now, we can only allow presence indexing on + // ELM_ROOT_TAG. That is because we don't yet + // allow specifying of data type per ICD + + if (!(pIcd->uiFlags & ICD_PRESENCE)) + { + rc = RC_SET( NE_XFLM_MUST_INDEX_ON_PRESENCE); + goto Exit; + } + + // ELM_ROOT_TAG cannot be specified as a data + // component. + + if (pIcd->uiDataComponent) + { + rc = RC_SET( NE_XFLM_ILLEGAL_DATA_COMPONENT); + goto Exit; + } + } + else + { + F_DataVector searchKey; + F_DataVector dataPart; + + // Get the dictionary number - look up in the index + + if (RC_BAD( rc = searchKey.setUINT( 0, + uiElementId == ELM_ELEMENT_COMPONENT_TAG + ? ELM_ELEMENT_TAG + : ELM_ATTRIBUTE_TAG))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUnicode( 1, puzName))) + { + goto Exit; + } + if (puzNamespace) + { + if (RC_BAD( rc = searchKey.setUnicode( 2, puzNamespace))) + { + goto Exit; + } + } + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &dataPart))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + // This may be a built-in type which is not indexed. + + if ( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if ( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, + (bIsAttr) ? ELM_ATTRIBUTE_TAG : ELM_ELEMENT_TAG, + puzName, + NULL, + TRUE, + puzNamespace, + &uiNameDictNumber))) + { + if ( rc == NE_XFLM_NOT_FOUND) + { + rc = (RCODE)(bIsAttr + ? RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME) + : RC_SET( NE_XFLM_UNDEFINED_ELEMENT_NAME)); + } + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + // Data part of the retrieved key has the dictionary number + // for this collection. + + if (RC_BAD( rc = dataPart.getUINT( 3, &uiNameDictNumber))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + uiNameDictNumber = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + flmAssert( uiNameDictNumber); + } + + if (pIcd->uiDictNum && + uiNameDictNumber != pIcd->uiDictNum) + { + rc = (RCODE)(bIsAttr + ? RC_SET( NE_XFLM_ATTRIBUTE_NAME_MISMATCH) + : RC_SET( NE_XFLM_ELEMENT_NAME_MISMATCH)); + goto Exit; + } + pIcd->uiDictNum = uiNameDictNumber; + bHadDictNumber = TRUE; + } + + // If this is a key component, make a few more checks + + if (pIcd->uiKeyComponent) + { + + // If no limit was set, use a default. + + if (pIcd->uiFlags & ICD_SUBSTRING) + { + pIxd->uiFlags |= IXD_HAS_SUBSTRING; + } + if (!bLimitSet) + { + if (pIcd->uiFlags & ICD_SUBSTRING) + { + pIcd->uiLimit = ICD_DEFAULT_SUBSTRING_LIMIT; + } + else + { + pIcd->uiLimit = ICD_DEFAULT_LIMIT; + } + } + } + else + { + // There are certain things that cannot be set for data + // and context components. Verify that they were not set. + + if (bRequiredSet) + { + rc = RC_SET( NE_XFLM_CANNOT_SET_REQUIRED); + goto Exit; + } + if (bLimitSet) + { + rc = RC_SET( NE_XFLM_CANNOT_SET_LIMIT); + goto Exit; + } + if (bIndexOnSet) + { + rc = RC_SET( NE_XFLM_CANNOT_SET_INDEX_ON); + goto Exit; + } + if (bCompareRuleSet) + { + rc = RC_SET( NE_XFLM_CANNOT_SET_COMPARE_RULES); + goto Exit; + } + if (!pIcd->uiDataComponent) + { + + // Insert into its place in the list of context components + + pPrevIcd = NULL; + pTmpIcd = pIxd->pFirstContext; + while (pTmpIcd && pIcd->uiCdl > pTmpIcd->uiCdl) + { + pPrevIcd = pTmpIcd; + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + + // Link after pPrevIcd + + if (pPrevIcd) + { + if ((pIcd->pNextKeyComponent = + pPrevIcd->pNextKeyComponent) != NULL) + { + pIcd->pNextKeyComponent->pPrevKeyComponent = pIcd; + } + else + { + pIxd->pLastContext = pIcd; + } + pPrevIcd->pNextKeyComponent = pIcd; + pIcd->pPrevKeyComponent = pPrevIcd; + } + else + { + if ((pIcd->pNextKeyComponent = pIxd->pFirstContext) != NULL) + { + pIxd->pFirstContext->pPrevKeyComponent = pIcd; + } + else + { + pIxd->pLastContext = pIcd; + } + pIxd->pFirstContext = pIcd; + } + pIxd->uiNumContextComponents++; + } + } + + // Make sure we had an element or attribute number specified + + if (!bHadDictNumber) + { + rc = (RCODE)(bIsAttr + ? RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NUMBER) + : RC_SET( NE_XFLM_MISSING_ELEMENT_NUMBER)); + goto Exit; + } + + // Get the element or attribute's data type + + if (bIsAttr) + { + F_AttrElmInfo attrInfo; + + if (RC_BAD( rc = pDict->getAttribute( this, pIcd->uiDictNum, &attrInfo))) + { + goto Exit; + } + + uiDataType = attrInfo.m_uiDataType; + } + else + { + if (pIcd->uiDictNum == ELM_ROOT_TAG) + { + uiDataType = XFLM_NODATA_TYPE; + } + else + { + F_AttrElmInfo elmInfo; + + if (RC_BAD( rc = pDict->getElement( this, pIcd->uiDictNum, &elmInfo))) + { + goto Exit; + } + + uiDataType = elmInfo.m_uiDataType; + } + } + icdSetDataType( pIcd, uiDataType); + +Exit: + + if (pAttr) + { + pAttr->Release(); + } + + if (puzName) + { + f_free( &puzName); + } + + if (puzNamespace) + { + f_free( &puzNamespace); + } + + if (pNameTable) + { + pNameTable->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Determine if a NODE is an index component. +***************************************************************************/ +FSTATIC RCODE isIndexComponent( + F_Db * pDb, + F_DOMNode * pNode, + FLMBOOL * pbIsIndexComponent, + FLMUINT * puiElementNum) +{ + RCODE rc = NE_XFLM_OK; + + *pbIsIndexComponent = TRUE; + + if( pNode->getNodeType() != ELEMENT_NODE) + { + *pbIsIndexComponent = FALSE; + goto Exit; + } + + if( RC_BAD( rc = pNode->getNameId( pDb, puiElementNum))) + { + goto Exit; + } + + switch (*puiElementNum) + { + case ELM_ELEMENT_COMPONENT_TAG: + case ELM_ATTRIBUTE_COMPONENT_TAG: + break; + default: + *pbIsIndexComponent = FALSE; + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Compare the old index definition with the new to see if anything + changed - to determine if we really need to rebuild the index. +***************************************************************************/ +FSTATIC FLMBOOL indexDefsSame( + IXD * pOldIxd, + IXD * pNewIxd + ) +{ + FLMBOOL bSame = FALSE; + ICD * pOldIcd; + ICD * pNewIcd; + + if (pOldIxd->uiCollectionNum != pNewIxd->uiCollectionNum || + pOldIxd->uiNumIcds != pNewIxd->uiNumIcds || + pOldIxd->uiNumKeyComponents != pNewIxd->uiNumKeyComponents || + pOldIxd->uiNumDataComponents != pNewIxd->uiNumDataComponents || + ~(pOldIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) != + ~(pNewIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) || + pOldIxd->uiLanguage != pNewIxd->uiLanguage) + { + goto Exit; + } + + // Traverse the ICDs and make sure they are the same + + pOldIcd = pOldIxd->pIcdTree; + pNewIcd = pNewIxd->pIcdTree; + + for (;;) + { + + // Compare the ICDs + + if (pOldIcd->uiDictNum != pNewIcd->uiDictNum || + pOldIcd->uiFlags != pNewIcd->uiFlags || + pOldIcd->uiCdl != pNewIcd->uiCdl || + pOldIcd->uiKeyComponent != pNewIcd->uiKeyComponent || + pOldIcd->uiDataComponent != pNewIcd->uiDataComponent || + pOldIcd->uiLimit != pNewIcd->uiLimit) + { + goto Exit; + } + + if (pOldIcd->pFirstChild) + { + if (!pNewIcd->pFirstChild) + { + + // Old ICD has a child, new one doesn't, indexes are + // different. + + goto Exit; + } + pOldIcd = pOldIcd->pFirstChild; + pNewIcd = pNewIcd->pFirstChild; + continue; + } + while (pOldIcd && !pOldIcd->pNextSibling) + { + + // Old ICD has no next sibling, new one doesn't, indexes are + // different. + + if (!pNewIcd || pNewIcd->pNextSibling) + { + goto Exit; + } + pOldIcd = pOldIcd->pParent; + pNewIcd = pNewIcd->pParent; + } + if (!pOldIcd) + { + + // Traversed back to parent ICD for old ICD, but not + // for new ICD, indexes are different. However, this + // should never happen. + + if (pNewIcd) + { + flmAssert( 0); + goto Exit; + } + break; + } + + // OLD ICD has a sibling it can traverse to, new one + // doesn't, indexes are different. + + if (!pNewIcd || !pNewIcd->pNextSibling) + { + goto Exit; + } + pOldIcd = pOldIcd->pNextSibling; + pNewIcd = pNewIcd->pNextSibling; + } + + bSame = TRUE; + +Exit: + + return( bSame); +} + +/*************************************************************************** +Desc: Update an index definition. +***************************************************************************/ +RCODE F_Dict::updateIndexDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiIndexNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting + ) +{ + RCODE rc = NE_XFLM_OK; + void * pvMark = m_dictPool.poolMark(); + FLMUNICODE * puzIndexName = NULL; + F_DOMNode * pNode = NULL; + FLMUINT uiElementId; + FLMBOOL bIsIndexComponent; + FLMUINT uiComponentNum; + IXD * pIxd; + IXD * pOldIxd; + ICD * pIcd; + ICD * pLastIcd; + ICD * pTmpIcd; + FLMBOOL bLinkAsChild; + FLMBOOL bHadRequired; + FLMBOOL bSinglePath; + FLMBOOL bHasChildren; + FLMUINT uiEncId; + + if (bOpeningDict) + { + flmAssert( !bDeleting); + pOldIxd = NULL; + } + else + { + if (RC_BAD( rc = getIndex( uiIndexNum, NULL, &pOldIxd, TRUE))) + { + if (rc == NE_XFLM_BAD_IX) + { + pOldIxd = NULL; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (bDeleting) + { + flmAssert( uiIndexNum); + + if (pOldIxd) + { + + // Get rid of the old b-tree + + if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, NULL, &pOldIxd->lfInfo, + (FLMBOOL)((pOldIxd->uiFlags & IXD_ABS_POS) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE), + (FLMBOOL)(pOldIxd->pFirstData + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE)))) + { + goto Exit; + } + + // Remove from index fixup list if we are deleting an index. + // It is impossible to be deleting an index in a background + // thread, so if there is a fixup, it is here because the + // index was added during this transaction (in the background). + // If the transaction aborts the IXD will simply go away, and + // there will be no need to fix it up. + + if (pDb->m_pIxdFixups) + { + IXD_FIXUP * pIxdFixup = pDb->m_pIxdFixups; + IXD_FIXUP * pPrevIxdFixup = NULL; + + while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum) + { + pPrevIxdFixup = pIxdFixup; + pIxdFixup = pIxdFixup->pNext; + } + + if (pIxdFixup) + { + if (pPrevIxdFixup) + { + pPrevIxdFixup->pNext = pIxdFixup->pNext; + } + else + { + pDb->m_pIxdFixups = pIxdFixup->pNext; + } + f_free( &pIxdFixup); + } + } + + // On delete or modify index make sure something is in the stop list. + + if (!(pDb->m_uiFlags & FDB_REPLAYING_RFL)) + { + if( RC_BAD( rc = pDb->addToStopList( uiIndexNum))) + { + goto Exit; + } + } + + // Unlink the old ICDs. + + unlinkIcds( pOldIxd->pIcdTree); + } + + // NOTE: It is possible that the index may not be in the + // index table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + m_pNameTable->removeTag( ELM_INDEX_TAG, uiIndexNum); + + if (uiIndexNum >= m_uiLowestIxNum && uiIndexNum <= m_uiHighestIxNum) + { + m_ppIxdTbl [uiIndexNum - m_uiLowestIxNum] = NULL; + } + goto Exit; + } + + // Allocate a new IXD + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( IXD), (void **)&pIxd))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->getIndexDef( ui64DocumentID, &puzIndexName, + &pIxd->uiIndexNum, &pIxd->uiCollectionNum, + &pIxd->uiLanguage, &pIxd->uiFlags, + &pIxd->ui64LastDocIndexed, + &uiEncId, + &pNode, bOpeningDict, bDeleting))) + { + goto Exit; + } + if (!uiIndexNum) + { + uiIndexNum = pIxd->uiIndexNum; + } + else + { + flmAssert( uiIndexNum == pIxd->uiIndexNum); + } + pIxd->ui64IxDefNodeId = ui64DocumentID; + + // Process each sub-element, setting up the path for the index + // Start at the first child + + if (RC_BAD( rc = pNode->getFirstChild( pDb, (IF_DOMNode **)&pNode))) + { + + // Change not-found to illegal index definition - must be at + // least one subordinate node. + + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); + } + goto Exit; + } + + pLastIcd = NULL; + bLinkAsChild = TRUE; + bSinglePath = TRUE; + + for (;;) + { + if (RC_BAD( rc = isIndexComponent( pDb, pNode, &bIsIndexComponent, + &uiElementId))) + { + goto Exit; + } + if (bIsIndexComponent) + { + + // Allocate an ICD and link in + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD), + (void **)&pIcd))) + { + goto Exit; + } + pIcd->uiCdl = pIxd->uiNumIcds; + pIxd->uiNumIcds++; + pIcd->pIxd = pIxd; + pIcd->uiIndexNum = pIxd->uiIndexNum; + if (!pIxd->pIcdTree) + { + pIxd->pIcdTree = pIcd; + } + else if (bLinkAsChild) + { + + // link as child + + pLastIcd->pFirstChild = pIcd; + pIcd->pParent = pLastIcd; + if (pLastIcd->pPrevSibling) + { + bSinglePath = FALSE; + } + } + else + { + + // link as sibling + + pLastIcd->pNextSibling = pIcd; + if (pLastIcd->pFirstChild) + { + bSinglePath = FALSE; + } + pIcd->pPrevSibling = pLastIcd; + pIcd->pParent = pLastIcd->pParent; + } + bLinkAsChild = FALSE; + pLastIcd = pIcd; + if (RC_BAD( rc = pDb->getIndexComponentDef( this, pNode, + uiElementId, pIxd, pIcd))) + { + goto Exit; + } + + // The ICD with a tag of ELM_ROOT_TAG cannot be + // linked as a child to another node. It always + // has to be the lone root ICD. + + if (pIcd->uiDictNum == ELM_ROOT_TAG && + (pIcd->pParent || pIcd->pNextSibling || pIcd->pPrevSibling)) + { + rc = RC_SET( NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG); + goto Exit; + } + + // Make sure that this ICD does not have the same + // dictionary number as any prior sibling. + + pTmpIcd = pIcd->pPrevSibling; + while (pTmpIcd) + { + if (pIcd->uiDictNum == pTmpIcd->uiDictNum && + ((pIcd->uiFlags & ICD_IS_ATTRIBUTE) == + (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + rc = RC_SET( NE_XFLM_DUP_SIBLING_IX_COMPONENTS); + goto Exit; + } + pTmpIcd = pTmpIcd->pPrevSibling; + } + + // If the node was an attribute component, we are not interested in + // anything that might be subordinate to it - only siblings. + + if (uiElementId == ELM_ATTRIBUTE_COMPONENT_TAG) + { + if ( RC_BAD( rc = pNode->hasChildren( pDb, &bHasChildren))) + { + goto Exit; + } + + // Attribute components should not have children + + if ( bHasChildren) + { + rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); + goto Exit; + } + + goto Get_Sibling; + } + + // See if there is a child node + + if (RC_OK( rc = pNode->getFirstChild( pDb, (IF_DOMNode **)&pNode))) + { + bLinkAsChild = TRUE; + continue; + } + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // Fall through to see if there are any sibling nodes + + } + +Get_Sibling: + + if (RC_OK( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) + { + continue; + } + + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // No siblings, go to the parent node and see if it + // has a sibling + + if (!pLastIcd || (pLastIcd = pLastIcd->pParent) == NULL) + { + break; + } + bLinkAsChild = FALSE; + if (RC_BAD( rc = pNode->getParentNode( pDb, (IF_DOMNode **)&pNode))) + { + + // Should not be not-found - because we came down + // through a parent node. + + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + goto Get_Sibling; + } + + if (bSinglePath) + { + pIxd->uiFlags |= IXD_SINGLE_PATH; + } + + // Look at all of the key components. Verify that we have all of the + // needed components and that none are missing. If none are set to + // ICD_REQUIRED_PIECE, set them all to ICD_REQUIRED_IN_SET + + pIcd = pIxd->pFirstKey; + uiComponentNum = 1; + bHadRequired = FALSE; + while (pIcd) + { + if (pIcd->uiKeyComponent != uiComponentNum) + { + rc = RC_SET( NE_XFLM_MISSING_KEY_COMPONENT); + goto Exit; + } + if (pIcd->uiFlags & ICD_REQUIRED_PIECE) + { + pIcd->uiFlags |= ICD_REQUIRED_IN_SET; + bHadRequired = TRUE; + } + uiComponentNum++; + pIcd = pIcd->pNextKeyComponent; + } + + // If we don't have at least one key component, the index + // definition is invalid + + if (!pIxd->uiNumKeyComponents) + { + rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_DEF); + goto Exit; + } + + // If none of the key components were marked as required, mark + // them all as ICD_REQUIRED_IN_SET. + + if (!bHadRequired) + { + pIcd = pIxd->pFirstKey; + while (pIcd) + { + pIcd->uiFlags |= ICD_REQUIRED_IN_SET; + pIcd = pIcd->pNextKeyComponent; + } + } + + // Look at all of the data components. Verify that we have all of the + // needed components and that none are missing. + + pIcd = pIxd->pFirstData; + uiComponentNum = 1; + while (pIcd) + { + if (pIcd->uiDataComponent != uiComponentNum) + { + rc = RC_SET( NE_XFLM_MISSING_DATA_COMPONENT); + goto Exit; + } + uiComponentNum++; + pIcd = pIcd->pNextDataComponent; + } + + // Look at all of the leaf ICDs. They must be data components or + // key components. + + pIcd = pIxd->pFirstContext; + while (pIcd) + { + + // Context components cannot be leaf components - only data + // and key components can be at the leaf. + + if (!pIcd->pFirstChild) + { + rc = RC_SET( NE_XFLM_ILLEGAL_INDEX_COMPONENT); + goto Exit; + } + pIcd = pIcd->pNextKeyComponent; + } + + // Add to the tag table, unless we already have our quota of + // index names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_INDEX_TAG, pIxd->uiIndexNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_INDEX_TAG, puzIndexName, NULL, + pIxd->uiIndexNum, 0, NULL, 0, + bOpeningDict ? FALSE : TRUE))) + { + goto Exit; + } + + // If we are not opening the dictionary and the indexes are + // the same, no need to change the index out + + if (!bOpeningDict && pOldIxd && indexDefsSame( pOldIxd, pIxd)) + { + + // Discard the new IXD, it is not needed. + + m_dictPool.poolReset( pvMark); + } + else + { + if (!bOpeningDict) + { + if (pOldIxd) + { + + // If modifying make sure something is in the stop list. + + if (!(pDb->m_uiFlags & FDB_REPLAYING_RFL)) + + { + if( RC_BAD( rc = pDb->addToStopList( uiIndexNum))) + { + goto Exit; + } + } + + // Delete the old b-tree LFILE + + if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, + NULL, &pOldIxd->lfInfo, + (FLMBOOL)((pOldIxd->uiFlags & IXD_ABS_POS) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE), + (FLMBOOL)(pOldIxd->pFirstData + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE)))) + { + goto Exit; + } + } + + // Create a NEW LFILE for the index. If this is a new index + // definition, we have not yet created one. If this is a + // modified index definition, the old LFILE would have been + // deleted up above. + + if (RC_BAD( rc = pDb->m_pDatabase->lFileCreate( pDb, &pIxd->lfInfo, + NULL, uiIndexNum, XFLM_LF_INDEX, + (FLMBOOL)((pIxd->uiFlags & IXD_ABS_POS) + ? TRUE + : FALSE), + (FLMBOOL)(pIxd->pFirstData + ? TRUE + : FALSE), + uiEncId))) + { + goto Exit; + } + } + + // Make room in the table for the new index if necessary. + + if (pIxd->uiIndexNum < m_uiLowestIxNum || + pIxd->uiIndexNum > m_uiHighestIxNum) + { + if (RC_BAD( rc = reallocTbl( pIxd->uiIndexNum, sizeof( IXD *), + (void **)&m_ppIxdTbl, + &m_uiLowestIxNum, + &m_uiHighestIxNum, 20, + XFLM_MAX_INDEX_NUM))) + { + goto Exit; + } + } + + // Link the new ICDs into their ICD chains and unlink the old ICDs + // from their ICD chains. We don't want to do either of these until + // we are sure we are going to succeed. + + m_ppIxdTbl [pIxd->uiIndexNum - m_uiLowestIxNum] = pIxd; + if (RC_BAD( rc = linkIcds( pIxd->pIcdTree))) + { + goto Exit; + } + + if (pOldIxd) + { + + // Unlink the old ICDs. + + unlinkIcds( pOldIxd->pIcdTree); + } + + // Build the index, unless we are just opening the dictionary + + if (!bOpeningDict) + { + if (RC_BAD( rc = pDb->buildIndex( uiIndexNum, pIxd->uiFlags))) + { + goto Exit; + } + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (puzIndexName) + { + f_free( &puzIndexName); + } + + if (RC_BAD( rc)) + { + m_dictPool.poolReset( pvMark); + } + + return( rc); +} + +/*************************************************************************** +Desc: Retrieve a collection definition - get name and number. +***************************************************************************/ +RCODE F_Db::getCollectionDef( + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT * puiEncId + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode; + IF_DOMNode * pAttr = NULL; + FLMBOOL bHadCollectionNumber = FALSE; + FLMBOOL bHadCollectionName = FALSE; + FLMUINT uiNameId; + + // Set up defaults + + if (ppuzCollectionName) + { + *ppuzCollectionName = NULL; + } + if (puiCollectionNumber) + { + *puiCollectionNumber = 0; + } + if (puiEncId) + { + *puiEncId = 0; + } + + // Retrieve the root element of the collection definition + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) + { + goto Exit; + } + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + + pCachedNode = pNode->m_pCachedNode; + + if( !pCachedNode->hasAttributes()) + { + rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NUMBER); + goto Exit; + } + + if( RC_BAD( rc = pNode->getFirstAttribute( this, &pAttr))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch( uiNameId) + { + case ATTR_NAME_TAG: + if (ppuzCollectionName) + { + if (RC_BAD( rc = pAttr->getUnicode( this, ppuzCollectionName))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalCollectionName( + *ppuzCollectionName))) + { + goto Exit; + } + } + bHadCollectionName = TRUE; + break; + + case ATTR_DICT_NUMBER_TAG: + if (puiCollectionNumber) + { + if (RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalCollectionNumber( + *puiCollectionNumber))) + { + goto Exit; + } + } + bHadCollectionNumber = TRUE; + break; + + case ATTR_ENCRYPTION_ID_TAG: + if (puiEncId) + { + if (RC_BAD( rc = pAttr->getUINT( this, puiEncId))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalEncDefNumber( + *puiEncId))) + { + goto Exit; + } + } + break; + + default: + + // Ignore all other attributes + + break; + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, &pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + // Make sure we had both a name and number specified + + if (!bHadCollectionName) + { + rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NAME); + goto Exit; + } + if (!bHadCollectionNumber) + { + rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NUMBER); + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pAttr) + { + pAttr->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Parse a data dictionary collection definition. +***************************************************************************/ +RCODE F_Dict::updateCollectionDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiCollectionNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting + ) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + F_COLLECTION * pOldCollection; + FLMUNICODE * puzCollectionName = NULL; + FLMUINT uiTmp; + FLMUINT uiEncId; + + if (bOpeningDict) + { + flmAssert( !bDeleting); + pOldCollection = NULL; + } + else + { + if (RC_BAD( rc = getCollection( uiCollectionNum, &pOldCollection))) + { + if (rc == NE_XFLM_BAD_COLLECTION) + { + pOldCollection = NULL; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (bDeleting) + { + flmAssert( uiCollectionNum); + + if (pOldCollection) + { + if (RC_BAD( rc = pDb->m_pDatabase->lFileDelete( pDb, + pOldCollection, &pOldCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + } + + pDb->removeCollectionNodes( uiCollectionNum, + pDb->m_ui64CurrTransID); + + // NOTE: It is possible that the collection may not be in the + // collection table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + m_pNameTable->removeTag( ELM_COLLECTION_TAG, uiCollectionNum); + + if (uiCollectionNum >= m_uiLowestCollectionNum && + uiCollectionNum <= m_uiHighestCollectionNum) + { + m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum] = NULL; + } + goto Exit; + } + + // Allocate a new collection + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_COLLECTION), + (void **)&pCollection))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->getCollectionDef( ui64DocumentID, + &puzCollectionName, &uiTmp, &uiEncId))) + { + goto Exit; + } + if (!uiCollectionNum) + { + uiCollectionNum = uiTmp; + } + else + { + flmAssert( uiCollectionNum == uiTmp); + } + + if (!bOpeningDict) + { + // If this is not a new collection, get the LFILE info + // for new collection we just allocated. + + if (pOldCollection) + { + f_memcpy( pCollection, pOldCollection, sizeof( F_COLLECTION)); + } + else + { + flmAssert( !bDeleting); + + // Create a NEW LFILE for the collection + + if (RC_BAD( rc = pDb->m_pDatabase->lFileCreate( pDb, + &pCollection->lfInfo, + pCollection, uiCollectionNum, + XFLM_LF_COLLECTION, + FALSE, TRUE, uiEncId))) + { + goto Exit; + } + } + } + + // Add to the tag table, unless we already have our quota of + // collection names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_COLLECTION_TAG, uiCollectionNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_COLLECTION_TAG, puzCollectionName, + NULL, uiCollectionNum, 0, NULL, 0, + bOpeningDict ? FALSE : TRUE))) + { + if (rc == NE_XFLM_EXISTS) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if (uiCollectionNum < m_uiLowestCollectionNum || + uiCollectionNum > m_uiHighestCollectionNum) + { + if (RC_BAD( rc = reallocTbl( uiCollectionNum, sizeof( F_COLLECTION *), + (void **)&m_ppCollectionTbl, + &m_uiLowestCollectionNum, + &m_uiHighestCollectionNum, 20, + XFLM_MAX_COLLECTION_NUM))) + { + goto Exit; + } + } + m_ppCollectionTbl [uiCollectionNum - m_uiLowestCollectionNum] = pCollection; + +Exit: + + if (puzCollectionName) + { + f_free( &puzCollectionName); + } + + return( rc); +} + +/*************************************************************************** +Desc: Retrieve a prefix definition - get name and number. +***************************************************************************/ +RCODE F_Db::getPrefixDef( + F_Dict * pDict, + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzPrefixName, + FLMUINT * puiPrefixNumber) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode; + IF_DOMNode * pAttr = NULL; + FLMBOOL bHadPrefixNumber = FALSE; + FLMBOOL bHadPrefixName = FALSE; + FLMUINT uiNameId; + + // Set up defaults + + if (ppuzPrefixName) + { + *ppuzPrefixName = NULL; + } + + if (puiPrefixNumber) + { + *puiPrefixNumber = 0; + } + + // Retrieve the root element of the prefix definition + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) + { + goto Exit; + } + + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + pCachedNode = pNode->m_pCachedNode; + + if( !pCachedNode->hasAttributes()) + { + rc = RC_SET( NE_XFLM_MISSING_PREFIX_NUMBER); + goto Exit; + } + + if( RC_BAD( rc = pNode->getFirstAttribute( this, &pAttr))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch( uiNameId) + { + case ATTR_NAME_TAG: + { + FLMUINT uiPrefixLen; + FLMUINT uiBufferSize; + + if (ppuzPrefixName) + { + if( RC_BAD( rc = pAttr->getUnicodeChars( this, &uiPrefixLen))) + { + goto Exit; + } + + uiBufferSize = sizeof( FLMUNICODE) * (uiPrefixLen + 1); + if( RC_BAD( rc = pDict->m_dictPool.poolAlloc( uiBufferSize, + (void **)ppuzPrefixName))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUnicode( this, *ppuzPrefixName, + uiBufferSize, 0, uiPrefixLen, NULL))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalPrefixName( *ppuzPrefixName))) + { + goto Exit; + } + } + bHadPrefixName = TRUE; + break; + } + + case ATTR_DICT_NUMBER_TAG: + { + if (puiPrefixNumber) + { + if (RC_BAD( rc = pAttr->getUINT( this, puiPrefixNumber))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalPrefixNumber( + *puiPrefixNumber))) + { + goto Exit; + } + } + bHadPrefixNumber = TRUE; + break; + } + + default: + { + // Ignore all other attributes + + break; + } + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + // Make sure we had both a name and number specified + + if (!bHadPrefixName) + { + rc = RC_SET( NE_XFLM_MISSING_PREFIX_NAME); + goto Exit; + } + if (!bHadPrefixNumber) + { + rc = RC_SET( NE_XFLM_MISSING_PREFIX_NUMBER); + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pAttr) + { + pAttr->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Parse a data dictionary prefix definition. +***************************************************************************/ +RCODE F_Dict::updatePrefixDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiPrefixNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting) +{ + RCODE rc = NE_XFLM_OK; + F_PREFIX * pPrefix = NULL; + F_PREFIX * pOldPrefix = NULL; + FLMUNICODE * puzPrefixName = NULL; + FLMUINT uiTmp; + void * pvMark = m_dictPool.poolMark(); + + if (bOpeningDict) + { + flmAssert( !bDeleting); + pOldPrefix = NULL; + } + else + { + if (RC_BAD( rc = getPrefix( uiPrefixNum, &pOldPrefix))) + { + if (rc == NE_XFLM_BAD_PREFIX) + { + pOldPrefix = NULL; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (bDeleting) + { + flmAssert( uiPrefixNum); + + // NOTE: It is possible that the prefix may not be in the + // collection table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + m_pNameTable->removeTag( ELM_PREFIX_TAG, uiPrefixNum); + + if (uiPrefixNum >= m_uiLowestPrefixNum && + uiPrefixNum <= m_uiHighestPrefixNum) + { + m_ppPrefixTbl [uiPrefixNum - m_uiLowestPrefixNum] = NULL; + } + goto Exit; + } + + // Allocate a new prefix + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_PREFIX), + (void **)&pPrefix))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->getPrefixDef( this, ui64DocumentID, + &puzPrefixName, &uiTmp))) + { + goto Exit; + } + + if (!uiPrefixNum) + { + uiPrefixNum = uiTmp; + } + else + { + flmAssert( uiPrefixNum == uiTmp); + } + + // Add to the tag table, unless we already have our quota of + // prefix names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_PREFIX_TAG, uiPrefixNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_PREFIX_TAG, puzPrefixName, + NULL, uiPrefixNum, 0, NULL, 0, + bOpeningDict ? FALSE : TRUE))) + { + goto Exit; + } + + if (uiPrefixNum < m_uiLowestPrefixNum || + uiPrefixNum > m_uiHighestPrefixNum) + { + if (RC_BAD( rc = reallocTbl( uiPrefixNum, sizeof( F_PREFIX *), + (void **)&m_ppPrefixTbl, + &m_uiLowestPrefixNum, + &m_uiHighestPrefixNum, 20, + XFLM_MAX_PREFIX_NUM))) + { + goto Exit; + } + } + + pPrefix->ui64PrefixId = uiPrefixNum; + pPrefix->puzPrefixName = puzPrefixName; + m_ppPrefixTbl [uiPrefixNum - m_uiLowestPrefixNum] = pPrefix; + +Exit: + + if( RC_BAD( rc)) + { + if( pvMark) + { + m_dictPool.poolReset( pvMark); + } + } + + return( rc ); +} + +/*************************************************************************** +Desc: Retrieve an encDef definition - get name and number and key +***************************************************************************/ +RCODE F_Db::getEncDefDef( + F_Dict * pDict, + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzEncDefName, + FLMUINT * puiEncDefNumber, + FLMUINT * puiEncDefKeySize, + F_CCS ** ppCcs + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode; + F_DOMNode * pAttr = NULL; + FLMBOOL bHadEncDefNumber = FALSE; + FLMBOOL bHadEncDefName = FALSE; + FLMBOOL bHadEncKey = FALSE; + FLMBOOL bHadEncKeySize = FALSE; + FLMBOOL bHadAlgorithm = FALSE; + FLMUINT uiNameId; + void * pvEncKeyBuf = NULL; + FLMUINT32 ui32EncKeyLen; + FLMBOOL bStartedUpdateTrans = FALSE; + FLMBOOL bRestartReadTrans = FALSE; + FLMBYTE * pszAlgorithm = NULL; + FLMUINT uiEncType = 0; + FLMUINT uiEncKeySize = 0; + + // Set up defaults + + if (ppuzEncDefName) + { + *ppuzEncDefName = NULL; + } + + if (puiEncDefNumber) + { + *puiEncDefNumber = 0; + } + + flmAssert( ppCcs); + if (*ppCcs) + { + (*ppCcs)->Release(); + } + *ppCcs = NULL; + + // Retrieve the root element of the encDef definition + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) + { + goto Exit; + } + + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + pCachedNode = pNode->m_pCachedNode; + + if( !pCachedNode->hasAttributes()) + { + rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NUMBER); + goto Exit; + } + + if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + // Cycle through the attributes + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch( uiNameId) + { + case ATTR_NAME_TAG: + { + FLMUINT uiEncDefLen; + FLMUINT uiBufferSize; + + if (ppuzEncDefName) + { + if( RC_BAD( rc = pAttr->getUnicodeChars( this, &uiEncDefLen))) + { + goto Exit; + } + + uiBufferSize = sizeof( FLMUNICODE) * (uiEncDefLen + 1); + if( RC_BAD( rc = pDict->m_dictPool.poolAlloc( uiBufferSize, + (void **)ppuzEncDefName))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUnicode( this, *ppuzEncDefName, + uiBufferSize, 0, uiEncDefLen, NULL))) + { + goto Exit; + } + + if (RC_BAD( rc = fdictLegalEncDefName( *ppuzEncDefName))) + { + goto Exit; + } + } + bHadEncDefName = TRUE; + break; + } + + case ATTR_DICT_NUMBER_TAG: + { + if (puiEncDefNumber) + { + if (RC_BAD( rc = pAttr->getUINT( this, puiEncDefNumber))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalEncDefNumber( + *puiEncDefNumber))) + { + goto Exit; + } + } + bHadEncDefNumber = TRUE; + break; + } + + case ATTR_ENCRYPTION_KEY_TAG: + { + FLMUINT uiBytesReturned; + + if( RC_BAD( rc = pAttr->getDataLength( this, (FLMUINT *)&ui32EncKeyLen))) + { + goto Exit; + } + + if( !ui32EncKeyLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = f_alloc( ui32EncKeyLen, &pvEncKeyBuf))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getBinary( this, pvEncKeyBuf, + 0, ui32EncKeyLen, &uiBytesReturned))) + { + goto Exit; + } + bHadEncKey = TRUE; + break; + } + + case ATTR_ENCRYPTION_KEY_SIZE_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, &uiEncKeySize))) + { + goto Exit; + } + + // Note: Will validate the key size when we finish the loop. + bHadEncKeySize = TRUE; + break; + } + + case ATTR_TYPE_TAG: + { + // Get the encryption Algorithm + + if( RC_BAD( rc = pAttr->getUTF8( this, &pszAlgorithm))) + { + goto Exit; + } + if (RC_BAD( rc = fdictLegalEncDefType( + (char *)pszAlgorithm, &uiEncType))) + { + goto Exit; + } + + bHadAlgorithm = TRUE; + break; + } + + default: + { + // Ignore all other attributes + + break; + } + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + // Make sure we had both a name and number specified + + if (!bHadEncDefName) + { + rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NAME); + goto Exit; + } + if (!bHadEncDefNumber) + { + rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NUMBER); + goto Exit; + } + + if (bHadAlgorithm && bHadEncKeySize) + { + if (RC_BAD( rc = fdictLegalEncKeySize( uiEncType, uiEncKeySize))) + { + goto Exit; + } + } + else if (!bHadAlgorithm) + { + rc = RC_SET( NE_XFLM_MISSING_ENC_ALGORITHM); + goto Exit; + } + + if (bHadEncKey) + { + if ((*ppCcs = f_new( F_CCS)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = (*ppCcs)->init( FALSE, uiEncType))) + { + goto Exit; + } + + if( RC_BAD( rc = (*ppCcs)->setKeyFromStore( + (FLMBYTE *)pvEncKeyBuf, NULL, m_pDatabase->m_pWrappingKey))) + { + goto Exit; + } + } + else + { + if (!bHadEncKeySize) + { + // Pick a key size based on the encryption algorithm. + (void)fdictGetLegalKeySize( uiEncType, &uiEncKeySize); + + } + + // This must be a new encryption definition that doesn't have a key yet. + // Generate a new encryption key and save it in the DOM node document. + + // We will need an update transaction before we can proceed. + + if ( getTransType() == XFLM_READ_TRANS) + { + // End this transaction + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + bRestartReadTrans = TRUE; + } + + if ( getTransType() == XFLM_NO_TRANS) + { + if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedUpdateTrans = TRUE; + } + + if ((*ppCcs = f_new( F_CCS)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = (*ppCcs)->init( FALSE, uiEncType))) + { + goto Exit; + } + +TryNewKeySize: + + if (RC_BAD( rc = (*ppCcs)->generateEncryptionKey( uiEncKeySize))) + { + if (!fdictGetLegalKeySize( uiEncType, &uiEncKeySize)) + { + goto Exit; + } + rc = NE_XFLM_OK; + goto TryNewKeySize; + } + + if (RC_BAD( rc = (*ppCcs)->getKeyToStore( + (FLMBYTE **)&pvEncKeyBuf, &ui32EncKeyLen, NULL, + m_pDatabase->m_pWrappingKey))) + { + goto Exit; + } + + // Set the key in the DOM node as a binary string. + if (RC_BAD( rc = pNode->createAttribute( this, + ATTR_ENCRYPTION_KEY_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setBinary( this, pvEncKeyBuf, ui32EncKeyLen))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if (!bHadEncKeySize) + { + // Set the key size + if (RC_BAD( rc = pNode->createAttribute( this, + ATTR_ENCRYPTION_KEY_SIZE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( this, uiEncKeySize))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pNode->getAttribute( this, + ATTR_ENCRYPTION_KEY_SIZE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + } + + if (RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // End the transaction + if (bStartedUpdateTrans) + { + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + bStartedUpdateTrans = FALSE; + } + + } + + if (puiEncDefKeySize) + { + *puiEncDefKeySize = uiEncKeySize; + } + +Exit: + + if (RC_BAD( rc)) + { + if (*ppCcs) + { + (*ppCcs)->Release(); + *ppCcs = NULL; + } + } + + if( bStartedUpdateTrans) + { + if( RC_OK( rc)) + { + // Commit the update transaction + + if (RC_BAD( rc = transCommit())) + { + (void)transAbort(); + } + } + else + { + (void)transAbort(); + } + } + + if( bRestartReadTrans) + { + rc = transBegin( XFLM_READ_TRANS); + } + + if( pNode) + { + pNode->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( pvEncKeyBuf) + { + f_free( &pvEncKeyBuf); + } + + if( pszAlgorithm) + { + f_free( &pszAlgorithm); + } + + return( rc); +} + +/*************************************************************************** +Desc: Parse a data dictionary encryption definition. +***************************************************************************/ +RCODE F_Dict::updateEncDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiEncDefNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting) +{ + RCODE rc = NE_XFLM_OK; + F_ENCDEF * pEncDef = NULL; + F_ENCDEF * pOldEncDef; + FLMUNICODE * puzEncDefName = NULL; + FLMUINT uiTmp; + void * pvMark = m_dictPool.poolMark(); + F_CCS * pCcs = NULL; + FLMUINT uiEncKeySize = 0; + + if (bOpeningDict) + { + flmAssert( !bDeleting); + pOldEncDef = NULL; + } + else + { + if (RC_BAD( rc = getEncDef( uiEncDefNum, &pOldEncDef))) + { + if (rc == NE_XFLM_BAD_ENCDEF_NUM) + { + pOldEncDef = NULL; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (bDeleting) + { + flmAssert( uiEncDefNum); + + // NOTE: It is possible that the encdef may not be in the + // collection table yet, because dictDocumentDone had not been + // called to put it in there, but we are calling dictDocumentDone + // to remove it. + + // Remove the tag number from the name table + + // VISIT: Before we can delete the encryption definition tag, we need + // to be sure it is not being used. Take a look at the collection tag + // to see how they are controlled. + + m_pNameTable->removeTag( ELM_ENCDEF_TAG, uiEncDefNum); + + if (uiEncDefNum >= m_uiLowestEncDefNum && + uiEncDefNum <= m_uiHighestEncDefNum) + { + m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] = NULL; + } + goto Exit; + } + + if (!pOldEncDef) + { + // Allocate a new encdef + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_ENCDEF), + (void **)&pEncDef))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->getEncDefDef( this, ui64DocumentID, + &puzEncDefName, &uiTmp, &uiEncKeySize, &pCcs))) + { + goto Exit; + } + + if (!uiEncDefNum) + { + uiEncDefNum = uiTmp; + } + else + { + flmAssert( uiEncDefNum == uiTmp); + } + + // Add to the tag table, unless we already have our quota of + // encryption definition names. Remove the tag by number first, in case + // it was renamed. + + if (!bOpeningDict) + { + m_pNameTable->removeTag( ELM_ENCDEF_TAG, uiEncDefNum); + } + + if (RC_BAD( rc = m_pNameTable->addTag( ELM_ENCDEF_TAG, puzEncDefName, + NULL, uiEncDefNum, 0, NULL, 0, + bOpeningDict ? FALSE : TRUE))) + { + goto Exit; + } + + if (uiEncDefNum < m_uiLowestEncDefNum || + uiEncDefNum > m_uiHighestEncDefNum) + { + if (RC_BAD( rc = reallocTbl( uiEncDefNum, sizeof( F_ENCDEF *), + (void **)&m_ppEncDefTbl, + &m_uiLowestEncDefNum, + &m_uiHighestEncDefNum, 20, + XFLM_MAX_ENCDEF_NUM))) + { + goto Exit; + } + } + + flmAssert(m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] == NULL); + + pEncDef->ui64EncDefId = uiEncDefNum; + pEncDef->ui64DocumentId = ui64DocumentID; + pEncDef->puzEncDefName = puzEncDefName; + pEncDef->uiEncKeySize = uiEncKeySize; + pEncDef->pCcs = pCcs; + pEncDef->pCcs->AddRef(); + m_ppEncDefTbl [uiEncDefNum - m_uiLowestEncDefNum] = pEncDef; + } + + +Exit: + + if( RC_BAD( rc)) + { + if( pvMark) + { + m_dictPool.poolReset( pvMark); + } + } + + if (pCcs) + { + pCcs->Release(); + } + + return( rc ); +} + +/*************************************************************************** +Desc: Update a definition in the in-memory dictionary - add, modify, or + delete it. +***************************************************************************/ +RCODE F_Dict::updateDict( + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT64 ui64DocumentID, + FLMUINT uiDictNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting) +{ + RCODE rc = NE_XFLM_OK; + +#ifdef FLM_DEBUG + if (!bOpeningDict) + { + flmAssert( pDb->m_uiFlags & FDB_UPDATED_DICTIONARY); + } +#endif + + flmAssert( !pDb->m_pDatabase->m_pRfl || + !pDb->m_pDatabase->m_pRfl->isLoggingEnabled() || + pDb->getTransType() == XFLM_READ_TRANS); + + // Commit any keys in the KREF buffers. + + if (RC_BAD( rc = pDb->keysCommit( FALSE))) + { + goto Exit; + } + + switch (uiDictType) + { + case ELM_ELEMENT_TAG: + if (RC_BAD( rc = updateElementDef( pDb, ui64DocumentID, + uiDictNumber, bOpeningDict, bDeleting))) + { + goto Exit; + } + break; + + case ELM_ATTRIBUTE_TAG: + if (RC_BAD( rc = updateAttributeDef( pDb, ui64DocumentID, + uiDictNumber, bOpeningDict, bDeleting))) + { + goto Exit; + } + break; + + case ELM_INDEX_TAG: + if (RC_BAD( rc = updateIndexDef( pDb, ui64DocumentID, + uiDictNumber, bOpeningDict, bDeleting))) + { + goto Exit; + } + break; + + case ELM_COLLECTION_TAG: + if (RC_BAD( rc = updateCollectionDef( pDb, + ui64DocumentID, uiDictNumber, + bOpeningDict, bDeleting))) + { + goto Exit; + } + break; + + case ELM_PREFIX_TAG: + if (RC_BAD( rc = updatePrefixDef( pDb, + ui64DocumentID, uiDictNumber, + bOpeningDict, bDeleting))) + { + goto Exit; + } + break; + + case ELM_ENCDEF_TAG: + if (!pDb->m_pDatabase->m_bInLimitedMode) + { + if (RC_BAD( rc = updateEncDef( pDb, + ui64DocumentID, uiDictNumber, + bOpeningDict, bDeleting))) + { + goto Exit; + } + } + break; + + default: + + // May be other things in the dictionary that we don't care + // about + + break; + + } + + // Commit any keys in the KREF buffers. + + if (RC_BAD( rc = pDb->keysCommit( FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup the predefined collections and indexes. +****************************************************************************/ +RCODE F_Dict::setupPredefined( + FLMUINT uiDefaultLanguage) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + ICD * pIcd; + FLMUINT uiLoop; + ATTR_ELM_DEF * pElementDef; + ATTR_ELM_DEF * pAttributeDef; + + // Set up reserved elements table + + if (RC_BAD( rc = f_calloc( (XFLM_LAST_RESERVED_ELEMENT_TAG - + XFLM_FIRST_RESERVED_ELEMENT_TAG + 1) * + sizeof( ATTR_ELM_DEF), + &m_pReservedElementDefTbl))) + { + goto Exit; + } + + // Set up reserved attributes table + + if (RC_BAD( rc = f_calloc( (XFLM_LAST_RESERVED_ATTRIBUTE_TAG - + XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1) * + sizeof( ATTR_ELM_DEF), + &m_pReservedAttributeDefTbl))) + { + goto Exit; + } + + // Set the data types and state to active for all reserved elements and + // attributes. + + for (uiLoop = 0; FlmReservedElementTags [uiLoop].pszTagName; uiLoop++) + { + pElementDef = &m_pReservedElementDefTbl [ + FlmReservedElementTags [uiLoop].uiTagNum - + XFLM_FIRST_RESERVED_ELEMENT_TAG]; + pElementDef->uiFlags = + (FlmReservedElementTags [uiLoop].uiDataType & + ATTR_ELM_DATA_TYPE_MASK) | + (ATTR_ELM_STATE_ACTIVE & ATTR_ELM_STATE_MASK); + } + + for (uiLoop = 0; FlmReservedAttributeTags [uiLoop].pszTagName; uiLoop++) + { + FLMUINT uiTagNum = FlmReservedAttributeTags [uiLoop].uiTagNum; + + pAttributeDef = &m_pReservedAttributeDefTbl[ uiTagNum - + XFLM_FIRST_RESERVED_ATTRIBUTE_TAG]; + pAttributeDef->uiFlags = + (FlmReservedAttributeTags [uiLoop].uiDataType & + ATTR_ELM_DATA_TYPE_MASK) | + (ATTR_ELM_STATE_ACTIVE & ATTR_ELM_STATE_MASK); + + // Special case for ATTR_XMLNS_XFLAIM_TAG and ATTR_XMLNS_TAG. + // These attributes allow applications to use "xmlns:xflaim" or + // "xmlns" in dictionary definitions without requiring the need + // to explicitly create an attribute definition for the "xmlns:xflaim" + // or "xmlns" attributes. + + if( uiTagNum == ATTR_XMLNS_XFLAIM_TAG || uiTagNum == ATTR_XMLNS_TAG) + { + pAttributeDef->uiFlags |= ATTR_ELM_NS_DECL; + } + } + + // Allocate memory for the predefined collections + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( F_COLLECTION) * 3, + (void **)&m_pDictCollection))) + { + goto Exit; + } + + m_pDataCollection = &m_pDictCollection[ 1]; + m_pMaintCollection = &m_pDictCollection[ 2]; + + + m_pDictCollection->lfInfo.uiLfNum = XFLM_DICT_COLLECTION; + m_pDictCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; + + m_pDataCollection->lfInfo.uiLfNum = XFLM_DATA_COLLECTION; + m_pDataCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; + + m_pMaintCollection->lfInfo.uiLfNum = XFLM_MAINT_COLLECTION; + m_pMaintCollection->lfInfo.eLfType = XFLM_LF_COLLECTION; + + // Allocate IXDs for the predefined indexes. + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( IXD) * 2, + (void **)&m_pNameIndex))) + { + goto Exit; + } + m_pNumberIndex = &m_pNameIndex [1]; + + // Initialize the name index IXD + + pIxd = m_pNameIndex; + pIxd->uiIndexNum = XFLM_DICT_NAME_INDEX; + pIxd->uiCollectionNum = XFLM_DICT_COLLECTION; +// pIxd->pIcdTree = NULL; // Set by poolCalloc +// pIxd->pFirstKey = NULL; // Set by poolCalloc +// pIxd->pLastKey = NULL; // Set by poolCalloc +// pIxd->pFirstContext = NULL; // Set by poolCalloc +// pIxd->pLastContext = NULL; // Set by poolCalloc +// pIxd->pFirstData = NULL; // Set by poolCalloc +// pIxd->pLastData = NULL; // Set by poolCalloc + pIxd->uiNumIcds = 4; + pIxd->uiNumKeyComponents = 3; + pIxd->uiNumDataComponents = 1; +// pIxd->uiNumContextComponents = 0; // Set by poolCalloc + pIxd->uiFlags = IXD_SINGLE_PATH; + pIxd->uiLanguage = uiDefaultLanguage; + pIxd->ui64LastDocIndexed = ~((FLMUINT64)0); + pIxd->lfInfo.uiLfNum = XFLM_DICT_NAME_INDEX; + pIxd->lfInfo.eLfType = XFLM_LF_INDEX; + + // Initialize the number index IXD + + pIxd = m_pNumberIndex; + pIxd->uiIndexNum = XFLM_DICT_NUMBER_INDEX; + pIxd->uiCollectionNum = XFLM_DICT_COLLECTION; +// pIxd->pIcdTree = NULL; // Set by poolCalloc +// pIxd->pFirstKey = NULL; // Set by poolCalloc +// pIxd->pLastKey = NULL; // Set by poolCalloc +// pIxd->pFirstContext = NULL; // Set by poolCalloc +// pIxd->pLastContext = NULL; // Set by poolCalloc +// pIxd->pFirstData = NULL; // Set by poolCalloc +// pIxd->pLastData = NULL; // Set by poolCalloc + pIxd->uiNumIcds = 2; + pIxd->uiNumKeyComponents = 2; +// pIxd->uiNumDataComponents = 0; // Set by poolCalloc +// pIxd->uiNumContextComponents = 0; // Set by poolCalloc + pIxd->uiFlags = IXD_SINGLE_PATH; + pIxd->uiLanguage = uiDefaultLanguage; + pIxd->ui64LastDocIndexed = ~((FLMUINT64)0); + pIxd->lfInfo.uiLfNum = XFLM_DICT_NUMBER_INDEX; + pIxd->lfInfo.eLfType = XFLM_LF_INDEX; + + // Set up the ICDs for the name index + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD) * 4, + (void **)&m_pNameIndex->pIcdTree))) + { + goto Exit; + } + + pIcd = m_pNameIndex->pIcdTree; + m_pNameIndex->pFirstKey = pIcd; + pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; + pIcd->pIxd = m_pNameIndex; + pIcd->uiDictNum = ELM_ROOT_TAG; + pIcd->uiFlags = ICD_PRESENCE | ICD_REQUIRED_PIECE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc +// pIcd->pParent = NULL; // Set by poolCalloc + pIcd->pFirstChild = pIcd + 1; +// pIcd->pPrevSibling = NULL; // Set by poolCalloc +// pIcd->pNextSibling = NULL; // Set by poolCalloc +// pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc + pIcd->pNextKeyComponent = pIcd + 1; +// pIcd->uiCdl = 0; // Set by poolCalloc + pIcd->uiKeyComponent = 1; +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc +// pIcd->uiDataComponent = 0; // Set by poolCalloc +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, XFLM_NODATA_TYPE); + + pIcd++; + pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; + pIcd->pIxd = m_pNameIndex; + pIcd->uiDictNum = ATTR_NAME_TAG; + pIcd->uiFlags = ICD_VALUE | ICD_REQUIRED_PIECE | ICD_IS_ATTRIBUTE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc + pIcd->pParent = m_pNameIndex->pIcdTree; +// pIcd->pFirstChild = NULL; // Set by poolCalloc +// pIcd->pPrevSibling = NULL; // Set by poolCalloc + pIcd->pNextSibling = pIcd + 1; + pIcd->pPrevKeyComponent = pIcd - 1; + pIcd->pNextKeyComponent = pIcd + 1; + pIcd->uiCdl = 1; + pIcd->uiKeyComponent = 2; +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc +// pIcd->uiDataComponent = 0; // Set by poolCalloc +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, attrElmGetType( + getReservedAttributeDef( pIcd->uiDictNum))); + + pIcd++; + m_pNameIndex->pLastKey = pIcd; + pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; + pIcd->pIxd = m_pNameIndex; + pIcd->uiDictNum = ATTR_TARGET_NAMESPACE_TAG; + pIcd->uiFlags = ICD_VALUE | ICD_IS_ATTRIBUTE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc + pIcd->pParent = m_pNameIndex->pIcdTree; +// pIcd->pFirstChild = NULL; // Set by poolCalloc + pIcd->pPrevSibling = pIcd - 1; + pIcd->pNextSibling = pIcd + 1; + pIcd->pPrevKeyComponent = pIcd - 1; +// pIcd->pNextKeyComponent = NULL; // Set by poolCalloc + pIcd->uiCdl = 2; + pIcd->uiKeyComponent = 3; +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc +// pIcd->uiDataComponent = 0; // Set by poolCalloc +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, attrElmGetType( + getReservedAttributeDef( pIcd->uiDictNum))); + + pIcd++; + m_pNameIndex->pFirstData = pIcd; + m_pNameIndex->pLastData = pIcd; + pIcd->uiIndexNum = m_pNameIndex->uiIndexNum; + pIcd->pIxd = m_pNameIndex; + pIcd->uiDictNum = ATTR_DICT_NUMBER_TAG; + pIcd->uiFlags = ICD_VALUE | ICD_IS_ATTRIBUTE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc + pIcd->pParent = m_pNameIndex->pIcdTree; +// pIcd->pFirstChild = NULL; // Set by poolCalloc + pIcd->pPrevSibling = pIcd - 1; +// pIcd->pNextSibling = NULL; // Set by poolCalloc +// pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc +// pIcd->pNextKeyComponent = NULL; // Set by poolCalloc + pIcd->uiCdl = 3; +// pIcd->uiKeyComponent = 0; // Set by poolCalloc +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc + pIcd->uiDataComponent = 1; +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, attrElmGetType( + getReservedAttributeDef( pIcd->uiDictNum))); + + // Set up the ICDs for the number index + + if (RC_BAD( rc = m_dictPool.poolCalloc( sizeof( ICD) * 2, + (void **)&m_pNumberIndex->pIcdTree))) + { + goto Exit; + } + + pIcd = m_pNumberIndex->pIcdTree; + pIcd->uiIndexNum = m_pNumberIndex->uiIndexNum; + m_pNumberIndex->pFirstKey = pIcd; + pIcd->pIxd = m_pNumberIndex; + pIcd->uiDictNum = ELM_ROOT_TAG; + pIcd->uiFlags = ICD_PRESENCE | ICD_REQUIRED_PIECE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc +// pIcd->pParent = NULL; // Set by poolCalloc + pIcd->pFirstChild = pIcd + 1; +// pIcd->pPrevSibling = NULL; // Set by poolCalloc +// pIcd->pNextSibling = NULL; // Set by poolCalloc +// pIcd->pPrevKeyComponent = NULL; // Set by poolCalloc + pIcd->pNextKeyComponent = pIcd + 1; +// pIcd->uiCdl = 0; // Set by poolCalloc + pIcd->uiKeyComponent = 1; +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc +// pIcd->uiDataComponent = 0; // Set by poolCalloc +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, XFLM_NODATA_TYPE); + + pIcd++; + m_pNumberIndex->pLastKey = pIcd; + pIcd->uiIndexNum = m_pNumberIndex->uiIndexNum; + pIcd->pIxd = m_pNumberIndex; + pIcd->uiDictNum = ATTR_DICT_NUMBER_TAG; + pIcd->uiFlags = ICD_VALUE | ICD_REQUIRED_PIECE | ICD_IS_ATTRIBUTE; +// pIcd->uiCompareRules = 0; // Set by poolCalloc + pIcd->pParent = m_pNumberIndex->pIcdTree; +// pIcd->pFirstChild = NULL; // Set by poolCalloc +// pIcd->pPrevSibling = NULL; // Set by poolCalloc +// pIcd->pNextSibling = NULL; // Set by poolCalloc + pIcd->pPrevKeyComponent = pIcd - 1; +// pIcd->pNextKeyComponent = NULL; // Set by poolCalloc + pIcd->uiCdl = 1; + pIcd->uiKeyComponent = 2; +// pIcd->pPrevDataComponent = NULL; // Set by poolCalloc +// pIcd->pNextDataComponent = NULL; // Set by poolCalloc +// pIcd->uiDataComponent = 0; // Set by poolCalloc +// pIcd->uiLimit = 0; // Set by poolCalloc + icdSetDataType( pIcd, attrElmGetType( + getReservedAttributeDef( pIcd->uiDictNum))); + + if (RC_BAD( rc = linkIcds( m_pNameIndex->pIcdTree))) + { + goto Exit; + } + + if (RC_BAD( rc = linkIcds( m_pNumberIndex->pIcdTree))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate an element table. +****************************************************************************/ +RCODE F_Dict::allocElementTable( + FLMUINT uiLowestElementNum, + FLMUINT uiHighestElementNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + // There should not already be an element table or an extended element + // table. + + flmAssert( m_pElementDefTbl == NULL && m_pExtElementDefTbl == NULL && + m_hExtElementDefMutex == F_MUTEX_NULL); + + // No need for a fixed element table if we don't have any element + // numbers in that range. + + if (uiHighestElementNum && + uiLowestElementNum <= FLM_HIGH_FIXED_ELEMENT_NUM) + { + m_uiLowestElementNum = uiLowestElementNum; + if (uiHighestElementNum > FLM_HIGH_FIXED_ELEMENT_NUM) + { + m_uiHighestElementNum = FLM_HIGH_FIXED_ELEMENT_NUM; + } + else + { + m_uiHighestElementNum = uiHighestElementNum; + } + uiCount = m_uiHighestElementNum - m_uiLowestElementNum + 1; + if (RC_BAD( rc = f_calloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pElementDefTbl))) + { + goto Exit; + } + } + + // See if we should allocate an extended element table + + if (uiHighestElementNum >= FLM_LOW_EXT_ELEMENT_NUM) + { + FLMUINT uiNewSize = uiHighestElementNum % + MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; + + if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) + { + uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; + } + + // Need to allocate a mutex too. + + if (RC_BAD( rc = f_mutexCreate( &m_hExtElementDefMutex))) + { + goto Exit; + } + + // Allocate a new array + + if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, + &m_pExtElementDefTbl))) + { + goto Exit; + } + m_uiExtElementDefTblSize = uiNewSize; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate an attribute table. +****************************************************************************/ +RCODE F_Dict::allocAttributeTable( + FLMUINT uiLowestAttributeNum, + FLMUINT uiHighestAttributeNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + // There should not already be an element table or an extended element + // table. + + flmAssert( m_pAttributeDefTbl == NULL && m_pExtAttributeDefTbl == NULL && + m_hExtAttributeDefMutex == F_MUTEX_NULL); + + // No need for a fixed attribute table if we don't have any attribute + // numbers in that range. + + if (uiHighestAttributeNum && + uiLowestAttributeNum <= FLM_HIGH_FIXED_ATTRIBUTE_NUM) + { + m_uiLowestAttributeNum = uiLowestAttributeNum; + if (uiHighestAttributeNum > FLM_HIGH_FIXED_ELEMENT_NUM) + { + m_uiHighestAttributeNum = FLM_HIGH_FIXED_ELEMENT_NUM; + } + else + { + m_uiHighestAttributeNum = uiHighestAttributeNum; + } + uiCount = m_uiHighestAttributeNum - m_uiLowestAttributeNum + 1; + if (RC_BAD( rc = f_calloc( uiCount * sizeof( ATTR_ELM_DEF), + &m_pAttributeDefTbl))) + { + goto Exit; + } + } + + // See if we should allocate an extended attribute table + + if (uiHighestAttributeNum >= FLM_LOW_EXT_ATTRIBUTE_NUM) + { + FLMUINT uiNewSize = uiHighestAttributeNum % + MAX_EXT_ATTR_ELM_ARRAY_SIZE + 1000; + + if (uiNewSize > MAX_EXT_ATTR_ELM_ARRAY_SIZE) + { + uiNewSize = MAX_EXT_ATTR_ELM_ARRAY_SIZE; + } + + // Need to allocate a mutex too. + + if (RC_BAD( rc = f_mutexCreate( &m_hExtAttributeDefMutex))) + { + goto Exit; + } + + // Allocate a new array + + if (RC_BAD( rc = f_calloc( sizeof( EXT_ATTR_ELM_DEF) * uiNewSize, + &m_pExtAttributeDefTbl))) + { + goto Exit; + } + m_uiExtAttributeDefTblSize = uiNewSize; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate an index table. +****************************************************************************/ +RCODE F_Dict::allocIndexTable( + FLMUINT uiLowestIndexNum, + FLMUINT uiHighestIndexNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + // There should not already be an index table. + + flmAssert( m_ppIxdTbl == NULL); + + m_uiLowestIxNum = uiLowestIndexNum; + m_uiHighestIxNum = uiHighestIndexNum; + if ((uiCount = getIndexCount( FALSE)) > 0) + { + if (RC_BAD( rc = f_calloc( uiCount * sizeof( IXD *), &m_ppIxdTbl))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate a prefix table. +****************************************************************************/ +RCODE F_Dict::allocPrefixTable( + FLMUINT uiLowestPrefixNum, + FLMUINT uiHighestPrefixNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + flmAssert( m_ppPrefixTbl == NULL); + + m_uiLowestPrefixNum = uiLowestPrefixNum; + m_uiHighestPrefixNum = uiHighestPrefixNum; + if ((uiCount = getPrefixCount()) > 0) + { + if (RC_BAD( rc = f_calloc( uiCount * sizeof( F_PREFIX *), &m_ppPrefixTbl))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate an encdef table. +****************************************************************************/ +RCODE F_Dict::allocEncDefTable( + FLMUINT uiLowestEncDefNum, + FLMUINT uiHighestEncDefNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + flmAssert( m_ppEncDefTbl == NULL); + + m_uiLowestEncDefNum = uiLowestEncDefNum; + m_uiHighestEncDefNum = uiHighestEncDefNum; + if ((uiCount = getEncDefCount()) > 0) + { + if (RC_BAD( rc = f_calloc( uiCount * sizeof( F_ENCDEF *), &m_ppEncDefTbl))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate a collection table. +****************************************************************************/ +RCODE F_Dict::allocCollectionTable( + FLMUINT uiLowestCollectionNum, + FLMUINT uiHighestCollectionNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + + // There should not already be a collection table. + + flmAssert( m_ppCollectionTbl == NULL); + + m_uiLowestCollectionNum = uiLowestCollectionNum; + m_uiHighestCollectionNum = uiHighestCollectionNum; + if ((uiCount = getCollectionCount( FALSE)) > 0) + { + if (RC_BAD( rc = f_calloc( uiCount * sizeof( LFILE *), + &m_ppCollectionTbl))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Find the node IDs where we keep the next element, next attribute, + next index, and next collection numbers. +****************************************************************************/ +RCODE F_Dict::createNextDictNums( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttr = NULL; + + // Create a new root element + + if (RC_BAD( rc = pDb->createRootElement( XFLM_DICT_COLLECTION, + ELM_NEXT_DICT_NUMS_TAG, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pNode->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // The NODE ID of this element had better be 1, because it is the + // first thing we create in the dictionary. Plus, the + // getNextDictNumNodeIds method is counting on it being one! + + if( pNode->getNodeId() != XFLM_DICTINFO_DOC_ID) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Create and populate the four attributes that hold next element, + // next attribute, next index, and next collection numbers. + // Freeze each of the nodes so that they can only be modified by + // FLAIM. + + // Node for next element number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_ELEMENT_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Node for next attribute number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_ATTRIBUTE_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Node for next index number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_INDEX_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Node for next collection number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_COLLECTION_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Node for next prefix number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_PREFIX_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Node for next encdef number + + if (RC_BAD( rc = pNode->createAttribute( pDb, + ATTR_NEXT_ENCDEF_NUM_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pAttr) + { + pAttr->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Allocate the next dictionary number for a specific dictionary type. +****************************************************************************/ +RCODE F_Dict::allocNextDictNum( + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT * puiDictNumber) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + FLMUINT uiAttrName; + FLMUINT uiMaxNum; + + if( RC_BAD( rc = pDb->getNode( + XFLM_DICT_COLLECTION, XFLM_DICTINFO_DOC_ID, &pDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( pDoc->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + switch (uiDictType) + { + case ELM_ELEMENT_TAG: + uiAttrName = ATTR_NEXT_ELEMENT_NUM_TAG; + uiMaxNum = XFLM_MAX_ELEMENT_NUM; + break; + case ELM_ATTRIBUTE_TAG: + uiAttrName = ATTR_NEXT_ATTRIBUTE_NUM_TAG; + uiMaxNum = XFLM_MAX_ATTRIBUTE_NUM; + break; + case ELM_INDEX_TAG: + uiAttrName = ATTR_NEXT_INDEX_NUM_TAG; + uiMaxNum = XFLM_MAX_INDEX_NUM; + break; + case ELM_COLLECTION_TAG: + uiAttrName = ATTR_NEXT_COLLECTION_NUM_TAG; + uiMaxNum = XFLM_MAX_COLLECTION_NUM; + break; + case ELM_PREFIX_TAG: + uiAttrName = ATTR_NEXT_PREFIX_NUM_TAG; + uiMaxNum = XFLM_MAX_PREFIX_NUM; + break; + case ELM_ENCDEF_TAG: + uiAttrName = ATTR_NEXT_ENCDEF_NUM_TAG; + uiMaxNum = XFLM_MAX_ENCDEF_NUM; + break; + default: + + // Nothing to allocate for other types. *puiDictNumber will + // return as zero. + + *puiDictNumber = 0; + goto Exit; + } + + if( RC_BAD( rc = pDoc->getAttribute( + pDb, uiAttrName, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUINT( pDb, puiDictNumber))) + { + goto Exit; + } + + // Dictionary number better be > 0 + + if( !(*puiDictNumber)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // See if we have exceeded the limit for this type. + + if( *puiDictNumber > uiMaxNum) + { + *puiDictNumber = 0; + + switch( uiDictType) + { + case ELM_ELEMENT_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_ELEMENT_NUMS); + break; + + case ELM_ATTRIBUTE_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_ATTRIBUTE_NUMS); + break; + + case ELM_INDEX_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_INDEX_NUMS); + break; + + case ELM_COLLECTION_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_COLLECTION_NUMS); + break; + + case ELM_PREFIX_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_PREFIX_NUMS); + break; + + case ELM_ENCDEF_TAG: + rc = RC_SET( NE_XFLM_NO_MORE_ENCDEF_NUMS); + } + + goto Exit; + } + + // Need to increment the dictionary number for the next + // caller. + + if( RC_BAD( rc = pAttr->removeModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( pDb, *puiDictNumber + 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + +Exit: + + if( pAttr) + { + pAttr->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Check and set the next dictionary number for a specific dictionary type. +****************************************************************************/ +RCODE F_Dict::setNextDictNum( + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT uiDictNumber) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + FLMUINT uiAttrName; + FLMUINT uiCurrDictNumber; + + if( RC_BAD( rc = pDb->getNode( + XFLM_DICT_COLLECTION, XFLM_DICTINFO_DOC_ID, &pDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( pDoc->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + switch( uiDictType) + { + case ELM_ELEMENT_TAG: + { + if( uiDictNumber > XFLM_MAX_ELEMENT_NUM) + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + + uiAttrName = ATTR_NEXT_ELEMENT_NUM_TAG; + break; + } + + case ELM_ATTRIBUTE_TAG: + { + if (uiDictNumber > XFLM_MAX_ATTRIBUTE_NUM) + { + rc = RC_SET( NE_XFLM_BAD_ATTRIBUTE_NUM); + goto Exit; + } + + uiAttrName = ATTR_NEXT_ATTRIBUTE_NUM_TAG; + break; + } + + case ELM_INDEX_TAG: + { + if (uiDictNumber > XFLM_MAX_INDEX_NUM) + { + rc = RC_SET( NE_XFLM_BAD_IX); + goto Exit; + } + + uiAttrName = ATTR_NEXT_INDEX_NUM_TAG; + break; + } + + case ELM_COLLECTION_TAG: + { + if (uiDictNumber > XFLM_MAX_COLLECTION_NUM) + { + rc = RC_SET( NE_XFLM_BAD_COLLECTION); + goto Exit; + } + + uiAttrName = ATTR_NEXT_COLLECTION_NUM_TAG; + break; + } + + case ELM_PREFIX_TAG: + { + if (uiDictNumber > XFLM_MAX_PREFIX_NUM) + { + rc = RC_SET( NE_XFLM_BAD_PREFIX); + goto Exit; + } + + uiAttrName = ATTR_NEXT_PREFIX_NUM_TAG; + break; + } + + case ELM_ENCDEF_TAG: + { + if (uiDictNumber > XFLM_MAX_ENCDEF_NUM) + { + rc = RC_SET( NE_XFLM_BAD_ENCDEF_NUM); + goto Exit; + } + + uiAttrName = ATTR_NEXT_ENCDEF_NUM_TAG; + break; + } + + default: + { + // Doesn't really matter on other dictionary types + // because dictionary number is not used for anything. + + goto Exit; + } + } + + if( RC_BAD( rc = pDoc->getAttribute( pDb, uiAttrName, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( pDb, &uiCurrDictNumber))) + { + goto Exit; + } + + // Dictionary number better be > 0 + + if( !uiCurrDictNumber) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // If the new dictionary number >= current next dictionary + // number, need to set the next dictionary number to one greater + // than the new dictionary number. + + if( uiDictNumber >= uiCurrDictNumber) + { + if( RC_BAD( rc = pAttr->removeModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( pDb, uiDictNumber + 1))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + +Exit: + + if( pAttr) + { + pAttr->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_AttrElmInfo::resetInfo( void) +{ + m_uiDictNum = 0; + m_uiDataType = XFLM_NODATA_TYPE; + m_uiFlags = 0; + m_uiState = ATTR_ELM_STATE_ACTIVE; + m_pFirstIcd = NULL; + + if( m_pDocNode) + { + m_pDocNode->Release(); + m_pDocNode = NULL; + } + + if( m_pTargetNamespaceAttr) + { + m_pTargetNamespaceAttr->Release(); + m_pTargetNamespaceAttr = NULL; + } + + if( m_pNameAttr) + { + m_pNameAttr->Release(); + m_pNameAttr = NULL; + } +} diff --git a/version5/src/fdict.h b/version5/src/fdict.h new file mode 100644 index 0000000..8a1febb --- /dev/null +++ b/version5/src/fdict.h @@ -0,0 +1,1044 @@ +//------------------------------------------------------------------------------ +// Desc: F_Dict class definitions - internal object for database's +// dictionary. +// +// 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: fdict.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FDICT_H +#define FDICT_H + +#define FLM_HIGH_FIXED_ELEMENT_NUM 0xFFFF +#define FLM_LOW_EXT_ELEMENT_NUM (FLM_HIGH_FIXED_ELEMENT_NUM + 1) + +#define FLM_HIGH_FIXED_ATTRIBUTE_NUM 0xFFFF +#define FLM_LOW_EXT_ATTRIBUTE_NUM (FLM_HIGH_FIXED_ATTRIBUTE_NUM + 1) + +#define MAX_EXT_ATTR_ELM_ARRAY_SIZE 0xFFFF + +struct IXD; +struct ICD; +class F_Database; +class F_Pool; +class F_CCS; +class F_AttrItem; + +/**************************************************************************** +Desc: Attribute/Element definition structure. +****************************************************************************/ +typedef struct AttrElmDef +{ + // IMPORTANT NOTE: If adding pointers to this structure, be sure + // to add code to F_Dict->clone() method to set them up properly. + + FLMUINT uiFlags; // Lower four bits has data type + // Upper four bits has state + // High bit indicates if it is an + // attribute or not. + // Value of zero means slot is not + // used - allows us to memset an array + // to zeroes and have all slots unused. +#define ATTR_ELM_DATA_TYPE_MASK 0x000F +#define ATTR_ELM_STATE_MASK 0x00F0 +#define ATTR_ELM_STATE_ACTIVE 0x0010 // Normal active attribute or element +#define ATTR_ELM_STATE_CHECKING 0x0020 // Attribute or element has been marked + // to be checked +#define ATTR_ELM_STATE_PURGE 0x0040 // Purge this attribute or element + // from the database. + // And delete the dictionary definition +#define ATTR_ELM_FLAGS_MASK 0x0F00 +#define ATTR_ELM_NS_DECL 0x0100 // Attribute is a namespace declaration +#define ATTR_ELM_UNIQUE_SUBELMS 0x0200 // Element's sub-elements must all have unique name ids + ICD * pFirstIcd; // Points to first ICD attribute or element is + // indexed in. NULL if not indexed. +} ATTR_ELM_DEF; + +/**************************************************************************** +Desc: Extended attribute/element definition structure. +****************************************************************************/ +typedef struct ExtAttrElmDef +{ + FLMUINT uiDictNum; + ATTR_ELM_DEF attrElmDef; +} EXT_ATTR_ELM_DEF; + +/***************************************************************************** +Desc: Dictionary definition info object +*****************************************************************************/ +class F_AttrElmInfo : public XF_RefCount, public XF_Base +{ +public: + + F_AttrElmInfo() + { + m_pDocNode = NULL; + m_pTargetNamespaceAttr = NULL; + m_pNameAttr = NULL; + resetInfo(); + } + + ~F_AttrElmInfo() + { + resetInfo(); + } + + void resetInfo( void); + + FINLINE FLMUINT getDataType( void) + { + return( m_uiDataType); + } + + FINLINE FLMUINT getState( void) + { + return( m_uiState); + } + + FINLINE FLMUINT getFlags( void) + { + return( m_uiFlags); + } + +private: + + FLMUINT m_uiDictNum; + FLMUINT m_uiDataType; + FLMUINT m_uiFlags; + FLMUINT m_uiState; + ICD * m_pFirstIcd; + + IF_DOMNode * m_pDocNode; + IF_DOMNode * m_pTargetNamespaceAttr; + IF_DOMNode * m_pNameAttr; + +friend class F_Db; +friend class F_DOMNode; +friend class F_Dict; +friend class F_NameTable; +friend class F_NodeVerifier; +friend class F_Query; +}; + +/************************************************************************** +Desc: Get an attribute or element's type. +**************************************************************************/ +FINLINE FLMUINT attrElmGetType( + ATTR_ELM_DEF * pAttrElmDef) +{ + return( pAttrElmDef->uiFlags & ATTR_ELM_DATA_TYPE_MASK); +} + +/************************************************************************** +Desc: See if an attribute or element is indexed. +**************************************************************************/ +FINLINE FLMBOOL attrElmIsIndexed( + ATTR_ELM_DEF * pAttrElmDef) +{ + return( pAttrElmDef->pFirstIcd ? TRUE : FALSE); +} + +/************************************************************************** +Desc: Get an attribute or element's state. +**************************************************************************/ +FINLINE FLMUINT attrElmGetState( + ATTR_ELM_DEF * pAttrElmDef) +{ + return( pAttrElmDef->uiFlags & ATTR_ELM_STATE_MASK); +} + +/************************************************************************** +Desc: Set an attribute or element's state. +**************************************************************************/ +FINLINE void attrElmSetState( + ATTR_ELM_DEF * pAttrElmDef, + FLMUINT uiState + ) +{ + pAttrElmDef->uiFlags = + (pAttrElmDef->uiFlags & (~(ATTR_ELM_STATE_MASK))) | + (uiState & ATTR_ELM_STATE_MASK); +} + +/************************************************************************** +Desc: Get an attribute or element's flags. +**************************************************************************/ +FINLINE FLMUINT attrElmGetFlags( + ATTR_ELM_DEF * pAttrElmDef) +{ + return( pAttrElmDef->uiFlags & ATTR_ELM_FLAGS_MASK); +} + +/************************************************************************** +Desc: Set an attribute or element's flags.. +**************************************************************************/ +FINLINE void attrElmSetFlags( + ATTR_ELM_DEF * pAttrElmDef, + FLMUINT uiFlags) +{ + pAttrElmDef->uiFlags = + (pAttrElmDef->uiFlags & (~(ATTR_ELM_FLAGS_MASK))) | + (uiFlags & ATTR_ELM_FLAGS_MASK); +} + +/**************************************************************************** +Struct: LFILE (Logical File) +Desc: This keeps track of the logical file information for an index or + a Collection. +****************************************************************************/ +typedef struct LFILE +{ + + // IMPORTANT NOTE: If adding pointers to this structure, be sure + // to add code to F_Dict->clone() method to set them up properly. + + FLMUINT uiRootBlk; // Address of root block. + FLMUINT uiBlkAddress; // Block address of LFile entry. + FLMUINT uiOffsetInBlk; // Offset within block of entry. + FLMUINT uiLfNum; // Index number or collection number. + eLFileType eLfType; // Type of logical file + FLMUINT uiEncId; // Encryption Id (0 if not encrypted) +} LFILE; + +/**************************************************************************** +Struct: F_COLLECTION (Collection) +Desc: This keeps track of collections. +****************************************************************************/ +typedef struct F_COLLECTION +{ + LFILE lfInfo; // B-Tree information + FLMBOOL bNeedToUpdateNodes; // Do we need to write out node info. + // at commit time? + FLMUINT64 ui64NextNodeId; // Next Node ID + FLMUINT64 ui64FirstDocId; // First document ID + FLMUINT64 ui64LastDocId; // Last document ID +} F_COLLECTION; + +/**************************************************************************** +Struct: F_PREFIX (Prefix) +Desc: This keeps track of Prefixes. +****************************************************************************/ +typedef struct F_PREFIX +{ + FLMUINT64 ui64PrefixId; + FLMUNICODE * puzPrefixName; +} F_PREFIX; + +/**************************************************************************** +Struct: F_ENCDEF (Encryption Definition) +Desc: This keeps track of encryption definitions +****************************************************************************/ +typedef struct +{ + FLMUINT64 ui64EncDefId; + FLMUINT64 ui64DocumentId; + FLMUNICODE * puzEncDefName; + FLMUINT uiEncKeySize; + F_CCS * pCcs; +} F_ENCDEF; + +/**************************************************************************** +Struct: IXD (Index Definition) +Desc: This structure holds the information for an index definition. + There may be multiple IXDs for the same index number. +****************************************************************************/ +typedef struct IXD +{ + + // IMPORTANT NOTE: If adding pointers to this structure, be sure + // to add code to F_Dict->clone() method to set them up properly. + // ALSO, fixup indexDefsSame function in fdict.cpp + + FLMUINT uiIndexNum; // Index number. + FLMUINT uiCollectionNum; // Collection number being indexed. + ICD * pIcdTree; // Points to ICD tree + ICD * pFirstKey; // Points to first key component + ICD * pLastKey; // Points to last key component + ICD * pFirstContext; // Points to first context-only component + ICD * pLastContext; // Points to last context-only component + ICD * pFirstData; // Points to first data component + ICD * pLastData; // Points to last data component + FLMUINT uiNumIcds; // Total ICDs for this index. + FLMUINT uiNumKeyComponents; // Number of key components in the index. + FLMUINT uiNumDataComponents; // Number of data components in the index. + FLMUINT uiNumContextComponents; // Number of context-only components in + // the index. + FLMUINT uiFlags; + #define IXD_ABS_POS 0x00001 // Maintain absolute positioning info. + #define IXD_HAS_SUBSTRING 0x00002 + #define IXD_OFFLINE 0x00004 // Index is offline - may or may + // not be suspended. + #define IXD_SUSPENDED 0x00008 // IXD_OFFLINE should also be set + #define IXD_SINGLE_PATH 0x00010 // ICD list is a single path + + FLMUINT uiLanguage; // WP.LRS language number (not code!) + FLMUINT64 ui64LastDocIndexed; // If value is not ~0 then + // update index with keys from a document + // update if doc id is <= this value. + // NOTE: This is only guaranteed to be + // correct for update transactions. + // This field should only be used by + // update transactions anyway. + LFILE lfInfo; // B-Tree information. + FLMUINT64 ui64IxDefNodeId; +} IXD; + +/**************************************************************************** +Struct: ICD (Index Component Definition) +Desc: This structure contains an index component definition. +****************************************************************************/ +typedef struct ICD +{ + + // IMPORTANT NOTE: If adding pointers to this structure, be sure + // to add code to F_Dict->clone() method to set them up properly. + // ALSO, fixup indexDefsSame function in fdict.cpp + + FLMUINT uiIndexNum; // Index number. + IXD * pIxd; // IXD corresponding to uiIndexNum + FLMUINT uiDictNum; // Attribute or element number. + FLMUINT uiFlags; // The first 4 bits contain data type + // Use FLM_XXXXX_TYPE definitions. + FLMUINT uiCompareRules; // Comparison rules used for strings. + + ICD * pNextInChain; // Next ICD in the chain that has this + // attribute or element number and is + // used in another place. + ICD * pParent; // Parent ICD + ICD * pFirstChild; // First Child ICD + ICD * pPrevSibling; // Previous Sibling ICD + ICD * pNextSibling; // Next Sibling ICD + FLMUINT uiCdl; // Place in CDL list where a node matching + // this ICD should be put. + FLMUINT uiKeyComponent; // Which key component is this? 0 means + // it is not a key component. + ICD * pNextKeyComponent; // Next key component ICD. Also used to + // link context components + ICD * pPrevKeyComponent; // Previous key component ICD. Also used + // to link context components + FLMUINT uiDataComponent; // Which data component is this? 0 means + // it is not a data component. + ICD * pNextDataComponent; // Next data component ICD. + ICD * pPrevDataComponent; // Previous data component ICD. + FLMUINT uiLimit; // Zero or # of characters/bytes to limit. +#define ICD_DEFAULT_LIMIT 128 +#define ICD_DEFAULT_SUBSTRING_LIMIT 48 + +} ICD; + +#define ICD_VALUE 0x00000010 // Value agrees with parsing syntax +#define ICD_EACHWORD 0x00000020 // Index each and every word in the field +#define ICD_PRESENCE 0x00000040 // Index the tag and NOT the value +#define ICD_METAPHONE 0x00000080 // Index words of text strings using + // metaphone values +#define ICD_IS_ATTRIBUTE 0x00000100 // ICD is an attribute +#define ICD_REQUIRED_PIECE 0x00000200 // Required piece (not optional) +#define ICD_REQUIRED_IN_SET 0x00000400 // Required within a set of fields. +#define ICD_SUBSTRING 0x00000800 // Index all substrings pieces +#define ICD_ESC_CHAR 0x00001000 // Placehold so that a query can parse the input + // string and find a literal '*' or '\\'. + // Not specified in dictionary or held in ICD + // Only a temporary flag. +#define ICD_DESCENDING 0x00002000 // Sort in descending order. +#define ICD_MISSING_HIGH 0x00004000 // Sort missing components high instead of low. + +FINLINE FLMUINT icdGetDataType( + ICD * pIcd) +{ + return( pIcd->uiFlags & 0x0F); +} + +FINLINE void icdSetDataType( + ICD * pIcd, + FLMUINT uiDataType) +{ + pIcd->uiFlags = (pIcd->uiFlags & 0xFFFFFFF0) | (uiDataType & 0xF); +} + +/**************************************************************************** +Struct: IX_ITEM (Indexed Item - Attribute or Element) +Desc: This structure is used to track all indexed attributes and elements + whose numbers are greater than or equal to FLM_LOW_EXT_ELEMENT_NUM + (for elements) or FLM_LOW_EXT_ATTRIBUTE_NUM (for attributes). +****************************************************************************/ +typedef struct IndexedItem +{ + FLMUINT uiDictNum; + ICD * pFirstIcd; +} IX_ITEM; + +/**************************************************************************** +Struct: RESERVED_TAG_NAME +Desc: This structure is used strictly to set up a static table (see + fntable.cpp) which lists all reserved tag numbers + and their types. +****************************************************************************/ +typedef struct ReservedTag +{ + const char * pszTagName; + FLMUINT uiTagNum; + FLMUINT uiDataType; + FLMUNICODE * puzNamespace; +} RESERVED_TAG_NAME; + +/************************************************************************** +Desc: This class is the FLAIM dictionary class. +**************************************************************************/ +class F_Dict : public XF_RefCount, public XF_Base +{ +public: + + // Constructor and destructor + + F_Dict(); + + ~F_Dict(); + + void resetDict( void); + + RCODE getElement( + F_Db * pDb, + FLMUINT uiElementNum, + F_AttrElmInfo * pElmInfo); + + RCODE getAttribute( + F_Db * pDb, + FLMUINT uiAttributeNum, + F_AttrElmInfo * pAttrInfo); + + RCODE getNextElement( + F_Db * pDb, + FLMUINT * puiElementNum, + F_AttrElmInfo * pElmInfo); + + RCODE getNextAttribute( + F_Db * pDb, + FLMUINT * puiAttributeNum, + F_AttrElmInfo * pAttrInfo); + + FINLINE FLMUINT getCollectionCount( + FLMBOOL bCountPredefined) + { + FLMUINT uiCount = m_uiHighestCollectionNum + ? m_uiHighestCollectionNum - + m_uiLowestCollectionNum + 1 + : 0; + if (bCountPredefined) + { + + // Add 2 for the FLM_DATA_COLLECTION and + // FLM_DICT_COLLECTION + + uiCount += 2; + } + return( uiCount); + } + + RCODE getCollection( + FLMUINT uiCollectionNum, + F_COLLECTION ** ppCollection, + FLMBOOL bOfflineOk = FALSE); + + RCODE getPrefixId( + F_Db * pDb, + const FLMUNICODE * puzPrefix, + FLMUINT * puiPrefixId); + + RCODE getPrefixId( + F_Db * pDb, + const char * pszPrefix, + FLMUINT * puiPrefixId); + + FINLINE RCODE getPrefix( + FLMUINT uiPrefixId, + FLMUNICODE * puzPrefixBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) + { + return( getPrefix( TRUE, uiPrefixId, (void *)puzPrefixBuf, + uiBufSize, puiCharsReturned)); + } + + FINLINE RCODE getPrefix( + FLMUINT uiPrefixId, + char * pszPrefixBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) + { + return( getPrefix( FALSE, uiPrefixId, (void *)pszPrefixBuf, + uiBufSize, puiCharsReturned)); + } + + RCODE getPrefix( + FLMUINT uiPrefixId, + F_PREFIX ** ppPrefix); + + RCODE getEncDefId( + F_Db * pDb, + const FLMUNICODE * puzEncDef, + FLMUINT * puiEncDefId); + + RCODE getEncDefId( + F_Db * pDb, + const char * pszEncDef, + FLMUINT * puiEncDefId); + + FINLINE RCODE getEncDef( + FLMUINT uiEncDefId, + FLMUNICODE * puzEncDefBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) + { + return getEncDef( TRUE, + uiEncDefId, + (void *)puzEncDefBuf, + uiBufSize, + puiCharsReturned); + } + + FINLINE RCODE getEncDef( + FLMUINT uiEncDefId, + char * pszEncDefBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) + { + return getEncDef( FALSE, + uiEncDefId, + (void *)pszEncDefBuf, + uiBufSize, + puiCharsReturned); + } + + RCODE getEncDef( + FLMUINT uiEncDefId, + F_ENCDEF ** ppEncDef); + + RCODE getDefinitionDoc( + F_Db * pDb, + FLMUINT uiTag, + FLMUINT uiDictId, + F_DOMNode ** ppDoc); + + FINLINE FLMUINT getPrefixCount(void) + { + FLMUINT uiCount = m_uiHighestPrefixNum + ? m_uiHighestPrefixNum - m_uiLowestPrefixNum + 1 + : 0; + return( uiCount); + } + + FINLINE FLMUINT getEncDefCount(void) + { + FLMUINT uiCount = m_uiHighestEncDefNum + ? m_uiHighestEncDefNum - m_uiLowestEncDefNum + 1 + : 0; + return( uiCount); + } + + FINLINE FLMUINT getIndexCount( + FLMBOOL bCountPredefined) + { + FLMUINT uiCount = m_uiHighestIxNum + ? m_uiHighestIxNum - m_uiLowestIxNum + 1 + : 0; + if (bCountPredefined) + { + + // Add 2 for the pre-defined indexes. + + uiCount += 2; + } + return( uiCount); + } + + FINLINE FLMUINT getIxdOffset( + FLMUINT uiIndexNum + ) + { + if (uiIndexNum <= XFLM_MAX_INDEX_NUM) + { + return( uiIndexNum - m_uiLowestIxNum + 2); + } + else + { + switch (uiIndexNum) + { + case XFLM_DICT_NUMBER_INDEX: + return( 0); + case XFLM_DICT_NAME_INDEX: + return( 1); + default: + flmAssert( 0); + return( 0xFFFF); + } + } + } + + RCODE getIndex( + FLMUINT uiIndexNum, + LFILE ** ppLFile, + IXD ** ppIxd, + FLMBOOL bOfflineOk = FALSE); + + IXD * getNextIndex( + FLMUINT uiIndexNum, + FLMBOOL bOkToGetPredefined); + + F_COLLECTION * getNextCollection( + FLMUINT uiCollectionNum, + FLMBOOL bOkToGetPredefined); + + FINLINE ATTR_ELM_DEF * getElementDef( + FLMUINT uiElementNum) + { + ATTR_ELM_DEF * pElementDef = NULL; + + if (uiElementNum >= m_uiLowestElementNum && + uiElementNum <= m_uiHighestElementNum) + { + pElementDef = &m_pElementDefTbl [uiElementNum - m_uiLowestElementNum]; + if (pElementDef && !attrElmGetState( pElementDef)) + { + pElementDef = NULL; + } + } + return( pElementDef); + } + + FINLINE ATTR_ELM_DEF * getReservedElementDef( + FLMUINT uiElementNum) + { + ATTR_ELM_DEF * pElementDef = &m_pReservedElementDefTbl [uiElementNum - + XFLM_FIRST_RESERVED_ELEMENT_TAG]; + if (!attrElmGetState( pElementDef)) + { + pElementDef = NULL; + } + return( pElementDef); + } + + FINLINE ATTR_ELM_DEF * getAttributeDef( + FLMUINT uiAttributeNum) + { + ATTR_ELM_DEF * pAttributeDef = NULL; + + if (uiAttributeNum >= m_uiLowestAttributeNum && + uiAttributeNum <= m_uiHighestAttributeNum) + { + pAttributeDef = &m_pAttributeDefTbl [uiAttributeNum - m_uiLowestAttributeNum]; + if (pAttributeDef && !attrElmGetState( pAttributeDef)) + { + pAttributeDef = NULL; + } + } + return( pAttributeDef); + } + + FINLINE ATTR_ELM_DEF * getReservedAttributeDef( + FLMUINT uiAttributeNum) + { + ATTR_ELM_DEF * pAttributeDef = &m_pReservedAttributeDefTbl [uiAttributeNum - + XFLM_FIRST_RESERVED_ATTRIBUTE_TAG]; + if (!attrElmGetState( pAttributeDef)) + { + pAttributeDef = NULL; + } + return( pAttributeDef); + } + + void linkToDatabase( + F_Database * pDatabase); + + void unlinkFromDatabase( void); + + RCODE linkIcdInChain( + ICD * pIcd); + + RCODE linkIcds( + ICD * pIcdTree); + + void unlinkIcdFromChain( + ICD * pIcd); + + void unlinkIcds( + ICD * pIcdTree); + + FINLINE FLMUINT getUseCount( void) + { + return( m_uiUseCount); + } + + FINLINE FLMUINT decrUseCount( void) + { + return( --m_uiUseCount); + } + + FINLINE void incrUseCount( void) + { + m_uiUseCount++; + } + + FINLINE F_Dict * getPrev( void) + { + return( m_pPrev); + } + + FINLINE F_Dict * getNext( void) + { + return( m_pNext); + } + + FINLINE F_Database * getDatabase( void) + { + return( m_pDatabase); + } + + RCODE copyIXD( + IXD ** ppDestIxd, + IXD * pSrcIxd); + + RCODE cloneDict( + F_Dict * pSrcDict); + + RCODE checkElementReferences( // was checkReferences + FLMUINT uiElementNum); + + RCODE checkAttributeReferences( // was checkReferences + FLMUINT uiAttributeNum); + + RCODE checkCollectionReferences( + FLMUINT uiCollectionNum); + + RCODE updateDict( + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT64 ui64DocumentID, + FLMUINT uiDictNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE setupPredefined( + FLMUINT uiDefaultLanguage); + + FINLINE FLMUINT getDictSeq( void) + { + return( m_uiDictSeq); + } + + RCODE allocNameTable( void); + + FINLINE F_NameTable * getNameTable( void) + { + return( m_pNameTable); + } + + RCODE allocElementTable( + FLMUINT uiLowestElementNum, + FLMUINT uiHighestElementNum); + + RCODE allocAttributeTable( + FLMUINT uiLowestAttributeNum, + FLMUINT uiHighestAttributeNum); + + RCODE allocIndexTable( + FLMUINT uiLowestIndexNum, + FLMUINT uiHighestIndexNum); + + RCODE allocPrefixTable( + FLMUINT uiLowestPrefixNum, + FLMUINT uiHighestPrefixNum); + + RCODE allocEncDefTable( + FLMUINT uiLowestEncDefNum, + FLMUINT uiHighestEncDefNum); + + RCODE allocCollectionTable( + FLMUINT uiLowestCollectionNum, + FLMUINT uiHighestCollectionNum); + +private: + + RCODE reallocTbl( + FLMUINT uiNewId, + FLMUINT uiElementSize, + void ** ppvTbl, + FLMUINT * puiLowest, + FLMUINT * puiHighest, + FLMUINT uiAdjustFactor, + FLMUINT uiMaxId); + + RCODE updateElementDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiElementNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE updateAttributeDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiAttributeNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE updateIndexDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiIndexNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE updateCollectionDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiCollectionNumber, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE updatePrefixDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiPrefixNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE updateEncDef( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiEncDefNum, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + IX_ITEM * findIxItem( + IX_ITEM * pIxTbl, + FLMUINT uiNumItems, + FLMUINT uiTagNum, + FLMUINT * puiInsertPos = NULL); + + FINLINE IX_ITEM * findIxElement( + FLMUINT uiElementNum, + FLMUINT * puiInsertPos = NULL) + { + return( findIxItem( m_pIxElementTbl, m_uiNumIxElements, + uiElementNum, puiInsertPos)); + } + + FINLINE IX_ITEM * findIxAttribute( + FLMUINT uiAttributeNum, + FLMUINT * puiInsertPos = NULL) + { + return( findIxItem( m_pIxAttributeTbl, m_uiNumIxAttributes, + uiAttributeNum, puiInsertPos)); + } + + RCODE getExtElement( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiElementNum, + F_AttrElmInfo * pElmInfo); + + RCODE getExtAttribute( + F_Db * pDb, + FLMUINT64 ui64DocumentID, + FLMUINT uiAttributeNum, + F_AttrElmInfo * pAttrInfo); + + void setExtElementFirstIcd( + FLMUINT uiElementNum, + ICD * pFirstIcd); + + void setExtAttributeFirstIcd( + FLMUINT uiAttributeNum, + ICD * pFirstIcd); + + FINLINE EXT_ATTR_ELM_DEF * getExtElementDef( + FLMUINT uiElementNum) + { + return( &m_pExtElementDefTbl [uiElementNum % + m_uiExtElementDefTblSize]); + } + + FINLINE EXT_ATTR_ELM_DEF * getExtAttributeDef( + FLMUINT uiAttributeNum) + { + return( &m_pExtAttributeDefTbl [uiAttributeNum % + m_uiExtAttributeDefTblSize]); + } + + RCODE getNextDictNumNodeIds( // fdict.cpp + F_Db * pDb); + + RCODE createNextDictNums( // fdict.cpp + F_Db * pDb); + + RCODE allocNextDictNum( // fdict.cpp + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT * puiDictNumber); + + RCODE setNextDictNum( // fdict.cpp + F_Db * pDb, + FLMUINT uiDictType, + FLMUINT uiDictNumber); + + RCODE getPrefix( // fdict.cpp + FLMBOOL bUnicode, + FLMUINT uiPrefixId, + void * pvPrefixBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned); + + RCODE getEncDef( // fdict.cpp + FLMBOOL bUnicode, + FLMUINT uiEncDefId, + void * pvEncDefBuf, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned); + + F_Dict * m_pNext; // Pointer to next F_Dict object in the list, + // if any. All versions of a dictionary that + // are currently in use are linked together. + // Usually, there will be only one local + // dictionary in the list. + F_Dict * m_pPrev; // Previous F_Dict object in the list. + F_Database * m_pDatabase; // database this dictionary is associated with. + // A null value means it is not yet linked + // to a database. + FLMUINT m_uiDictSeq; // This is the sequence number of the + // dictionary + F_Pool m_dictPool; // Pool for all allocations except tables. + + // Fixed element definition table - used for elements whose tag numbers + // are less than or equal to FLM_HIGH_FIXED_ELEMENT_NUM + + ATTR_ELM_DEF * m_pElementDefTbl; + FLMUINT m_uiLowestElementNum; + FLMUINT m_uiHighestElementNum; + + // Reserved element definition table - used for elements whose tag numbers + // are in the "reserved tag" range. + + ATTR_ELM_DEF * m_pReservedElementDefTbl; + + // Extended element definition table - used for elements whose tag + // numbers are greater than or equal to FLM_LOW_EXT_ELEMENT_NUM + + EXT_ATTR_ELM_DEF * m_pExtElementDefTbl; + FLMUINT m_uiExtElementDefTblSize; + F_MUTEX m_hExtElementDefMutex; + + // Table for tracking ALL indexed elements whose tag number is + // greater than or equal to FLM_LOW_EXT_ELEMENT_NUM. + + IX_ITEM * m_pIxElementTbl; + FLMUINT m_uiIxElementTblSize; + FLMUINT m_uiNumIxElements; + + // Fixed attribute definition table - used for attributes whose tag numbers + // are less than or equal to FLM_HIGH_FIXED_ATTRIBUTE_NUM + + ATTR_ELM_DEF * m_pAttributeDefTbl; + FLMUINT m_uiLowestAttributeNum; + FLMUINT m_uiHighestAttributeNum; + + // Reserved attribute definition table - used for attributes whose tag + // numbers are in the "reserved tag" range. + + ATTR_ELM_DEF * m_pReservedAttributeDefTbl; + + // Extended attribute definition table - used for attributes whose tag + // numbers are greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM + + EXT_ATTR_ELM_DEF * m_pExtAttributeDefTbl; + FLMUINT m_uiExtAttributeDefTblSize; + F_MUTEX m_hExtAttributeDefMutex; + + // Table for tracking ALL indexed attributes whose tag number is + // greater than or equal to FLM_LOW_EXT_ATTRIBUTE_NUM. + + IX_ITEM * m_pIxAttributeTbl; + FLMUINT m_uiIxAttributeTblSize; + FLMUINT m_uiNumIxAttributes; + + // Pre-defined collections + + F_COLLECTION * m_pDictCollection; + F_COLLECTION * m_pDataCollection; + F_COLLECTION * m_pMaintCollection; + + // User defined Collections + + F_COLLECTION ** m_ppCollectionTbl; + FLMUINT m_uiLowestCollectionNum; + FLMUINT m_uiHighestCollectionNum; + + // User defined prefixes + + F_PREFIX ** m_ppPrefixTbl; + FLMUINT m_uiLowestPrefixNum; + FLMUINT m_uiHighestPrefixNum; + + // User defined encryption defs + + F_ENCDEF ** m_ppEncDefTbl; + FLMUINT m_uiLowestEncDefNum; + FLMUINT m_uiHighestEncDefNum; + + // Pre-defined indexes + + IXD * m_pNameIndex; + IXD * m_pNumberIndex; + + // User defined indexes + + IXD ** m_ppIxdTbl; + FLMUINT m_uiLowestIxNum; + FLMUINT m_uiHighestIxNum; + ICD * m_pRootIcdList; + + FLMUINT m_uiUseCount; // Number of F_Db structures currently + // pointing to this dictionary. + + // Name table + + F_NameTable * m_pNameTable; + + // Keep track of whether or not the database is operating in limited mode. This + // field is copied from the database when the dictionary is created or cloned + + FLMBOOL m_bInLimitedMode; + +friend class F_Database; +friend class F_Db; +friend class F_Query; +friend class F_DbCheck; +friend class F_BTreeInfo; +friend class F_AttrItem; +}; + +RCODE fdictGetDataType( // fdict.cpp + char * pszDataType, + FLMUINT * puiDataType); + +char * fdictGetDataTypeStr( // fdict.cpp + FLMUINT uiDataType); + +#endif // #ifndef FDICT_H diff --git a/version5/src/fdir.h b/version5/src/fdir.h new file mode 100644 index 0000000..f6982da --- /dev/null +++ b/version5/src/fdir.h @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// Desc: This interface encapsulates the concept of a file system directory. +// +// 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: fdir.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FDIR_H +#define FDIR_H + +class F_DirHdl; + +#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 XF_IO_FA_NORMAL FILE_ATTRIBUTE_NORMAL // Normal file + #define XF_IO_FA_RDONLY FILE_ATTRIBUTE_READONLY // Read only attribute + #define XF_IO_FA_HIDDEN FILE_ATTRIBUTE_HIDDEN // Hidden file + #define XF_IO_FA_SYSTEM FILE_ATTRIBUTE_SYSTEM // System file + #define XF_IO_FA_VOLUME FILE_ATTRIBUTE_VOLUME // Volume label + #define XF_IO_FA_DIRECTORY FILE_ATTRIBUTE_DIRECTORY // Directory + #define XF_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 XF_IO_FA_NORMAL 0x01 // Normal file, no attributes + #define XF_IO_FA_RDONLY 0x02 // Read only attribute + #define XF_IO_FA_HIDDEN 0x04 // Hidden file + #define XF_IO_FA_SYSTEM 0x08 // System file + #define XF_IO_FA_VOLUME 0x10 // Volume label + #define XF_IO_FA_DIRECTORY 0x20 // Directory + #define XF_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); + +/**************************************************************************** +Class: F_Dir +Desc: Implementation of the F_Directory interface for Win32 and Unix +****************************************************************************/ +class F_DirHdl : public IF_DirHdl, public XF_Base +{ +public: + + F_DirHdl(); + + virtual ~F_DirHdl() + { + + if( m_bFindOpen) + { + f_fileFindClose( &m_FindData); + } + } + + /*-- Iteration Methods ---------------------------------------------------*/ + /* Methods to enumerate the contents of a directory. */ + + RCODE XFLMAPI Next( void); // Set the iteration cursor to the next + // item in the directory + + /* --- Methods for accessing the current item (that the cursor is on) ----*/ + + const char * XFLMAPI CurrentItemName( void); + + FINLINE void XFLMAPI CurrentItemPath( + char * pszPath) + { + if( RC_OK( m_rc)) + { + f_strcpy( pszPath, m_szDirectoryPath); + gv_pFileSystem->pathAppend( pszPath, m_szFileName); + } + } + + FLMUINT64 XFLMAPI CurrentItemSize( void); + + FLMBOOL XFLMAPI CurrentItemIsDir( void); + + RCODE XFLMAPI OpenDir( // Open directory + const char * pszDirName, // Directory to be opened. + const char * pszPattern); // File name pattern. + + RCODE XFLMAPI CreateDir( // Create a directory. + const char * pszDirName); // Name of directory to be created. + + RCODE XFLMAPI RemoveDir( // Remove a directory + const char * pszDirPath); // Name of directory to be removed + +private: + + char m_szDirectoryPath[ F_PATH_MAX_SIZE]; // Path to directory + char m_szPattern[ F_PATH_MAX_SIZE]; // Pattern for matching + FLMUINT32 m_ui32RefCount; + RCODE m_rc; + FLMBOOL m_bFirstTime; // Indicates whether to use FindFirst or FindNext + FLMBOOL m_bFindOpen; // Indicates if need to call f_fileFindClose + FLMBOOL m_EOF; // Indicates if EndOfDirectory has been reached + char m_szFileName[ F_PATH_MAX_SIZE]; // Next item found in directory + FLMUINT m_uiAttrib; + F_IO_FIND_DATA m_FindData; +}; + +#endif // #ifndef FDIR_H diff --git a/version5/src/fdllmain.cpp b/version5/src/fdllmain.cpp new file mode 100644 index 0000000..dedb298 --- /dev/null +++ b/version5/src/fdllmain.cpp @@ -0,0 +1,231 @@ +//------------------------------------------------------------------------------ +// Desc: This is the standard functionality that all com servers must export +// +// 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: fdllmain.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fcomfact.h" + +static F_DbSystem * gv_pDbSystem = NULL; +static FLMUINT32 gv_ui32LockCount = 0; + +XFLMEXTC RCODE XFLMAPI DllCanUnloadNow( void); + +XFLMEXTC RCODE XFLMAPI DllGetClassObject( + RXFLMCLSID rclsid, + RXFLMIID riid, + void ** ppv); + +XFLMEXTC RCODE XFLMAPI DllStart( void); + +XFLMEXTC RCODE XFLMAPI DllStop( void); + +#if defined( FLM_UNIX) + +#ifdef __GNUC__ + void __attribute__ ((constructor)) flaim_init( void) {} + void __attribute__ ((destructor)) flaim_fini( void) {} +#elif !defined( FLM_SOLARIS) + extern "C" void _init(void) {} + extern "C" void _fini(void) {} +#endif + +#elif defined( FLM_WIN) + + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + + static HMODULE s_module; + + #pragma comment(linker, "/export:DllCanUnloadNow=_DllCanUnloadNow@0,PRIVATE") + #pragma comment(linker, "/export:DllGetClassObject=_DllGetClassObject@12,PRIVATE") + #pragma comment(linker, "/export:DllStart=_DllStart@0,PRIVATE") + #pragma comment(linker, "/export:DllStop=_DllStop@0,PRIVATE") + #pragma comment(linker, "/export:_XTCOM_Table,DATA") + +#elif !defined( FLM_NLM) + #error platform not supported. +#endif + +/****************************************************************************** +Desc: +******************************************************************************/ +void LockModule(void) +{ + ftkAtomicIncrement( &gv_ui32LockCount); +// flmAssert( gv_ui32LockCount < 20); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +void UnlockModule(void) +{ + ftkAtomicDecrement( &gv_ui32LockCount); +} + +/****************************************************************************** +Desc: Returns 0 if it's okay to unload, or a non-zero status + code if not. +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllCanUnloadNow( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( gv_pDbSystem); + + if( gv_ui32LockCount > 1) + { + rc = RC_SET( NE_XFLM_FAILURE); + } + else + { + // gv_ui32LockCount should be 1 because gv_pDbSystem is non-null. + + flmAssert( gv_ui32LockCount == 1); + + // Check for open databases + + f_mutexLock( gv_XFlmSysData.hShareMutex); + + if (gv_XFlmSysData.pDatabaseHashTbl) + { + FBUCKET * pDatabaseHashTbl; + FLMUINT uiCnt; + + for (uiCnt = 0, pDatabaseHashTbl = gv_XFlmSysData.pDatabaseHashTbl; + uiCnt < FILE_HASH_ENTRIES; + uiCnt++, pDatabaseHashTbl++) + { + if (pDatabaseHashTbl->pFirstInBucket != NULL) + { + rc = RC_SET( NE_XFLM_FAILURE); + break; + } + } + } + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + return( rc); +} + +/****************************************************************************** +Desc: Returns the desired interface to the class object for + the specified service class. +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllGetClassObject( + RXFLMCLSID rclsid, + RXFLMIID riid, + void ** ppv) +{ + static F_DbSystemFactory gv_DbSysFactory; + + if( f_memcmp( &rclsid, &Internal_CLSID_F_DbSystemFactory, + sizeof( Internal_CLSID_F_DbSystemFactory)) == 0) + { + return( gv_DbSysFactory.QueryInterface( riid, ppv)); + } + + *ppv = NULL; + return( RC_SET( NE_XFLM_CLASS_NOT_AVAILABLE)); +} + +/****************************************************************************** +Desc: Called by PSA when it loads the library. Must return 0 for + success, or a non-zero error code. +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllStart( void) +{ + RCODE rc = NE_XFLM_OK; + + if( (gv_pDbSystem = f_new F_DbSystem) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + + if( RC_BAD( rc = gv_pDbSystem->init())) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + if( gv_pDbSystem) + { + gv_pDbSystem->Release(); + gv_pDbSystem = NULL; + } + } + + return( rc); +} + +/****************************************************************************** +Desc: Called by PSA when it unloads the library. The return value + is ignored. +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllStop( void) +{ + if( gv_pDbSystem) + { + flmAssert( gv_ui32LockCount == 1); + + gv_pDbSystem->exit(); + gv_pDbSystem->Release(); + gv_pDbSystem = NULL; + } + + return( NE_XFLM_OK); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllRegisterServer( + const char *) +{ + return( NE_XFLM_OK); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +XFLMEXTC RCODE XFLMAPI DllUnregisterServer( void) +{ + return( NE_XFLM_OK); +} + +/****************************************************************************** +Desc: This is an array of all the CLSID's that XFlaim implements. +******************************************************************************/ +extern "C" const XFLMCLSID * XTCOM_Table[] = +{ + &Internal_CLSID_F_DbSystemFactory, + 0 +}; diff --git a/version5/src/fdoclist.cpp b/version5/src/fdoclist.cpp new file mode 100644 index 0000000..e665851 --- /dev/null +++ b/version5/src/fdoclist.cpp @@ -0,0 +1,255 @@ +//------------------------------------------------------------------------------ +// Desc: Document list object 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: fdoclist.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define MAX_PENDING_NODES 255 + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_NodeList::findNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId, + FLMUINT * puiPos + ) +{ + FLMBOOL bFound = FALSE; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblCollection; + FLMUINT64 ui64TblDocument; + FLMUINT64 ui64TblNodeId; + FLMINT iCmp; + + // Do binary search in the table + + if ((uiTblSize = m_uiNumNodes) == 0) + { + *puiPos = 0; + goto Exit; + } + + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + + uiTblCollection = m_pNodeTbl[ uiMid].uiCollection; + ui64TblDocument = m_pNodeTbl[ uiMid].ui64Document; + ui64TblNodeId = m_pNodeTbl[ uiMid].ui64NodeId; + + if( uiCollection == uiTblCollection) + { + if( ui64Document == ui64TblDocument) + { + if( ui64NodeId == ui64TblNodeId) + { + iCmp = 0; + } + else if( ui64NodeId < ui64TblNodeId) + { + iCmp = -1; + } + else + { + iCmp = 1; + } + } + else if( ui64Document < ui64TblDocument) + { + iCmp = -1; + } + else + { + iCmp = 1; + } + } + else if( uiCollection < uiTblCollection) + { + iCmp = -1; + } + else + { + iCmp = 1; + } + + if (!iCmp) + { + // Found Match + + bFound = TRUE; + *puiPos = uiMid; + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + // Done, item not found + + *puiPos = (iCmp < 0 + ? uiMid + : uiMid + 1); + goto Exit; + } + + if (iCmp < 0) + { + if (uiMid == 0) + { + *puiPos = 0; + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + *puiPos = uiMid + 1; + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( bFound); +} + +/***************************************************************************** +Desc: Add a node to the node list. If it is already there, it is ok. +******************************************************************************/ +RCODE F_NodeList::addNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiInsertPos; + + // Cannot allow collection or document to be zero + + flmAssert( uiCollection && ui64Document); + + if( m_uiLastCollection == uiCollection && + m_ui64LastDocument == ui64Document && + m_ui64LastNodeId == ui64NodeId) + { + goto Exit; + } + + if( !findNode( uiCollection, ui64Document, ui64NodeId, &uiInsertPos)) + { + // Have we reached the limit of the number of documents we will + // keep pending in a transaction? + + if( m_uiNumNodes == MAX_PENDING_NODES) + { + rc = RC_SET( NE_XFLM_TOO_MANY_PENDING_NODES); + goto Exit; + } + + // See if we need to allocate the table + + if( !m_pNodeTbl) + { + if (RC_BAD( rc = f_alloc( + sizeof( NODE_LIST_ITEM) * MAX_PENDING_NODES, &m_pNodeTbl))) + { + goto Exit; + } + + m_uiNodeTblSize = MAX_PENDING_NODES; + } + + flmAssert( uiInsertPos <= m_uiNumNodes); + + // Make room for the new node ID + + if( uiInsertPos < m_uiNumNodes) + { + f_memmove( &m_pNodeTbl[ uiInsertPos+1], + &m_pNodeTbl[ uiInsertPos], + sizeof( NODE_LIST_ITEM) * (m_uiNumNodes - uiInsertPos)); + } + + m_pNodeTbl[ uiInsertPos].uiCollection = uiCollection; + m_pNodeTbl[ uiInsertPos].ui64Document = ui64Document; + m_pNodeTbl[ uiInsertPos].ui64NodeId = ui64NodeId; + m_uiNumNodes++; + } + + // Save collection and document id - this is an optimization + // that will keep us from calling findNode too much if + // we are working inside the same document. + + m_uiLastPosition = uiInsertPos; + m_uiLastCollection = uiCollection; + m_ui64LastDocument = ui64Document; + m_ui64LastNodeId = ui64NodeId; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Remove a node from the node list. If it is not there, it is ok. +******************************************************************************/ +void F_NodeList::removeNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId) +{ + FLMUINT uiPos; + + // Cannot allow collection or document to be zero + + flmAssert( uiCollection && ui64Document); + + if( m_uiLastCollection == uiCollection && + m_ui64LastDocument == ui64Document && + m_ui64LastNodeId == ui64NodeId) + { + flmAssert( m_uiLastPosition < m_uiNumNodes); + removeNode( m_uiLastPosition); + } + else + { + if( findNode( uiCollection, ui64Document, ui64NodeId, &uiPos)) + { + flmAssert( uiPos < m_uiNumNodes); + removeNode( uiPos); + } + } +} diff --git a/version5/src/fdom.cpp b/version5/src/fdom.cpp new file mode 100644 index 0000000..c226653 --- /dev/null +++ b/version5/src/fdom.cpp @@ -0,0 +1,18209 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" + +// External data + +extern FLMBYTE gv_ucSENLengthArray[]; + +// Local constants + +#define SEN_RESERVE_BYTES 5 + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE RCODE F_Db::attrIsInIndexDef( + FLMUINT uiAttrNameId, + FLMBOOL * pbIsInIndexDef) +{ + RCODE rc = NE_XFLM_OK; + F_AttrElmInfo defInfo; + + if( RC_BAD( rc = m_pDict->getAttribute( this, uiAttrNameId, &defInfo))) + { + return( rc); + } + + *pbIsInIndexDef = defInfo.m_pFirstIcd ? TRUE : FALSE; + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: This class converts a text stream of Unicode or UTF8 to an ASCII + text stream. +******************************************************************************/ +class F_AsciiIStream : public IF_IStream, public XF_Base +{ +public: + + F_AsciiIStream( + FLMBYTE * pucText, + FLMUINT uiNumBytesInBuffer, + eXFlmTextType eTextType) + { + m_pucText = pucText; + m_pucCurrPtr = pucText; + m_uiCurrChar = 0; + m_eTextType = eTextType; + + if( uiNumBytesInBuffer) + { + m_pucEnd = &pucText[ uiNumBytesInBuffer]; + } + else + { + m_pucEnd = NULL; + } + } + + virtual ~F_AsciiIStream() + { + } + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE void XFLMAPI close( void) + { + } + +private: + + const FLMBYTE * m_pucText; + const FLMBYTE * m_pucCurrPtr; + const FLMBYTE * m_pucEnd; + FLMUINT m_uiCurrChar; + eXFlmTextType m_eTextType; +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +FLMUINT32 XFLMAPI F_BTreeIStream::Release( void) +{ + FLMUINT32 ui32RefCnt = --m_ui32RefCnt; + if (m_ui32RefCnt == 0) + { + close(); + if( gv_XFlmSysData.pNodePool) + { + m_ui32RefCnt = 1; + gv_XFlmSysData.pNodePool->insertBTreeIStream( this); + return( 0); + } + else + { + delete this; + } + } + return ui32RefCnt; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::canSetValue( + F_Db * pDb, + FLMUINT uiDataType) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->m_pDatabase; + IF_DOMNode * pNode = NULL; + eDomNodeType eNodeType = getNodeType(); + + if( eNodeType < ELEMENT_NODE || eNodeType > ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Cannot set a value without a data type + + if( uiDataType == XFLM_NODATA_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + + // If the node is read-only, don't allow it to be changed + + if( getModeFlags() & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + + // If this is a comment or CDATA node, only allow text values + + if (uiDataType != XFLM_TEXT_TYPE && + (eNodeType == COMMENT_NODE || eNodeType == CDATA_SECTION_NODE)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // If the node is a data node and it has already been linked + // into the document, its data type cannot be changed. + + if( getParentId() && eNodeType == DATA_NODE && uiDataType != getDataType()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Cannot allow a value to be set on this node if a pending input stream + // is still open. + + if( pDatabase->m_pPendingInput && + pDatabase->m_pPendingInput != m_pCachedNode) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT32 XFLMAPI F_DOMNode::Release( void) +{ + FLMUINT32 ui32RefCnt = --m_ui32RefCnt; + + if (m_ui32RefCnt == 0) + { + if( gv_XFlmSysData.pNodeCacheMgr) + { + m_ui32RefCnt = 1; + gv_XFlmSysData.pNodeCacheMgr->insertDOMNode( this); + return( 0); + } + else + { + delete this; + } + } + + return ui32RefCnt; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::isChildTypeValid( + eDomNodeType eChildNodeType) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bTypeValid = FALSE; + + if( !m_pCachedNode) + { + rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); + goto Exit; + } + + switch( getNodeType()) + { + case ELEMENT_NODE: + { + if (eChildNodeType == ELEMENT_NODE || + (eChildNodeType == DATA_NODE && + getDataType() != XFLM_NODATA_TYPE && getDataLength() == 0) || + eChildNodeType == COMMENT_NODE || + eChildNodeType == PROCESSING_INSTRUCTION_NODE || + eChildNodeType == CDATA_SECTION_NODE) + { + bTypeValid = TRUE; + } + + break; + } + + case DOCUMENT_NODE: + { + if (eChildNodeType == ELEMENT_NODE || + eChildNodeType == PROCESSING_INSTRUCTION_NODE || + eChildNodeType == COMMENT_NODE) + { + bTypeValid = TRUE; + } + break; + } + + case ATTRIBUTE_NODE: + case DATA_NODE: + case CDATA_SECTION_NODE: + case PROCESSING_INSTRUCTION_NODE: + case COMMENT_NODE: + { + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if (!bTypeValid) + { + rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::isDescendantOf( + F_Db * pDb, + F_DOMNode * pAncestor, + FLMBOOL * pbDescendant) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pParent = NULL; + FLMUINT64 ui64AncestorId; + FLMUINT64 ui64ThisParentId; + + *pbDescendant = FALSE; + + if( !m_pCachedNode) + { + goto Exit; + } + + ui64AncestorId = pAncestor->getNodeId(); + ui64ThisParentId = getParentId(); + + if( ui64ThisParentId == ui64AncestorId) + { + *pbDescendant = TRUE; + goto Exit; + } + + if( !ui64ThisParentId || + (ui64AncestorId != ui64ThisParentId && + ui64ThisParentId == getDocumentId()) || !pAncestor->getFirstChildId()) + { + goto Exit; + } + + if( RC_BAD( rc = getParentNode( pDb, (IF_DOMNode **)&pParent))) + { + goto Exit; + } + + while( pParent) + { + if( pParent->getNodeId() == ui64AncestorId) + { + *pbDescendant = TRUE; + goto Exit; + } + + if( RC_BAD( rc = pParent->getParentNode( pDb, (IF_DOMNode **)&pParent))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + +Exit: + + if( pParent) + { + pParent->Release(); + } + + return( rc); +} + +/***************************************************************************** +Notes: When an node is unlinked, its document or root node ID is not + changed. Once unlinked, the node can be deleted or re-linked + elsewhere within the same document. + This routine assumes that the caller has checked the cannot delete + bits and read-only bits if necessary. +******************************************************************************/ +RCODE F_DOMNode::unlinkNode( + F_Db * pDb, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pTmpNode = NULL; + F_COLLECTION * pCollection = NULL; + FLMUINT64 ui64OldPrevSib = 0; + FLMUINT64 ui64OldNextSib = 0; + FLMBOOL bChangedThisHeader = FALSE; + eDomNodeType eNodeType; + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Unlink the node from its siblings and parent + + if( (ui64OldPrevSib = getPrevSibId()) != 0) + { + if( RC_BAD( rc = pDb->getNode( getCollection(), getPrevSibId(), + &pTmpNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + pTmpNode->setNextSibId( getNextSibId()); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) + { + goto Exit; + } + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + setPrevSibId( 0); + bChangedThisHeader = TRUE; + } + + if( (ui64OldNextSib = getNextSibId()) != 0) + { + if( RC_BAD( rc = pDb->getNode( getCollection(), getNextSibId(), + &pTmpNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + pTmpNode->setPrevSibId( ui64OldPrevSib); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) + { + goto Exit; + } + + if( !bChangedThisHeader) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + } + + setNextSibId( 0); + bChangedThisHeader = TRUE; + } + + if( getParentId()) + { + FLMBOOL bChangedTmpHeader = FALSE; + + if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), + &pTmpNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND || + rc == NE_XFLM_DOM_NODE_DELETED) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( eNodeType == ANNOTATION_NODE) + { + if (!bChangedTmpHeader) + { + if (RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + bChangedTmpHeader = TRUE; + } + + pTmpNode->setAnnotationId( 0); + } + else + { + // If the parent node is one whose child elements must all + // be unique, we must remove the node from the node list of the + // parent. + + if( pTmpNode->getModeFlags() & FDOM_HAVE_CELM_LIST) + { + FLMUINT uiElmOffset; + + if( !bChangedTmpHeader) + { + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + bChangedTmpHeader = TRUE; + } + + // Only element nodes should be child nodes of this parent. + + flmAssert( eNodeType == ELEMENT_NODE); + + if( !pTmpNode->findChildElm( getNameId(), &uiElmOffset)) + { + // Child node should have been found. + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->removeChildElm( uiElmOffset))) + { + goto Exit; + } + } + + // If this is a data node, we need to update the parent element's + // data node count + + if( eNodeType == DATA_NODE) + { + if( !bChangedTmpHeader) + { + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + bChangedTmpHeader = TRUE; + } + + flmAssert( pTmpNode->getDataChildCount()); + pTmpNode->setDataChildCount( pTmpNode->getDataChildCount() - 1); + } + + if( !ui64OldPrevSib) + { + if( !bChangedTmpHeader) + { + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + bChangedTmpHeader = TRUE; + } + + flmAssert( pTmpNode->canHaveChildren()); + pTmpNode->setFirstChildId( ui64OldNextSib); + } + + if( !ui64OldNextSib) + { + if( !bChangedTmpHeader) + { + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + bChangedTmpHeader = TRUE; + } + + flmAssert( pTmpNode->canHaveChildren()); + pTmpNode->setLastChildId( ui64OldPrevSib); + } + } + + if( bChangedTmpHeader) + { + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, uiFlags))) + { + goto Exit; + } + } + + if( !bChangedThisHeader) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + } + + setParentId( 0); + bChangedThisHeader = TRUE; + } + + // If this is a root node, the document list pointers may need + // to be updated + + if( isRootNode()) + { + if( eNodeType != DOCUMENT_NODE && eNodeType != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Get a pointer to the collection + + if( RC_BAD( rc = pDb->m_pDict->getCollection( + getCollection(), &pCollection))) + { + goto Exit; + } + + if( pCollection->ui64FirstDocId == getNodeId() || + pCollection->ui64LastDocId == getNodeId()) + { + // Clone the dictionary since the first/last document ID + // values of the collection will be changed + + if( !(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if( RC_BAD( rc = pDb->dictClone())) + { + goto Exit; + } + } + + // Get a pointer to the new collection + + if( RC_BAD( rc = pDb->m_pDict->getCollection( + getCollection(), &pCollection))) + { + goto Exit; + } + + // Change the first and/or last document IDs + + if( pCollection->ui64FirstDocId == getNodeId()) + { + pCollection->ui64FirstDocId = ui64OldNextSib; + pCollection->bNeedToUpdateNodes = TRUE; + } + + if( pCollection->ui64LastDocId == getNodeId()) + { + pCollection->ui64LastDocId = ui64OldPrevSib; + pCollection->bNeedToUpdateNodes = TRUE; + } + } + } + + // If this is a data node, clear its name tag + + if( eNodeType == DATA_NODE) + { + if( !bChangedThisHeader) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + } + + setNameId( 0); + bChangedThisHeader = TRUE; + } + + if( bChangedThisHeader) + { + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, uiFlags))) + { + goto Exit; + } + } + +Exit: + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::addModeFlags( + F_Db * pDb, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Only need to set flags if they are not all currently set. + + if( (getModeFlags() & uiFlags) != uiFlags) + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->addModeFlags( + pDb, m_uiAttrNameId, uiFlags))) + { + goto Exit; + } + } + else + { + m_pCachedNode->setFlags( uiFlags); + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( pRfl->logNodeFlagsUpdate( + pDb, getCollection(), m_pCachedNode->getNodeId(), + m_uiAttrNameId, uiFlags, TRUE))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::removeModeFlags( + F_Db * pDb, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Only need to remove the flags if any of them are currently set. + + if( getModeFlags() & uiFlags) + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->removeModeFlags( + pDb, m_uiAttrNameId, uiFlags))) + { + goto Exit; + } + } + else + { + m_pCachedNode->unsetFlags( uiFlags); + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( pRfl->logNodeFlagsUpdate( + pDb, getCollection(), m_pCachedNode->getNodeId(), + m_uiAttrNameId, uiFlags, FALSE))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getData( + F_Db * pDb, + FLMBYTE * pucBuffer, + FLMUINT * puiLength) +{ + RCODE rc = NE_XFLM_OK; + IF_PosIStream * pIStream = NULL; + FLMBOOL bStartedTrans = FALSE; + F_NodeBufferIStream bufferIStream; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // If a NULL buffer is passed in, just return the + // data length + + if( !pucBuffer) + { + rc = getDataLength( pDb, puiLength); + goto Exit; + } + + if( RC_BAD( rc = getIStream( pDb, &bufferIStream, &pIStream))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsBinary( pIStream, pucBuffer, + *puiLength, 0, puiLength))) + { + goto Exit; + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getDataLength( + IF_Db * ifpDb, + FLMUINT * puiLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + eDomNodeType eNodeType; + F_Db * pDb = (F_Db *)ifpDb; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getDataLength( + m_uiAttrNameId, puiLength))) + { + goto Exit; + } + } + else if( (*puiLength = getDataLength()) == 0) + { + // If this is an element node, we will automatically search + // for the first data node (if any) + + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + F_DOMNode * pNode = NULL; + + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + *puiLength = pNode->getDataLength(); + pNode->Release(); + } + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::insertBefore( + IF_Db * ifpDb, + IF_DOMNode * ifpNewChild, + IF_DOMNode * ifpRefChild) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pFirstNewChild = NULL; + F_DOMNode * pLastNewChild = NULL; + F_DOMNode * pCurNewNode = NULL; + F_DOMNode * pNextSib = NULL; + F_DOMNode * pInsertBefore = NULL; + F_DOMNode * pTmpNode = NULL; + FLMBOOL bDescendant; + FLMBOOL bDone = FALSE; + FLMUINT64 ui64Tmp; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartOfUpdate; + F_DOMNode * pDataElementNode = NULL; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + F_DOMNode * pNewChild = (F_DOMNode *)ifpNewChild; + F_DOMNode * pRefChild = (F_DOMNode *)ifpRefChild; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bUpdatedNode = FALSE; + FLMUINT uiRflToken = 0; + FLMUINT64 ui64RefChildId = 0; + eDomNodeType eThisNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eThisNodeType = getNodeType(); + + if( eThisNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + if( !pNewChild) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = pNewChild->syncFromDb( pDb))) + { + goto Exit; + } + + if( pRefChild) + { + if( RC_BAD( rc = pRefChild->syncFromDb( pDb))) + { + goto Exit; + } + + if( pRefChild->getNodeType() == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + ui64RefChildId = pRefChild->getNodeId(); + } + + if( pNewChild->getDatabase() != getDatabase() || + pNewChild->getCollection() != getCollection()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( pNewChild->getNodeType() == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // If this is a node whose children are all supposed to be unique, + // make sure that is the case, and find out where to insert the node. + + if( getModeFlags() & FDOM_HAVE_CELM_LIST) + { + FLMUINT uiInsertPos; + + // Only element child nodes are allowed. + + if( pNewChild->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET( NE_XFLM_DOM_INVALID_CHILD_TYPE); + goto Exit; + } + + // All of the element names must be unique. + + if( m_pCachedNode->findChildElm( pNewChild->getNameId(), + &uiInsertPos)) + { + rc = RC_SET( NE_XFLM_DOM_DUPLICATE_ELEMENT); + goto Exit; + } + + // Element was not found, insert into the list of elements. + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bUpdatedNode = TRUE; + + if( RC_BAD( rc = m_pCachedNode->insertChildElm( uiInsertPos, + pNewChild->getNameId(), + pNewChild->getNodeId()))) + { + goto Exit; + } + } + + // If a non-NULL reference child was passed in, + // do some basic sanity checks + + if( pRefChild) + { + if( pRefChild->getDatabase() != getDatabase() || + pRefChild->getCollection() != getCollection()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( pNewChild->getNodeId() == pRefChild->getNodeId()) + { + rc = RC_SET( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + + if( pRefChild->getParentId() != getNodeId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + } + + pFirstNewChild = pNewChild; + pFirstNewChild->AddRef(); + + pLastNewChild = pNewChild; + pLastNewChild->AddRef(); + + if( pRefChild) + { + pInsertBefore = pRefChild; + pInsertBefore->AddRef(); + } + + pCurNewNode = pFirstNewChild; + pCurNewNode->AddRef(); + + bStartOfUpdate = TRUE; + for( ;;) + { + // Make sure it is legal for the new child node to be + // linked to this node + + if( RC_BAD( rc = isChildTypeValid( pCurNewNode->getNodeType()))) + { + goto Exit; + } + + // If the node being inserted is not from the same document, + // we cannot perform the operation + + if( pCurNewNode->getDocumentId() != getDocumentId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_WRONG_DOCUMENT_ERR); + goto Exit; + } + + // A document node can only have one element node and one document type + // node. + + if( eThisNodeType == DOCUMENT_NODE) + { + if( pCurNewNode->getNodeType() == ELEMENT_NODE && + getFirstChildId()) + { + if( RC_BAD( rc = getChild( ifpDb, ELEMENT_NODE, + (IF_DOMNode **)&pTmpNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + } + } + + // Get the next sibling node (if any) before we + // change the tree + + if( pNextSib) + { + pNextSib->Release(); + pNextSib = NULL; + } + + if( RC_BAD( rc = pCurNewNode->getNextSibling( pDb, + (IF_DOMNode **)&pNextSib))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + bDone = TRUE; + } + else + { + goto Exit; + } + } + + bMustAbortOnError = TRUE; + + if( pDataElementNode) + { + pDataElementNode->Release(); + pDataElementNode = NULL; + } + + // Do indexing work before making changes + + if( pCurNewNode->getNameId()) + { + if( pCurNewNode->getNodeType() == DATA_NODE) + { + if( pCurNewNode->getParentId()) + { + if( RC_BAD( rc = pCurNewNode->getParentNode( pDb, + (IF_DOMNode **)&pDataElementNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), pDataElementNode, + IX_DEL_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + if( !getFirstChildId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + bStartOfUpdate = FALSE; + } + } + else + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), pCurNewNode, IX_UNLINK_NODE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + } + + // Remove pCurNewNode from the tree + + if( pCurNewNode->getModeFlags() & (FDOM_CANNOT_DELETE | FDOM_READ_ONLY)) + { + rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); + goto Exit; + } + + if( RC_BAD( rc = pCurNewNode->unlinkNode( pDb, 0))) + { + goto Exit; + } + + if( pDataElementNode) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), pDataElementNode, IX_ADD_NODE_VALUE, + bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + } + + // Make sure that "this" node is not a child of the node + // being inserted + + if( RC_BAD( rc = isDescendantOf( pDb, pCurNewNode, &bDescendant))) + { + goto Exit; + } + + if( bDescendant) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + + // If the node being inserted isn't from the same document, + // we cannot perform the operation + + if( pCurNewNode->getDocumentId() != getDocumentId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_WRONG_DOCUMENT_ERR); + goto Exit; + } + + if( RC_BAD( rc = pCurNewNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + if( pInsertBefore) + { + if( RC_BAD( rc = pInsertBefore->makeWriteCopy( pDb))) + { + goto Exit; + } + + // Insert the new child before the ref child + + if( !pInsertBefore->getPrevSibId()) + { + if( !bUpdatedNode) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + bUpdatedNode = TRUE; + } + + // Change the parent's first child pointer + + setFirstChildId( pCurNewNode->getNodeId()); + } + else + { + pCurNewNode->setPrevSibId( pInsertBefore->getPrevSibId()); + + // Get the prev sib and set its next sib value + + if( RC_BAD( rc = pDb->getNode( + getCollection(), pInsertBefore->getPrevSibId(), &pTmpNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + pTmpNode->setNextSibId( pCurNewNode->getNodeId()); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + pTmpNode->Release(); + pTmpNode = NULL; + } + + pInsertBefore->setPrevSibId( pCurNewNode->getNodeId()); + pCurNewNode->setNextSibId( pInsertBefore->getNodeId()); + + if( RC_BAD( rc = pDb->updateNode( pInsertBefore->m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + } + else + { + if( (ui64Tmp = getLastChildId()) != 0) + { + // Get the prev sib and set its next sib value + + if( RC_BAD( rc = pDb->getNode( + getCollection(), getLastChildId(), &pTmpNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + pTmpNode->setNextSibId( pCurNewNode->getNodeId()); + pCurNewNode->setPrevSibId( getLastChildId()); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + pTmpNode->Release(); + pTmpNode = NULL; + } + + pCurNewNode->setPrevSibId( getLastChildId()); + + if( !bUpdatedNode) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + bUpdatedNode = TRUE; + } + + setLastChildId( pCurNewNode->getNodeId()); + + if( !ui64Tmp) + { + setFirstChildId( pCurNewNode->getNodeId()); + } + } + + // Need to increment the data child node count + + if( pCurNewNode->getNodeType() == DATA_NODE && + eThisNodeType == ELEMENT_NODE) + { + setDataChildCount( getDataChildCount() + 1); + bUpdatedNode = TRUE; + } + + if( bUpdatedNode) + { + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + // Reset to FALSE for next time around in loop. + + bUpdatedNode = FALSE; + } + + pCurNewNode->setParentId( getNodeId()); + + // Need to set the naming tag and data type of the node. + + if( pCurNewNode->getNodeType() == DATA_NODE && + eThisNodeType == ELEMENT_NODE) + { + if( pCurNewNode->getDataType() == XFLM_NODATA_TYPE) + { + pCurNewNode->setDataType( getDataType()); + } + else if( getDataType() != pCurNewNode->getDataType()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + pCurNewNode->setNameId( getNameId()); + } + + // An additional restriction on unique child elements is that they + // must have a node ID greater than the parent element. This allows + // them to be stored using a very compact representation. + + if( getModeFlags() & FDOM_HAVE_CELM_LIST) + { + if( pCurNewNode->getNodeId() < getNodeId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_CHILD_ELM_NODE_ID); + goto Exit; + } + } + + if( RC_BAD( rc = pDb->updateNode( pCurNewNode->m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + if( pCurNewNode->getNodeId() == pLastNewChild->getNodeId()) + { + bDone = TRUE; + } + + // Do post-link indexing work + + if( pCurNewNode->getNameId()) + { + if( pCurNewNode->getNodeType() == DATA_NODE) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), pCurNewNode, IX_LINK_NODE, bStartOfUpdate))) + { + goto Exit; + } + } + + bStartOfUpdate = FALSE; + } + + pCurNewNode->Release(); + pCurNewNode = pNextSib; + pNextSib = NULL; + + if( bDone) + { + break; + } + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logInsertBefore( + pDb, getCollection(), getNodeId(), pNewChild->getNodeId(), + ui64RefChildId))) + { + goto Exit; + } + +Exit: + + // Release any nodes we are still holding + + if( pFirstNewChild) + { + pFirstNewChild->Release(); + } + + if( pLastNewChild) + { + pLastNewChild->Release(); + } + + if( pCurNewNode) + { + pCurNewNode->Release(); + } + + if( pDataElementNode) + { + pDataElementNode->Release(); + } + + if( pNextSib) + { + pNextSib->Release(); + } + + if( pInsertBefore) + { + pInsertBefore->Release(); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setNumber64( + IF_Db * ifpDb, + FLMINT64 i64Value, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNeg = FALSE; + F_Db * pDb = (F_Db*)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + IF_DOMNode * pNode = NULL; + FLMUINT uiCollection; + FLMUINT uiRflToken = 0; + FLMUINT uiNodeDataType; + FLMUINT uiValLen; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bIsIndexed = TRUE; + FLMBOOL bStartOfUpdate = TRUE; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = canSetValue( pDb, XFLM_NUMBER_TYPE))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Grab some information about the node + + uiCollection = getCollection(); + eNodeType = getNodeType(); + + // Special case for element nodes + + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + bMustAbortOnError = TRUE; + rc = ((F_DOMNode *)pNode)->setNumber64( pDb, i64Value, + ui64Value, uiEncDefId); + goto Exit; + } + + // If the number is less than zero, invert the sign + + if( i64Value < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)(-i64Value); + } + else if( i64Value) + { + ui64Value = (FLMUINT64)i64Value; + } + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + + if( RC_BAD( rc = m_pCachedNode->setNumber64( pDb, + m_uiAttrNameId, ui64Value, bNeg, uiEncDefId))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, m_uiAttrNameId))) + { + goto Exit; + } + + goto Exit; + } + + // Generate the storage value + + uiNodeDataType = getDataType(); + + if( uiNodeDataType == XFLM_NUMBER_TYPE || + uiNodeDataType == XFLM_NODATA_TYPE) + { + if( bNeg) + { + if( (getModeFlags() & FDOM_SIGNED_QUICK_VAL) && + i64Value == getQuickINT64()) + { + goto Exit; + } + } + else if( (getModeFlags() & FDOM_UNSIGNED_QUICK_VAL) && + ui64Value == getQuickUINT64()) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + else + { + bIsIndexed = FALSE; + } + + if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) + { + goto Exit; + } + + if( getDataBufSize() < FLM_MAX_NUM_BUF_SIZE) + { + if( RC_BAD( rc = resizeDataBuffer( FLM_MAX_NUM_BUF_SIZE, FALSE))) + { + goto Exit; + } + } + + uiValLen = FLM_MAX_NUM_BUF_SIZE; + if( RC_BAD( rc = flmNumber64ToStorage( ui64Value, + &uiValLen, getDataPtr(), bNeg, FALSE))) + { + goto Exit; + } + + if( uiNodeDataType == XFLM_NODATA_TYPE) + { + uiNodeDataType = XFLM_NUMBER_TYPE; + setDataType( uiNodeDataType); + } + } + else if( uiNodeDataType == XFLM_TEXT_TYPE) + { + FLMBYTE ucNumBuf[ 64]; + FLMBYTE * pucSen; + FLMUINT uiSenLen; + + if( !bNeg) + { + f_ui64toa( ui64Value, (char *)&ucNumBuf[ 1]); + } + else + { + f_i64toa( i64Value, (char *)&ucNumBuf[ 1]); + } + + uiValLen = f_strlen( ucNumBuf); + pucSen = &ucNumBuf[ 0]; + uiSenLen = flmEncodeSEN( uiValLen, &pucSen, (FLMUINT)0); + flmAssert( uiSenLen == 1); + uiValLen += uiSenLen + 1; + + // If the value isn't being changed, there is no need to continue + + if( getDataLength() == uiValLen && + f_memcmp( getDataPtr(), ucNumBuf, uiValLen) == 0) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + else + { + bIsIndexed = FALSE; + } + + if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) + { + goto Exit; + } + + // Allocate or re-allocate the buffer + + if( calcDataBufSize( uiValLen) > getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) + { + goto Exit; + } + } + + if( uiValLen) + { + f_memcpy( getDataPtr(), ucNumBuf, uiValLen); + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + + setEncDefId( uiEncDefId); + setDataLength( uiValLen); + + // Clear the "on disk" flag (if set), as well as the quick nums + + unsetFlags( FDOM_VALUE_ON_DISK | + FDOM_FIXED_SIZE_HEADER | + FDOM_UNSIGNED_QUICK_VAL | + FDOM_SIGNED_QUICK_VAL); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + if( !bNeg) + { + setUINT64( ui64Value); + } + else + { + setINT64( i64Value); + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( !uiEncDefId) + { + if( RC_BAD( rc = pRfl->logNodeSetNumberValue( pDb, + uiCollection, getNodeId(), ui64Value, bNeg))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pRfl->logEncryptedNodeUpdate( pDb, m_pCachedNode))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( pNode) + { + pNode->Release(); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setStorageValue( + F_Db * pDb, + void * pvValue, + FLMUINT uiValueLen, + FLMUINT uiEncDefId, + FLMBOOL bLast) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + eDomNodeType eNodeType; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bIsIndexed = TRUE; + F_Database * pDatabase = pDb->m_pDatabase; + FLMBOOL bFirst = pDatabase->m_pPendingInput ? FALSE : TRUE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getDataType() == XFLM_UNKNOWN_TYPE || (bStartedTrans && !bLast)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + bMustAbortOnError = TRUE; + uiCollection = getCollection(); + eNodeType = getNodeType(); + + if( eNodeType == ELEMENT_NODE || + eNodeType == DATA_NODE) + { + if( !getNameId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + + if( bFirst) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_DEL_NODE_VALUE, TRUE, &bIsIndexed))) + { + goto Exit; + } + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + } + + if( eNodeType == ELEMENT_NODE || + eNodeType == DATA_NODE || + eNodeType == COMMENT_NODE) + { + FLMUINT uiBytesToCopy; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + + if( bFirst) + { + setEncDefId( uiEncDefId); + } + + if( bFirst && bLast) + { + if( calcDataBufSize( uiValueLen) != getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( uiValueLen, FALSE))) + { + goto Exit; + } + } + + setDataLength( uiValueLen); + + if( uiValueLen) + { + f_memcpy( getDataPtr(), pvValue, uiValueLen); + } + } + else + { + if( bFirst) + { + if( RC_BAD( rc = m_pCachedNode->openPendingInput( pDb, + getDataType()))) + { + goto Exit; + } + } + + while( uiValueLen) + { + uiBytesToCopy = pDatabase->m_uiUpdBufferSize - + pDatabase->m_uiUpdByteCount; + + uiBytesToCopy = uiBytesToCopy > uiValueLen + ? uiValueLen + : uiBytesToCopy; + + if( !uiBytesToCopy) + { + if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + + continue; + } + + f_memcpy( &pDatabase->m_pucUpdBuffer[ pDatabase->m_uiUpdByteCount], + pucValue, uiBytesToCopy); + pucValue += uiBytesToCopy; + uiValueLen -= uiBytesToCopy; + pDatabase->m_uiUpdByteCount += uiBytesToCopy; + } + + if( bLast) + { + if( pDatabase->m_bUpdFirstBuf) + { + if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pCachedNode->flushPendingInput( pDb, TRUE))) + { + goto Exit; + } + + pDatabase->endPendingInput(); + } + } + } + else if( eNodeType == ATTRIBUTE_NODE) + { + if( !bLast) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->setStorageValue( + pDb, m_uiAttrNameId, pvValue, uiValueLen, uiEncDefId))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( bLast) + { + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + this, IX_ADD_NODE_VALUE, FALSE))) + { + goto Exit; + } + } + } + +Exit: + + if( RC_BAD( rc)) + { + if( bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + pDatabase->endPendingInput(); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::openPendingInput( + F_Db * pDb, + FLMUINT uiNewDataType) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->m_pDatabase; + eDomNodeType eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Set up the database to point to this node. Only one node + // is allowed to stream data into the B-Tree at a time. + + if( RC_BAD( rc = pDatabase->startPendingInput( uiNewDataType, this))) + { + goto Exit; + } + + // If the naming tag has already been set, make sure the + // new data type is compatible with the defined type for + // the tag. + + if( getNameId()) + { + switch( eNodeType) + { + case ELEMENT_NODE: + case DATA_NODE: + { + F_AttrElmInfo elmInfo; + + if( RC_BAD( rc = pDb->m_pDict->getElement( + pDb, m_nodeInfo.uiNameId, &elmInfo))) + { + goto Exit; + } + + if( elmInfo.getDataType() != uiNewDataType) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + break; + } + + case ANNOTATION_NODE: + { + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + } + + // Better be the latest version for update. + + flmAssert( m_ui64LowTransId == pDb->m_ui64CurrTransID); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + m_nodeInfo.uiDataLength = 0; + m_nodeInfo.uiDataType = uiNewDataType; + unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + setFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + flmAssert( !pDatabase->m_uiUpdByteCount); + flmAssert( !pDatabase->m_uiUpdCharCount); + +Exit: + + if( RC_BAD( rc)) + { + pDatabase->endPendingInput(); + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::flushPendingInput( + F_Db * pDb, + FLMBOOL bLast) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection = NULL; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE ucHeader[ MAX_DOM_HEADER_SIZE + 16]; + FLMUINT uiKeyLen; + FLMUINT uiHeaderStorageSize; + F_Database * pDatabase = pDb->m_pDatabase; + FLMUINT uiOutputLength; + FLMUINT uiLeftoverLength; + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( m_nodeInfo.ui64NodeId, + &uiKeyLen, ucKey, FALSE, TRUE))) + { + goto Exit; + } + + // Open the B-Tree + + if( !pDatabase->m_pPendingBTree) + { + if( !pDatabase->m_bUpdFirstBuf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( + &pDatabase->m_pPendingBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getCollection( + m_nodeInfo.uiCollection, &pCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btOpen( + pDb, &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + } + + // Better be the latest version for update. + + flmAssert( m_ui64LowTransId == pDb->m_ui64CurrTransID); + uiOutputLength = pDatabase->m_uiUpdByteCount; + uiLeftoverLength = 0; + + // Output the node header + + if( pDatabase->m_bUpdFirstBuf) + { + FLMUINT uiIVLen = 0; + + // This routine is designed to handle multi-block data streams. + // It shouldn't be called if everything fits within the a single + // update buffer. + + flmAssert( !bLast); + + // There shouldn't be any attributes at this point, because + // this shouldn't be an element node + + flmAssert( !hasAttributes()); + flmAssert( m_nodeInfo.eNodeType != ELEMENT_NODE); + + // Build the node header + + if( RC_BAD( rc = headerToBuf( TRUE, ucHeader, + &uiHeaderStorageSize, NULL, NULL))) + { + goto Exit; + } + + if( getEncDefId()) + { + F_ENCDEF * pEncDef; + + if( RC_BAD( rc = pDb->m_pDict->getEncDef( getEncDefId(), &pEncDef))) + { + goto Exit; + } + + uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( uiIVLen == 8 || uiIVLen == 16); + + if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, pDatabase->m_ucIV))) + { + goto Exit; + } + + f_memcpy( &ucHeader[ uiHeaderStorageSize], pDatabase->m_ucIV, uiIVLen); + } + + // Output the header + + if( nodeIsNew()) + { + // If this is a new entry, we need to insert it into the + // b-tree. + + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btInsertEntry( + ucKey, sizeof( ucKey), uiKeyLen, + ucHeader, uiHeaderStorageSize + uiIVLen, TRUE, FALSE, + &m_ui32BlkAddr, &m_uiOffsetIndex))) + { + if( rc == NE_XFLM_NOT_UNIQUE) + { + rc = RC_SET( NE_XFLM_EXISTS); + } + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( + ucKey, sizeof( ucKey), uiKeyLen, + ucHeader, uiHeaderStorageSize + uiIVLen, TRUE, FALSE, TRUE, + &m_ui32BlkAddr, &m_uiOffsetIndex))) + { + goto Exit; + } + } + + pDatabase->m_bUpdFirstBuf = FALSE; + } + + // Output the data buffer + + if( pDatabase->m_uiUpdByteCount || bLast) + { + // Encrypt the buffer if this node is encrypted. If encrypted, + // uiOutputLength will hold the length of the encrypted data. + // If not encrypted, it will hold the length of the non-encrypted data, + // i.e. pDatabase->m_uiUpdByteCount. the encrypted data will be returned + // in the input buffer. + + if( getEncDefId()) + { + // If this is not the last buffer, only encrypt to the nearest + // FLM_ENCRYPT_CHUNK_SIZE byte boundary. Move whatever we don't + // encrypt down to the beginning of the buffer after writing the + // encrypted part out to the B-tree (see below). This is necessary + // because the decryption algorithm assumes that all of the + // encrypted data is in FLM_ENRYPT_CHUNK_SIZE byte chunks - except + // for the last chunk, which may be encrypted to the nearest 16 byte + // boundary. + + if( !bLast && (uiOutputLength & (FLM_ENCRYPT_CHUNK_SIZE - 1))) + { + uiLeftoverLength = uiOutputLength & (FLM_ENCRYPT_CHUNK_SIZE - 1); + uiOutputLength -= uiLeftoverLength; + } + + if( RC_BAD( rc = pDb->encryptData( getEncDefId(), pDatabase->m_ucIV, + pDatabase->m_pucUpdBuffer, pDatabase->m_uiUpdBufferSize, + uiOutputLength, &uiOutputLength))) + { + goto Exit; + } + } + + if( nodeIsNew()) + { + // If this is a new entry, we need to continue calling the + // insert method to stream its data into the b-tree + + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btInsertEntry( + ucKey, sizeof( ucKey), uiKeyLen, + pDatabase->m_pucUpdBuffer, uiOutputLength, + FALSE, bLast, + &m_ui32BlkAddr, &m_uiOffsetIndex))) + { + if( rc == NE_XFLM_NOT_UNIQUE) + { + rc = RC_SET( NE_XFLM_EXISTS); + } + + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( + ucKey, sizeof( ucKey), uiKeyLen, + pDatabase->m_pucUpdBuffer, uiOutputLength, + FALSE, bLast, TRUE, + &m_ui32BlkAddr, &m_uiOffsetIndex))) + { + goto Exit; + } + } + } + + m_nodeInfo.uiDataLength += uiOutputLength; + if( (pDatabase->m_uiUpdByteCount = uiLeftoverLength) != 0) + { + f_memmove( pDatabase->m_pucUpdBuffer, + pDatabase->m_pucUpdBuffer + uiOutputLength, uiLeftoverLength); + } + + if( bLast) + { + // Clear the dirty flag and the new flag. + + unsetNodeDirtyAndNew( pDb); + } + +Exit: + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::setMetaValue( + IF_Db * ifpDb, + FLMUINT64 ui64Value) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db*)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartedTrans = FALSE; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // Only allow meta values on element nodes. + + if( eNodeType != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If the value isn't changing, don't do anything + + if( ui64Value == getMetaValue()) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure the node can be updated + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + // Set the value + + setMetaValue( ui64Value); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( + m_pCachedNode, FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetMetaValue( pDb, + getCollection(), getNodeId(), ui64Value))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getMetaValue( + IF_Db * ifpDb, + FLMUINT64 * pui64Value) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db*)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + *pui64Value = getMetaValue(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::isDataLocalToNode( + IF_Db * ifpDb, + FLMBOOL * pbDataIsLocal) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db*)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (getNodeType() == ATTRIBUTE_NODE) + { + *pbDataIsLocal = TRUE; + } + else + { + *pbDataIsLocal = getDataLength() ? TRUE : FALSE; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Read from a text stream, outputting ASCII. If we encounter a + character that cannot be output as ASCII, we will return an error. +******************************************************************************/ +RCODE F_AsciiIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUNICODE uzChar; + + *puiBytesRead = 0; + while( *puiBytesRead < uiBytesToRead) + { + if( m_eTextType == XFLM_UNICODE_TEXT) + { + if( (m_pucEnd && ((FLMUINT)(m_pucEnd - m_pucCurrPtr) < + sizeof( FLMUNICODE))) || + (uzChar = *((FLMUNICODE *)m_pucCurrPtr)) == 0) + { + break; + } + + if( uzChar > 127) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + *pucBuffer++ = (FLMBYTE)uzChar; + m_pucCurrPtr += sizeof( FLMUNICODE); + } + else // UTF8 + { + if( RC_BAD( rc = flmGetCharFromUTF8Buf( + &m_pucCurrPtr, m_pucEnd, &uzChar))) + { + goto Exit; + } + + if( !uzChar) + { + break; + } + + if( uzChar > 127) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + *pucBuffer++ = (FLMBYTE)uzChar; + } + + m_uiCurrChar++; + (*puiBytesRead)++; + } + + if( *puiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: This class converts a storage text stream to an ASCII + text stream. +******************************************************************************/ +class F_AsciiStorageStream : public IF_IStream, public XF_Base +{ +public: + + F_AsciiStorageStream() + { + m_pIStream = NULL; + } + + ~F_AsciiStorageStream() + { + close(); + } + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE void XFLMAPI close( void) + { + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + } + } + + RCODE open( + IF_IStream * pIStream); + +private: + + IF_IStream * m_pIStream; +}; + +/***************************************************************************** +Desc: Open an Ascii storage stream. +******************************************************************************/ +RCODE F_AsciiStorageStream::open( + IF_IStream * pIStream) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucSENBuf [16]; + FLMUINT uiLen; + FLMUINT uiSENLen; + + close(); + m_pIStream = pIStream; + m_pIStream->AddRef(); + + // Skip over the SEN + + uiLen = 1; + if( RC_BAD( rc = m_pIStream->read( (char *)&ucSENBuf [0], uiLen, &uiLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( (uiSENLen = flmGetSENLength( ucSENBuf[ 0])) > 1) + { + uiLen = uiSENLen - 1; + if( RC_BAD( rc = m_pIStream->read( + (char *)&ucSENBuf [1], uiLen, &uiLen))) + { + goto Exit; + } + } + + // We are now positioned to read UTF8 + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Read from a storage text stream, outputting ASCII. If we encounter a + character that cannot be output as ASCII, we will return an error. +******************************************************************************/ +RCODE F_AsciiStorageStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uzChar; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesRead = 0; + + // Better have a stream that has been positioned by a call to open(). + + flmAssert( m_pIStream); + + while( uiBytesRead < uiBytesToRead) + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( m_pIStream, &uzChar))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + + if( uzChar <= 127) + { + *pucBuffer++ = (FLMBYTE)uzChar; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + uiBytesRead++; + } + + if( uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/***************************************************************************** +Desc: This class converts a binary stream to an ASCII text storage + stream. +******************************************************************************/ +class F_BinaryToTextStream : public IF_IStream, public XF_Base +{ +public: + + F_BinaryToTextStream() + { + m_pEncoderStream = NULL; + } + + ~F_BinaryToTextStream() + { + close(); + } + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE void XFLMAPI close( void) + { + if( m_pEncoderStream) + { + m_pEncoderStream->Release(); + m_pEncoderStream = NULL; + } + } + + RCODE open( + IF_IStream * pIStream, + FLMUINT uiDataLen, + FLMUINT * puiTextLength); + +private: + + FLMBYTE m_ucSENBuf [16]; + FLMUINT m_uiSENLen; + FLMUINT m_uiCurrOffset; + F_Base64EncoderIStream * m_pEncoderStream; +}; + +/***************************************************************************** +Desc: Open a binary-to-text stream. +******************************************************************************/ +RCODE F_BinaryToTextStream::open( + IF_IStream * pIStream, + FLMUINT uiDataLen, + FLMUINT * puiTextLength) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucSen; + FLMUINT uiOutputLen; + + close(); + + // Set up the SEN buffer. Calculate length to be 4 bytes for every 3 + // binary bytes. + + uiOutputLen = (uiDataLen / 3) * 4; + + // If number of bytes is not an exact multiple of 3, we will need 4 + // more bytes. + + if( uiDataLen % 3) + { + uiOutputLen += 4; + } + + pucSen = &m_ucSENBuf [0]; + m_uiSENLen = flmEncodeSEN( (FLMUINT64)uiOutputLen, &pucSen, (FLMUINT)0); + m_uiCurrOffset = 0; + + // Need to include the data length, SEN length, and the terminating null + // character. + + *puiTextLength = uiOutputLen + m_uiSENLen + 1; + + // Set up the encoder stream + + if( (m_pEncoderStream = f_new F_Base64EncoderIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pEncoderStream->open( pIStream, FALSE))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Read from a binary stream, outputting base 64 encoded ASCII. +******************************************************************************/ +RCODE F_BinaryToTextStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesRead; + + // Better have a stream that has been positioned by a call to open(). + + flmAssert( m_pEncoderStream); + *puiBytesRead = 0; + + if( m_uiCurrOffset < m_uiSENLen) + { + if( uiBytesToRead >= m_uiSENLen - m_uiCurrOffset) + { + f_memcpy( pucBuffer, &m_ucSENBuf [m_uiCurrOffset], + m_uiSENLen - m_uiCurrOffset); + (*puiBytesRead) += (m_uiSENLen - m_uiCurrOffset); + pucBuffer += (m_uiSENLen - m_uiCurrOffset); + m_uiCurrOffset = m_uiSENLen; + } + else + { + f_memcpy( pucBuffer, &m_ucSENBuf [m_uiCurrOffset], + uiBytesToRead); + (*puiBytesRead) += uiBytesToRead; + m_uiCurrOffset += uiBytesToRead; + pucBuffer += uiBytesToRead; + } + } + + // If we didn't get everything from the SEN buffer, read from the + // decoding stream. + + if( *puiBytesRead < uiBytesToRead) + { + if( RC_BAD( rc = m_pEncoderStream->read( pucBuffer, + uiBytesToRead - *puiBytesRead, &uiBytesRead))) + { + if( rc == NE_XFLM_EOF_HIT) + { + (*puiBytesRead) += uiBytesRead; + } + goto Exit; + } + else + { + (*puiBytesRead) += uiBytesRead; + } + } + + if( *puiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Store text as a number. +******************************************************************************/ +RCODE F_DOMNode::storeTextAsNumber( + F_Db * pDb, + void * pvValue, + FLMUINT uiNumBytesInBuffer, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucChar; + FLMUINT uiIncrAmount = 0; + FLMBOOL bNeg = FALSE; + FLMBOOL bHex = FALSE; + FLMUINT64 ui64Num = 0; + FLMUINT uiBytesRead; + FLMBOOL bFirstChar = TRUE; + F_AsciiIStream asciiStream( (FLMBYTE *)pvValue, uiNumBytesInBuffer, XFLM_UTF8_TEXT); + + // Convert the text to a number. + + for (;;) + { + if( RC_BAD( rc = asciiStream.read( &ucChar, 1, &uiBytesRead))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + // See if we can convert to a number. + + if( ucChar >= ASCII_ZERO && ucChar <= ASCII_NINE) + { + uiIncrAmount = (FLMUINT)(ucChar - '0'); + } + else if( ucChar >= ASCII_UPPER_A && ucChar <= ASCII_UPPER_F) + { + if( bHex) + { + uiIncrAmount = (FLMUINT)(ucChar - ASCII_UPPER_A + 10); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( ucChar >= ASCII_LOWER_A && ucChar <= ASCII_LOWER_F) + { + if( bHex) + { + uiIncrAmount = (FLMUINT)(ucChar - ASCII_LOWER_A + 10); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( ucChar == ASCII_LOWER_X || ucChar == ASCII_UPPER_X) + { + if( !ui64Num && !bHex) + { + bHex = TRUE; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( ucChar == ASCII_DASH && bFirstChar) + { + bNeg = TRUE; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + + if( !bHex) + { + if( ui64Num > (~(FLMUINT64)0) / 10) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + ui64Num *= (FLMUINT64)10; + } + else + { + if( ui64Num > (~(FLMUINT64)0) / 16) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + ui64Num *= (FLMUINT64)16; + } + + if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)uiIncrAmount) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num += (FLMUINT64)uiIncrAmount; + bFirstChar = FALSE; + } + + // If the number is negative, make sure it doesn't + // overflow the maximum negative number. + + if( bNeg) + { + if( ui64Num > gv_ui64MaxSignedIntVal + 1) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + + if( RC_BAD( rc = setINT64( (IF_Db *)pDb, + -((FLMINT64)ui64Num), uiEncDefId))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = setUINT64( (IF_Db *)pDb, ui64Num, uiEncDefId))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Store text as a binary value. +******************************************************************************/ +RCODE F_DOMNode::storeTextAsBinary( + F_Db * pDb, + const void * pvValue, + FLMUINT uiNumBytesInBuffer, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucBuf[ 64]; + F_AsciiIStream asciiStream( (FLMBYTE *)pvValue, + uiNumBytesInBuffer, XFLM_UTF8_TEXT); + F_Base64DecoderIStream decoderStream; + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); + FLMUINT uiBytesRead; + + if( RC_BAD( rc = decoderStream.open( &asciiStream))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = decoderStream.read( ucBuf, sizeof( ucBuf), &uiBytesRead))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if( RC_BAD( rc = dynaBuf.appendData( ucBuf, uiBytesRead))) + { + goto Exit; + } + } + + if( RC_BAD( rc = setBinary( + (IF_Db *)pDb, dynaBuf.getBufferPtr(), + dynaBuf.getDataLength(), uiEncDefId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Store binary as a text value (base 64 encoded) +******************************************************************************/ +RCODE F_DOMNode::storeBinaryAsText( + F_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucBuf[ 64]; + F_BufferIStream bufferStream; + F_Base64EncoderIStream encoderStream; + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); + FLMUINT uiBytesRead; + + if( RC_BAD( rc = bufferStream.open( (FLMBYTE *)pvValue, uiLength))) + { + goto Exit; + } + + if( RC_BAD( rc = encoderStream.open( &bufferStream, FALSE))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = encoderStream.read( + ucBuf, sizeof( ucBuf), &uiBytesRead))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if( RC_BAD( rc = dynaBuf.appendData( ucBuf, uiBytesRead))) + { + goto Exit; + } + } + + if( RC_BAD( rc = setTextFastPath( pDb, dynaBuf.getBufferPtr(), + dynaBuf.getDataLength(), XFLM_UTF8_TEXT, uiEncDefId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setTextStreaming( + F_Db * pDb, + const void * pvValue, + FLMUINT uiNumBytesInBuffer, + eXFlmTextType eTextType, + FLMBOOL bLast, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + FLMBYTE * pucUpdBuffer; + FLMUINT uiUpdBufferSize; + FLMBYTE * pucTmp; + FLMUINT uiSenLen; + FLMBYTE ucTmpSen[ FLM_MAX_NUM_BUF_SIZE]; + F_Database * pDatabase = pDb->m_pDatabase; + F_Rfl * pRfl = pDatabase->m_pRfl; + F_DOMNode * pNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bFirst = pDatabase->m_pPendingInput ? FALSE : TRUE; + FLMUINT uiRflToken = 0; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // Not supported on attributes + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If this is an element node, we need to find or create + // the child data node + + if( eNodeType == ELEMENT_NODE) + { + // The streaming interface is not directly supported on element nodes. + // If the node already has a value and does not already have a child data + // node, we can clear the value on the element, create a data node, and + // allow the operation to continue. + + if( getDataLength()) + { + if( getDataChildCount()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = clearNodeValue( pDb))) + { + goto Exit; + } + } + else if( getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + } + + if( !pNode) + { + if( RC_BAD( rc = createNode( (IF_Db *)pDb, DATA_NODE, + getNameId(), XFLM_LAST_CHILD, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + } + + if( RC_BAD( rc = pNode->setTextStreaming( pDb, + pvValue, uiNumBytesInBuffer, eTextType, bLast, uiEncDefId))) + { + goto Exit; + } + + goto Exit; + } + + // Make sure the state of the node and database + // allow a value to be set. + + if( RC_BAD( rc = canSetValue( pDb, XFLM_TEXT_TYPE))) + { + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + bMustAbortOnError = TRUE; + + // If we are in the middle of streaming data into a node, + // any error will probably leave the database in a bad + // state. Because of this, we want to force the + // transaction to abort. + + if( pDatabase->m_pPendingInput) + { + if( pDatabase->m_pPendingInput != m_pCachedNode) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + + pucUpdBuffer = pDatabase->m_pucUpdBuffer; + + // NOTE: So that flushNode would not have to allocate a buffer for + // writing out data with a non-padded header, we reserve enough + // room in pDatabase->m_pucUpdBuffer so that it could hold a maximum + // header plus all of the data if it needed to. + // Also, make sure there is room to encrypt the data. That is why + // we subtract another 16 bytes. + + uiUpdBufferSize = pDatabase->m_uiUpdBufferSize - + MAX_DOM_HEADER_SIZE - ENCRYPT_MIN_CHUNK_SIZE; + + // Update the index keys + + if( bFirst) + { + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_DEL_NODE_VALUE, TRUE))) + { + goto Exit; + } + } + } + + // Open the pending input stream + + if( bFirst) + { + if( RC_BAD( rc = openPendingInput( pDb, XFLM_TEXT_TYPE))) + { + goto Exit; + } + + // Reserve 5 bytes for a SEN indicating the number of characters + // in the string. The SEN (representing a 32-bit number) will + // never require more than 5 bytes and will typically only + // require 1 or 2 bytes. + + pDatabase->m_uiUpdByteCount += SEN_RESERVE_BYTES; + *pucUpdBuffer = 0; + + // Save the encryption scheme + + setEncDefId( uiEncDefId); + } + + // Set the value + + if( pvValue) + { + // If a zero was passed for the character count, change it to + // the UINT high value. We will output characters until a terminating + // null is found. + + if( !uiNumBytesInBuffer) + { + uiNumBytesInBuffer = FLM_MAX_UINT; + } + + switch( eTextType) + { + case XFLM_UNICODE_TEXT: + { + FLMUNICODE * puCurChar = (FLMUNICODE *)pvValue; + + while( *puCurChar && uiNumBytesInBuffer >= sizeof( FLMUNICODE)) + { + uiLen = uiUpdBufferSize - pDatabase->m_uiUpdByteCount; + if( RC_BAD( rc = flmUni2UTF8( *puCurChar, + &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], &uiLen))) + { + if( rc == NE_XFLM_CONV_DEST_OVERFLOW) + { + if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + continue; + } + + goto Exit; + } + + pDatabase->m_uiUpdByteCount += uiLen; + pDatabase->m_uiUpdCharCount++; + uiNumBytesInBuffer -= sizeof( FLMUNICODE); + puCurChar++; + } + + break; + } + + case XFLM_UTF8_TEXT: + { + FLMBYTE * pucCurByte = (FLMBYTE *)pvValue; + FLMBYTE * pucEnd = (FLMBYTE *)pvValue + uiNumBytesInBuffer; + + for( ;;) + { + if( (uiUpdBufferSize - pDatabase->m_uiUpdByteCount) < 3) + { + if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = flmGetUTF8CharFromUTF8Buf( &pucCurByte, + pucEnd, &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], &uiLen))) + { + goto Exit; + } + + if( !uiLen) + { + break; + } + + pDatabase->m_uiUpdByteCount += uiLen; + pDatabase->m_uiUpdCharCount++; + uiNumBytesInBuffer -= uiLen; + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + } + + if( bLast) + { + // Terminate the buffer with a null byte + + if( pDatabase->m_uiUpdCharCount) + { + // See if there is room for a single zero byte + + if( pDatabase->m_uiUpdByteCount == uiUpdBufferSize) + { + if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + } + + pucUpdBuffer[ pDatabase->m_uiUpdByteCount++] = 0; + } + + if( pDatabase->m_bUpdFirstBuf) + { + if( pDatabase->m_uiUpdCharCount) + { + FLMUINT uiValLen; + + // Five bytes were reserved to encode the number of characters + // in the string. Since we didn't have to use multiple buffers + // to write the string to the database, we won't have to + // output a padded SEN. + + pucTmp = ucTmpSen; + flmEncodeSEN( pDatabase->m_uiUpdCharCount, &pucTmp); + uiSenLen = (FLMUINT)(pucTmp - ucTmpSen); + + // Copy the value into the node + + uiValLen = (pDatabase->m_uiUpdByteCount - + SEN_RESERVE_BYTES) + uiSenLen; + + if( calcDataBufSize( uiValLen) > getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) + { + goto Exit; + } + } + + setDataLength( uiValLen); + f_memcpy( getDataPtr(), ucTmpSen, uiSenLen); + f_memcpy( getDataPtr() + uiSenLen, + &pucUpdBuffer[ SEN_RESERVE_BYTES], + pDatabase->m_uiUpdByteCount - SEN_RESERVE_BYTES); + } + else + { + setDataLength( 0); + } + + // Clear flags + + unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + setDataLength( 0); + goto Exit; + } + } + else + { + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT uiHeaderStorageSize; + FLMUINT32 ui32BlkAddr; + FLMUINT uiOffsetIndex; + + // Flush anything that is in the current buffer and end the + // pending input stream. + + if( RC_BAD( rc = flushPendingInput( pDb, TRUE))) + { + goto Exit; + } + + pucTmp = ucTmpSen; + flmEncodeSEN( pDatabase->m_uiUpdCharCount, + &pucTmp, SEN_RESERVE_BYTES); + flmAssert( (FLMUINT)(pucTmp - ucTmpSen) == SEN_RESERVE_BYTES); + + // Output the header + + if( RC_BAD( rc = headerToBuf( TRUE, pDatabase->m_pucUpdBuffer, + &uiHeaderStorageSize, NULL, NULL))) + { + goto Exit; + } + + // Copy the SEN into the update buffer + + f_memcpy( &pDatabase->m_pucUpdBuffer[ uiHeaderStorageSize], + ucTmpSen, SEN_RESERVE_BYTES); + + // Replace the header + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( getNodeId(), &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + ui32BlkAddr = getBlkAddr(); + uiOffsetIndex = getOffsetIndex(); + + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( + ucKey, sizeof( ucKey), uiKeyLen, + pDatabase->m_pucUpdBuffer, uiHeaderStorageSize + SEN_RESERVE_BYTES, + TRUE, TRUE, FALSE, &ui32BlkAddr, &uiOffsetIndex))) + { + goto Exit; + } + + setBlkAddr( ui32BlkAddr); + setOffsetIndex( uiOffsetIndex); + + // Clear the dirty flag and the new flag. + + unsetNodeDirtyAndNew( pDb); + } + + pDatabase->endPendingInput(); + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_ADD_NODE_VALUE, FALSE))) + { + goto Exit; + } + } + + // Log the node to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, + RFL_NODE_SET_TEXT_VALUE_PACKET, m_pCachedNode))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( RC_BAD( rc)) + { + pDatabase->endPendingInput(); + + if( bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setTextFastPath( + F_Db * pDb, + const void * pvValue, + FLMUINT uiNumBytesInBuffer, + eXFlmTextType eTextType, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumCharsInBuffer = 0; + FLMBYTE ucUTFCharBuf[ 3]; + F_Database * pDatabase = pDb->m_pDatabase; + F_Rfl * pRfl = pDatabase->m_pRfl; + F_DOMNode * pNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + FLMUINT uiValLen; + FLMUINT uiReadLen; + FLMBOOL bIsIndexed = TRUE; + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); + FLMBOOL bStartOfUpdate = TRUE; + eDomNodeType eNodeType; + + // Make sure a transaction is active + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Cannot set a value if input is still pending + + if( pDatabase->m_pPendingInput) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // Convert the text to UTF-8 if needed + + if( eTextType == XFLM_UNICODE_TEXT) + { + FLMUNICODE * puCurChar = (FLMUNICODE *)pvValue; + + if( puCurChar) + { + if( !uiNumBytesInBuffer) + { + uiNumBytesInBuffer = FLM_MAX_UINT; + } + + while( *puCurChar && uiNumBytesInBuffer >= sizeof( FLMUNICODE)) + { + if( *puCurChar <= 0x007F) + { + if( RC_BAD( rc = dynaBuf.appendByte( (FLMBYTE)*puCurChar))) + { + goto Exit; + } + } + else + { + uiReadLen = sizeof( ucUTFCharBuf); + if( RC_BAD( rc = flmUni2UTF8( *puCurChar, + ucUTFCharBuf, &uiReadLen))) + { + goto Exit; + } + + if( RC_BAD( rc = dynaBuf.appendData( ucUTFCharBuf, uiReadLen))) + { + goto Exit; + } + } + + puCurChar++; + uiNumBytesInBuffer -= sizeof( FLMUNICODE); + uiNumCharsInBuffer++; + } + } + + if( RC_BAD( rc = dynaBuf.appendByte( 0))) + { + goto Exit; + } + + eTextType = XFLM_UTF8_TEXT; + pvValue = dynaBuf.getBufferPtr(); + uiNumBytesInBuffer = dynaBuf.getDataLength(); + } + else if( eTextType == XFLM_UTF8_TEXT) + { + if( RC_BAD( rc = flmGetUTF8Length( (FLMBYTE *)pvValue, uiNumBytesInBuffer, + &uiNumBytesInBuffer, &uiNumCharsInBuffer))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If this is an element node, we need to see if there is a + // child data node + + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( getDataChildCount() == 1) + { + // If this node only has one data child, delete the child and + // store the value directly on the element + + if( RC_BAD( rc = pNode->deleteNode( pDb))) + { + goto Exit; + } + + pNode->Release(); + pNode = NULL; + } + else + { + rc = pNode->setTextFastPath( pDb, pvValue, uiNumBytesInBuffer, + eTextType, uiEncDefId); + goto Exit; + } + } + + // Make sure the states of the node and database + // allow a value to be set. + + if( RC_BAD( rc = canSetValue( pDb, XFLM_TEXT_TYPE))) + { + goto Exit; + } + + // If this is an attribute node, need to use the + // attribute list object to set the value + + if( eNodeType == ATTRIBUTE_NODE) + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, + &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + + if( RC_BAD( rc = m_pCachedNode->setUTF8( pDb, + m_uiAttrNameId, pvValue, uiNumBytesInBuffer, + uiNumCharsInBuffer, uiEncDefId))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + } + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + setDataLength( 0); + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, m_uiAttrNameId))) + { + goto Exit; + } + + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + bMustAbortOnError = TRUE; + + // Update the index keys + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + else + { + bIsIndexed = FALSE; + } + + // Verify and save the encryption scheme + + if( uiEncDefId) + { + if( RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, NULL))) + { + flmAssert( 0); + goto Exit; + } + } + + setEncDefId( uiEncDefId); + + // Set the value + + uiValLen = 0; + + if( pvValue && uiNumBytesInBuffer) + { + FLMUINT uiSenLen; + FLMBYTE * pucTmp; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + FLMBOOL bNullTerminate = FALSE; + + if( pucValue[ uiNumBytesInBuffer - 1] != 0) + { + bNullTerminate = TRUE; + } + + uiSenLen = flmGetSENByteCount( uiNumCharsInBuffer); + uiValLen = uiNumBytesInBuffer + uiSenLen + (bNullTerminate ? 1 : 0); + + if( calcDataBufSize( uiValLen) > getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( uiValLen, FALSE))) + { + goto Exit; + } + } + + pucTmp = getDataPtr(); + flmEncodeSENKnownLength( uiNumCharsInBuffer, uiSenLen, &pucTmp); + f_memcpy( pucTmp, pucValue, uiNumBytesInBuffer); + + if( bNullTerminate) + { + pucTmp[ uiNumBytesInBuffer] = 0; + } + } + + setDataLength( uiValLen); + + // Clear flags + + unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + setDataLength( 0); + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + // Log the node to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, + RFL_NODE_SET_TEXT_VALUE_PACKET, m_pCachedNode))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( RC_BAD( rc)) + { + if( bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setBinaryStreaming( + IF_Db * ifpDb, + const void * pvValue, + FLMUINT uiLength, + FLMBOOL bLast, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_Database * pDatabase = pDb->m_pDatabase; + F_Rfl * pRfl = pDatabase->m_pRfl; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + FLMBYTE * pucUpdBuffer; + FLMUINT uiUpdBufferSize; + FLMUINT uiTmp; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pNode = NULL; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // Not supported on attribute nodes + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If this is an element node, we need to find or create + // the child data node + + if( eNodeType == ELEMENT_NODE) + { + // The streaming interface is not directly supported on element nodes. + // If the node already has a value and does not already have a child data + // node, we can clear the value on the element, create a data node, and + // allow the operation to continue. + + if( getDataLength()) + { + if( getDataChildCount()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = clearNodeValue( pDb))) + { + goto Exit; + } + } + else if( getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + } + + if( !pNode) + { + if( RC_BAD( rc = createNode( (IF_Db *)pDb, DATA_NODE, + getNameId(), XFLM_LAST_CHILD, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + } + + if( RC_BAD( rc = pNode->setBinary( ifpDb, pucValue, uiLength, bLast, + uiEncDefId))) + { + goto Exit; + } + + goto Exit; + } + + // Make sure the state of the node and database + // allow a value to be set. + + if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + bMustAbortOnError = TRUE; + + // If we are in the middle of streaming data into a node, + // any error will probably leave the database in a bad + // state. Because of this, we want to force the + // transaction to abort. + + if( pDatabase->m_pPendingInput) + { + if( pDatabase->m_pPendingInput != m_pCachedNode) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + + pucUpdBuffer = pDatabase->m_pucUpdBuffer; + + // NOTE: So that flushNode would not have to allocate a buffer for + // writing out data with a non-padded header, we reserve enough + // room in pDatabase->m_pucUpdBuffer so that it could hold a maximum + // header plus all of the data if it needed to. + // + // Also, make sure there is room to encrypt the data. That is why + // we subtract another 16 bytes. + + uiUpdBufferSize = pDatabase->m_uiUpdBufferSize - + MAX_DOM_HEADER_SIZE - ENCRYPT_MIN_CHUNK_SIZE; + + // Open the pending input stream + + if( !pDatabase->m_pPendingInput) + { + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + this, IX_DEL_NODE_VALUE, TRUE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = openPendingInput( pDb, XFLM_BINARY_TYPE))) + { + goto Exit; + } + } + + // Save the encryption scheme + + setEncDefId( uiEncDefId); + + while( uiLength) + { + if( (uiTmp = f_min( uiLength, + uiUpdBufferSize - pDatabase->m_uiUpdByteCount)) == 0) + { + if( RC_BAD( rc = flushPendingInput( pDb, FALSE))) + { + goto Exit; + } + continue; + } + + f_memcpy( &pucUpdBuffer[ pDatabase->m_uiUpdByteCount], pucValue, uiTmp); + pDatabase->m_uiUpdByteCount += uiTmp; + pucValue += uiTmp; + uiLength -= uiTmp; + } + + if( bLast) + { + if( pDatabase->m_bUpdFirstBuf) + { + if( pDatabase->m_uiUpdByteCount) + { + if( calcDataBufSize( pDatabase->m_uiUpdByteCount) > getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( + pDatabase->m_uiUpdByteCount, FALSE))) + { + goto Exit; + } + } + } + + setDataLength( pDatabase->m_uiUpdByteCount); + + if( pDatabase->m_uiUpdByteCount) + { + f_memcpy( getDataPtr(), pucUpdBuffer, pDatabase->m_uiUpdByteCount); + } + + // Clear unwanted flags + + unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + } + else + { + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT uiHeaderStorageSize; + FLMUINT32 ui32BlkAddr; + FLMUINT uiOffsetIndex; + + // Flush anything that is in the current buffer + + if( RC_BAD( rc = flushPendingInput( pDb, TRUE))) + { + goto Exit; + } + + // Output the header + + if( RC_BAD( rc = headerToBuf( TRUE, pDatabase->m_pucUpdBuffer, + &uiHeaderStorageSize, NULL, NULL))) + { + goto Exit; + } + + // Replace the header + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( getNodeId(), &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + ui32BlkAddr = getBlkAddr(); + uiOffsetIndex = getOffsetIndex(); + + if( RC_BAD( rc = pDatabase->m_pPendingBTree->btReplaceEntry( + ucKey, sizeof( ucKey), uiKeyLen, + pDatabase->m_pucUpdBuffer, uiHeaderStorageSize, + TRUE, TRUE, FALSE, &ui32BlkAddr, &uiOffsetIndex))) + { + goto Exit; + } + + setBlkAddr( ui32BlkAddr); + setOffsetIndex( uiOffsetIndex); + + // Clear the dirty flag and the new flag. + + unsetNodeDirtyAndNew( pDb); + } + + pDatabase->endPendingInput(); + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + this, IX_ADD_NODE_VALUE, FALSE))) + { + goto Exit; + } + } + + // Log the node to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, + RFL_NODE_SET_BINARY_VALUE_PACKET, m_pCachedNode))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDatabase->endPendingInput(); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setBinaryFastPath( + IF_Db * ifpDb, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_Database * pDatabase = pDb->m_pDatabase; + F_Rfl * pRfl = pDatabase->m_pRfl; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pNode = NULL; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + eDomNodeType eNodeType; + FLMBOOL bIsIndexed = TRUE; + FLMBOOL bStartOfUpdate = TRUE; + + // Make sure a transaction is active + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Cannot set a value if input is still pending + + if( pDatabase->m_pPendingInput) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // If this is an element node, we need to find or create + // the child data node + + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( getDataChildCount() == 1) + { + // If this node only has one data child, delete the child and + // store the value directly on the element + + if( RC_BAD( rc = pNode->deleteNode( ifpDb))) + { + goto Exit; + } + + pNode->Release(); + pNode = NULL; + } + else + { + rc = pNode->setBinaryFastPath( ifpDb, pucValue, uiLength, uiEncDefId); + goto Exit; + } + } + else if( eNodeType == ATTRIBUTE_NODE) + { + // Make sure the state of the node and database + // allow a value to be set. + + if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) + { + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( (F_Db *)ifpDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + + if( RC_BAD( rc = m_pCachedNode->setBinary( + pDb, m_uiAttrNameId, pvValue, uiLength, uiEncDefId))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + getCollection(), this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, m_uiAttrNameId))) + { + goto Exit; + } + + goto Exit; + } + + // If node is a text data type, convert the binary to base 64 encoding + // and store as text. + + if( getDataType() == XFLM_TEXT_TYPE) + { + // Convert to base64 text and call the routine to set as native. + + rc = storeBinaryAsText( (F_Db *)ifpDb, pvValue, uiLength, uiEncDefId); + goto Exit; + } + + // Make sure the state of the node and database + // allow a value to be set. + + if( RC_BAD( rc = canSetValue( pDb, XFLM_BINARY_TYPE))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + unsetFlags( FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + + if( getNameId()) + { + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + this, IX_DEL_NODE_VALUE, bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + else + { + bIsIndexed = FALSE; + } + + setEncDefId( uiEncDefId); + + if( calcDataBufSize( uiLength) > getDataBufSize()) + { + if( RC_BAD( rc = resizeDataBuffer( uiLength, FALSE))) + { + goto Exit; + } + } + + setDataLength( uiLength); + + if( uiLength) + { + f_memcpy( getDataPtr(), pvValue, uiLength); + } + + // Clear unwanted flags + + unsetFlags( FDOM_VALUE_ON_DISK | FDOM_FIXED_SIZE_HEADER); + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + this, IX_ADD_NODE_VALUE, bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + // Log the node to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetValue( pDb, + RFL_NODE_SET_BINARY_VALUE_PACKET, m_pCachedNode))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::clearNodeValue( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->m_pDatabase; + F_Rfl * pRfl = pDatabase->m_pRfl; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + + // Make sure a transaction is active + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Cannot clear a value if input is still pending + + if( pDatabase->m_pPendingInput) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = setStorageValue( pDb, NULL, 0, 0, TRUE))) + { + goto Exit; + } + + // Log the update to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeClearValue( pDb, getCollection(), + getNodeId(), m_uiAttrNameId))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType, + FLMUINT * puiDataLength) +{ + RCODE rc = NE_XFLM_OK; + F_PosIStream * pIStream = NULL; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode = this; + eDomNodeType eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + if( m_uiFlags & FDOM_VALUE_ON_DISK) + { + F_BTreeIStream * pBTreeIStream; + F_ENCDEF * pEncDef; + FLMUINT uiIVLen; + FLMUINT uiLen; + F_NODE_INFO nodeInfo; + FLMUINT uiTmpFlags; + + if( eNodeType == ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = pDb->flushDirtyNode( this))) + { + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pNodePool->allocBTreeIStream( + &pBTreeIStream))) + { + goto Exit; + } + pIStream = pBTreeIStream; + + if( RC_BAD( rc = pBTreeIStream->open( pDb, getCollection(), + getNodeId(), getBlkAddr(), getOffsetIndex()))) + { + goto Exit; + } + + // Skip the node header + + if( RC_BAD( rc = flmReadNodeInfo( getCollection(), getNodeId(), + pBTreeIStream, (FLMUINT)pBTreeIStream->remainingSize(), TRUE, + &nodeInfo, &uiTmpFlags))) + { + goto Exit; + } + + // Read the IV if data is encrypted + + if( getEncDefId()) + { + if( RC_BAD( rc = pDb->m_pDict->getEncDef( getEncDefId(), &pEncDef))) + { + goto Exit; + } + + uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( uiIVLen == 8 || uiIVLen == 16); + + if( RC_BAD( rc = pBTreeIStream->read( (char *)pBTreeIStream->m_ucIV, + uiIVLen, &uiLen))) + { + goto Exit; + } + + flmAssert( uiLen == uiIVLen); + pBTreeIStream->m_bDataEncrypted = TRUE; + } + + pBTreeIStream->m_uiEncDefId = getEncDefId(); + pBTreeIStream->m_uiDataLength = getDataLength(); + } + else + { + F_NodeBufferIStream * pNodeBufferIStream; + FLMUINT64 ui64TmpNodeId; + + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + ui64TmpNodeId = getFirstChildId(); + + for( ;;) + { + if( !ui64TmpNodeId) + { + break; + } + + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( + getCollection(), ui64TmpNodeId, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + + if( pNode->getNodeType() == DATA_NODE) + { + pCachedNode = pNode->m_pCachedNode; + break; + } + + ui64TmpNodeId = pNode->getNextSibId(); + } + } + + if( pStackStream) + { + pNodeBufferIStream = pStackStream; + pStackStream->AddRef(); + flmAssert( !pStackStream->m_pCachedNode); + } + else + { + if( (pNodeBufferIStream = f_new F_NodeBufferIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + pIStream = pNodeBufferIStream; + + if( RC_BAD( rc = pNodeBufferIStream->open( pCachedNode->getDataPtr(), + pCachedNode->getDataLength()))) + { + goto Exit; + } + + if( !pStackStream) + { + pNodeBufferIStream->m_pCachedNode = pCachedNode; + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pCachedNode->incrNodeUseCount(); + pCachedNode->incrStreamUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + } + + if( puiDataType) + { + *puiDataType = pCachedNode->getDataType(); + } + + if( puiDataLength) + { + *puiDataLength = pCachedNode->getDataLength(); + } + + *ppIStream = pIStream; + pIStream = NULL; + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getRawIStream( + F_Db * pDb, + IF_PosIStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_PosIStream * pIStream = NULL; + F_BTreeIStream * pBTreeIStream; + eDomNodeType eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Flush all dirty nodes out to the B-Tree + + if( RC_BAD( rc = pDb->flushDirtyNode( this))) + { + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pNodePool->allocBTreeIStream( + &pBTreeIStream))) + { + goto Exit; + } + pIStream = pBTreeIStream; + + if( RC_BAD( rc = pBTreeIStream->open( pDb, getCollection(), + getNodeId(), getBlkAddr(), getOffsetIndex()))) + { + goto Exit; + } + + *ppIStream = pIStream; + pIStream = NULL; + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType, + FLMUINT * puiDataLength) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, NULL))) + { + goto Exit; + } + + // Sync the node to make sure it is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + switch( getNodeType()) + { + case DATA_NODE: + case COMMENT_NODE: + case ANNOTATION_NODE: + case CDATA_SECTION_NODE: + { + if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, pStackStream, + ppIStream, puiDataType, puiDataLength))) + { + goto Exit; + } + + break; + } + + case ELEMENT_NODE: + { + if( getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( RC_BAD( rc = pNode->m_pCachedNode->getIStream( pDb, + pStackStream, ppIStream, puiDataType, puiDataLength))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, + pStackStream, ppIStream, puiDataType, puiDataLength))) + { + goto Exit; + } + } + + break; + } + + case ATTRIBUTE_NODE: + { + if( RC_BAD( rc = m_pCachedNode->getIStream( + pDb, m_uiAttrNameId, pStackStream, ppIStream, + puiDataType, puiDataLength))) + { + goto Exit; + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getTextIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiNumChars) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + + *ppIStream = NULL; + *puiNumChars = 0; + + if( RC_BAD( rc = getIStream( pDb, pStackStream, ppIStream, &uiDataType))) + { + goto Exit; + } + + if( uiDataType != XFLM_TEXT_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + + // Skip the leading SEN so that the stream is positioned to + // read raw utf8. + + if( (*ppIStream)->remainingSize()) + { + if( RC_BAD( rc = flmReadSEN( *ppIStream, puiNumChars))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc) && *ppIStream) + { + (*ppIStream)->Release(); + *ppIStream = NULL; + *puiNumChars = 0; + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getNumber64( + F_Db * pDb, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + F_DOMNode * pNode = NULL; + eDomNodeType eNodeType; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getNumber64( + pDb, m_uiAttrNameId, &ui64Num, &bNeg))) + { + goto Exit; + } + } + else if ( !getQuickNumber64( &ui64Num, &bNeg)) + { + if( eNodeType == ELEMENT_NODE && getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + rc = pNode->getNumber64( pDb, pui64Num, pbNeg); + goto Exit; + } + else + { + if( RC_BAD( rc = getIStream( pDb, &bufferIStream, + &pIStream, &uiDataType))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsNumber( pIStream, uiDataType, + &ui64Num, &bNeg))) + { + goto Exit; + } + } + } + + if( pui64Num) + { + *pui64Num = ui64Num; + } + + if( pbNeg) + { + *pbNeg = bNeg; + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( pNode) + { + pNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Allocate data for a unicode element and retrieve it. +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUnicode( + IF_Db * ifpDb, + FLMUNICODE ** ppuzUnicode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Get the unicode length (does not include NULL terminator) + + if( RC_BAD( rc = getUnicodeChars( pDb, &uiLen))) + { + goto Exit; + } + + if( uiLen) + { + FLMUINT uiBufSize = (uiLen + 1) * sizeof( FLMUNICODE); + + if( RC_BAD( rc = f_alloc( uiBufSize, ppuzUnicode))) + { + goto Exit; + } + + if( RC_BAD( rc = getUnicode( pDb, + *ppuzUnicode, uiBufSize, 0, uiLen, &uiLen))) + { + goto Exit; + } + } + else + { + *ppuzUnicode = NULL; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUnicode( + IF_Db * ifpDb, + FLMUNICODE * puzBuffer, + FLMUINT uiBufSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned, + FLMUINT * puiBufferBytesUsed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + FLMUINT uiDataLength; + F_NodeBufferIStream bufferStream; + IF_PosIStream * pIStream = NULL; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = getIStream( pDb, &bufferStream, + &pIStream, &uiDataType, &uiDataLength))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsText( + pIStream, NULL, uiDataLength, uiDataType, puzBuffer, + uiBufSize, XFLM_UNICODE_TEXT, + uiMaxCharsRequested, uiCharOffset, puiCharsReturned, + puiBufferBytesUsed))) + { + goto Exit; + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( bStartedTrans) + { + pDb->abortTrans(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUnicode( + IF_Db * ifpDb, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiBufSize; + FLMUINT uiChars; + void * pvBuffer; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getUnicode( ifpDb, NULL, 0, 0, ~((FLMUINT)0), &uiChars))) + { + goto Exit; + } + + uiBufSize = (uiChars + 1) * sizeof( FLMUNICODE); + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getUnicode( ifpDb, (FLMUNICODE *)pvBuffer, uiBufSize, 0, + ~((FLMUINT)0), NULL))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUTF8( + IF_Db * ifpDb, + FLMBYTE * pszValue, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned, + FLMUINT * puiBufferBytesUsed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + FLMUINT uiDataLength; + F_DOMNode * pNode = NULL; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + switch( getNodeType()) + { + case DATA_NODE: + case COMMENT_NODE: + case ANNOTATION_NODE: + case CDATA_SECTION_NODE: + { + pNode = this; + pNode->AddRef(); + break; + } + + case ATTRIBUTE_NODE: + { + pNode = this; + pNode->AddRef(); + goto SlowDecode; + } + + case ELEMENT_NODE: + { + if( getDataChildCount()) + { + if( RC_BAD( rc = getChild( pDb, DATA_NODE, (IF_DOMNode **)&pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + } + else + { + pNode = this; + pNode->AddRef(); + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( (pNode->getModeFlags() & FDOM_VALUE_ON_DISK) || + pNode->getDataType() != XFLM_TEXT_TYPE || uiCharOffset) + { +SlowDecode: + + if( RC_BAD( rc = pNode->getIStream( pDb, &bufferIStream, &pIStream, + &uiDataType, &uiDataLength))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsText( + pIStream, NULL, uiDataLength, + uiDataType, pszValue, uiBufferSize, XFLM_UTF8_TEXT, + uiMaxCharsRequested, uiCharOffset, puiCharsReturned, + puiBufferBytesUsed))) + { + goto Exit; + } + } + else + { + const FLMBYTE * pucBuffer = pNode->getDataPtr(); + const FLMBYTE * pucEnd = pucBuffer + pNode->getDataLength(); + FLMUINT uiCharCount = 0; + FLMUINT uiStrByteLen = 0; + + if( pucBuffer) + { + if( RC_BAD( rc = flmDecodeSEN( &pucBuffer, pucEnd, &uiCharCount))) + { + goto Exit; + } + + uiStrByteLen = (FLMUINT)(pucEnd - pucBuffer); + } + + if( uiCharCount > uiMaxCharsRequested || + (pszValue && uiBufferSize < uiStrByteLen)) + { + goto SlowDecode; + } + + if( pszValue) + { + if( uiStrByteLen) + { + f_memcpy( pszValue, pucBuffer, uiStrByteLen); + } + else if( uiBufferSize > 0) + { + *pszValue = 0; + } + } + + if( puiCharsReturned) + { + *puiCharsReturned = uiCharCount; + } + + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = uiStrByteLen; + } + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( pNode) + { + pNode->Release(); + } + + if( bStartedTrans) + { + pDb->abortTrans(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUTF8( + IF_Db * ifpDb, + FLMBYTE ** ppszUTF8) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiBufSize; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getUTF8( ifpDb, NULL, 0, 0, + FLM_MAX_UINT, NULL, &uiBufSize))) + { + goto Exit; + } + + if( uiBufSize) + { + if( RC_BAD( rc = f_alloc( uiBufSize, ppszUTF8))) + { + goto Exit; + } + + if( RC_BAD( rc = getUTF8( ifpDb, *ppszUTF8, uiBufSize, 0, + FLM_MAX_UINT, NULL, NULL))) + { + goto Exit; + } + } + else + { + *ppszUTF8 = NULL; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getUTF8( + IF_Db * ifpDb, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiBufSize; + void * pvBuffer; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getUTF8( ifpDb, NULL, 0, 0, + FLM_MAX_UINT, NULL, &uiBufSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getUTF8( ifpDb, (FLMBYTE *)pvBuffer, uiBufSize, 0, + FLM_MAX_UINT, NULL, NULL))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getBinary( + IF_Db * ifpDb, + void * pvValue, + FLMUINT uiByteOffset, + FLMUINT uiBytesRequested, + FLMUINT * puiBytesReturned) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiTmp; + FLMUINT uiDataType; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // If a NULL buffer is passed in, just return the + // data length + + if( !pucValue) + { + if( RC_BAD( rc = getDataLength( pDb, &uiTmp))) + { + goto Exit; + } + + if( uiByteOffset <= uiTmp) + { + *puiBytesReturned = uiTmp - uiByteOffset; + } + else + { + *puiBytesReturned = 0; + } + + goto Exit; + } + + if( RC_BAD( rc = getIStream( pDb, &bufferIStream, &pIStream, &uiDataType))) + { + goto Exit; + } + + if( uiDataType == XFLM_TEXT_TYPE) + { + F_AsciiStorageStream asciiStream; + + if( RC_BAD( rc = asciiStream.open( pIStream))) + { + goto Exit; + } + else + { + F_Base64DecoderIStream decoderStream; + + if( RC_BAD( rc = decoderStream.open( &asciiStream))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsBinary( + &decoderStream, (FLMBYTE *)pucValue, + uiBytesRequested, uiByteOffset, puiBytesReturned))) + { + goto Exit; + } + } + } + else + { + if( RC_BAD( rc = flmReadStorageAsBinary( + pIStream, (FLMBYTE *)pucValue, + uiBytesRequested, uiByteOffset, puiBytesReturned))) + { + goto Exit; + } + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getBinary( + IF_Db * ifpDb, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiBufSize; + void * pvBuffer; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getBinary( ifpDb, NULL, 0, FLM_MAX_UINT, &uiBufSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getBinary( ifpDb, pvBuffer, 0, uiBufSize, NULL))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getAttributeValueNumber( + F_Db * pDb, + FLMUINT uiAttrName, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->getNumber64( pDb, uiAttrName, + pui64Num, pbNeg))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getAttributeValueText( + IF_Db * ifpDb, + FLMUINT uiAttrName, + eXFlmTextType eTextType, + void * pvBuffer, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned, + FLMUINT * puiBufferBytesUsed) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucStorageData = NULL; + FLMUINT uiDataType; + FLMUINT uiDataLength; + F_AttrItem * pAttrItem; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( (pAttrItem = m_pCachedNode->getAttribute( uiAttrName, NULL)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( !pAttrItem->m_uiEncDefId) + { + pucStorageData = pAttrItem->getAttrDataPtr(); + uiDataLength = pAttrItem->getAttrDataLength(); + uiDataType = pAttrItem->m_uiDataType; + + if( uiDataType == XFLM_TEXT_TYPE && eTextType == XFLM_UTF8_TEXT) + { + const FLMBYTE * pucStart = pucStorageData; + const FLMBYTE * pucEnd = pucStart + uiDataLength; + FLMUINT uiCharCount = 0; + FLMUINT uiStrByteLen = 0; + + if( pucStart) + { + if( RC_BAD( rc = flmDecodeSEN( &pucStart, pucEnd, &uiCharCount))) + { + goto Exit; + } + + uiStrByteLen = (FLMUINT)(pucEnd - pucStart); + + if( uiBufSize < uiStrByteLen) + { + goto SlowDecode; + } + + f_memcpy( pvBuffer, pucStart, uiStrByteLen); + } + + if( puiCharsReturned) + { + *puiCharsReturned = uiCharCount; + } + + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = uiStrByteLen; + } + + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pCachedNode->getIStream( pDb, uiAttrName, + &bufferIStream, &pIStream, &uiDataType, &uiDataLength))) + { + goto Exit; + } + } + +SlowDecode: + + if( RC_BAD( rc = flmReadStorageAsText( + pIStream, pucStorageData, uiDataLength, uiDataType, pvBuffer, + uiBufSize, eTextType, FLM_MAX_UINT, 0, puiCharsReturned, + puiBufferBytesUsed))) + { + goto Exit; + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( bStartedTrans) + { + pDb->abortTrans(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueUnicode( + IF_Db * ifpDb, + FLMUINT uiAttrName, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBufSize; + void * pvBuffer; + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, + NULL, 0, NULL, &uiBufSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, + (FLMUNICODE *)pvBuffer, uiBufSize, NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueUnicode( + IF_Db * ifpDb, + FLMUINT uiAttrName, + FLMUNICODE ** ppuzUnicode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBufSize; + + if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, + NULL, 0, NULL, &uiBufSize))) + { + goto Exit; + } + + if( uiBufSize) + { + if( RC_BAD( rc = f_alloc( uiBufSize, ppuzUnicode))) + { + goto Exit; + } + + if( RC_BAD( rc = getAttributeValueUnicode( ifpDb, uiAttrName, + *ppuzUnicode, uiBufSize))) + { + goto Exit; + } + } + else + { + *ppuzUnicode = NULL; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueUTF8( + IF_Db * ifpDb, + FLMUINT uiAttrName, + FLMBYTE ** ppszValue) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBufSize; + + if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, + NULL, 0, NULL, &uiBufSize))) + { + goto Exit; + } + + if( uiBufSize) + { + if( RC_BAD( rc = f_alloc( uiBufSize, ppszValue))) + { + goto Exit; + } + + if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, + *ppszValue, uiBufSize))) + { + goto Exit; + } + } + else + { + *ppszValue = NULL; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueUTF8( + IF_Db * ifpDb, + FLMUINT uiAttrName, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBufSize; + void * pvBuffer; + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, + NULL, 0, NULL, &uiBufSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getAttributeValueUTF8( ifpDb, uiAttrName, + (FLMBYTE *)pvBuffer, uiBufSize))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueBinary( + IF_Db * ifpDb, + FLMUINT uiAttrName, + void * pvValue, + FLMUINT uiBufferSize, + FLMUINT * puiLength) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->getBinary( pDb, uiAttrName, + pvValue, uiBufferSize, puiLength))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->abortTrans(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAttributeValueBinary( + IF_Db * ifpDb, + FLMUINT uiAttrName, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiBufSize; + void * pvBuffer; + + pBuffer->truncateData( 0); + + if( RC_BAD( rc = getAttributeValueBinary( ifpDb, uiAttrName, NULL, 0, + &uiBufSize))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( uiBufSize, &pvBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = getAttributeValueBinary( ifpDb, uiAttrName, pvBuffer, + uiBufSize, &uiBufSize))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + ifpDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::setAttributeValueNumber( + IF_Db * ifpDb, + FLMUINT uiAttrName, + FLMINT64 i64Value, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pAttribute = NULL; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + FLMBOOL bNeg = FALSE; + FLMBOOL bIsInIndexDef; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) + { + goto Exit; + } + + if( bIsInIndexDef) + { + if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, + (IF_DOMNode **)&pAttribute))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pAttribute->setNumber64( pDb, i64Value, + ui64Value, uiEncDefId))) + { + goto Exit; + } + } + else + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( !ui64Value) + { + if( i64Value < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)-i64Value; + } + else + { + ui64Value = (FLMUINT64)i64Value; + } + } + + if( RC_BAD( rc = m_pCachedNode->setNumber64( pDb, + uiAttrName, ui64Value, bNeg, uiEncDefId))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, uiAttrName))) + { + goto Exit; + } + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pAttribute) + { + pAttribute->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::setAttributeValueUnicode( + IF_Db * ifpDb, + FLMUINT uiAttrName, + const FLMUNICODE * puzValue, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pAttribute = NULL; + FLMBOOL bStartedTrans = FALSE; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bMustAbortOnError = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, + (IF_DOMNode **)&pAttribute))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttribute->setUnicode( (IF_Db *)pDb, puzValue, 0, + TRUE, uiEncDefId))) + { + goto Exit; + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pAttribute) + { + pAttribute->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::setAttributeValueBinary( + IF_Db * ifpDb, + FLMUINT uiAttrName, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pAttribute = NULL; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + FLMBOOL bIsInIndexDef; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) + { + goto Exit; + } + + if( bIsInIndexDef) + { + if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, + (IF_DOMNode **)&pAttribute))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttribute->setBinary( + (IF_Db *)pDb, (FLMBYTE *)pvValue, uiLength, TRUE, uiEncDefId))) + { + goto Exit; + } + } + else + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = m_pCachedNode->setBinary( pDb, + uiAttrName, pvValue, uiLength, uiEncDefId))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, uiAttrName))) + { + goto Exit; + } + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pAttribute) + { + pAttribute->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::setAttributeValueUTF8( + IF_Db * ifpDb, + FLMUINT uiAttrName, + const FLMBYTE * pucValue, + FLMUINT uiLength, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pAttribute = NULL; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiNumCharsInBuffer; + FLMUINT uiRflToken = 0; + FLMBOOL bIsInIndexDef; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->attrIsInIndexDef( uiAttrName, &bIsInIndexDef))) + { + goto Exit; + } + + if( bIsInIndexDef) + { + if( RC_BAD( rc = createAttribute( (IF_Db *)pDb, uiAttrName, + (IF_DOMNode **)&pAttribute))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttribute->setUTF8( + (IF_Db *)pDb, pucValue, uiLength, TRUE, uiEncDefId))) + { + goto Exit; + } + } + else + { + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = flmGetUTF8Length( pucValue, uiLength, + &uiLength, &uiNumCharsInBuffer))) + { + goto Exit; + } + + if( RC_BAD( m_pCachedNode->setUTF8( pDb, uiAttrName, pucValue, + uiLength, uiNumCharsInBuffer, uiEncDefId))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the value to the RFL + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttrSetValue( pDb, + m_pCachedNode, uiAttrName))) + { + goto Exit; + } + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pAttribute) + { + pAttribute->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Delete a node and all of its child/descendant-nodes. +NOTE: If the cannot-delete bit or read-only bit is set on the node, the delete + is not allowed. However, the child/descendant-nodes cannot-delete and + read-only bits will NOT be checked. If a parent node can be deleted, + then by definition all of its child/descendant nodes can also be deleted. +******************************************************************************/ +RCODE F_DOMNode::deleteNode( + IF_Db * ifpDb) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64CurNode; + F_DOMNode * pCurNode = NULL; + F_DOMNode * pParentNode = NULL; + F_DOMNode * pTmpNode = NULL; + FLMBOOL bMustAbortOnError = FALSE; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMBOOL bStartOfUpdate; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCollection; + FLMUINT uiFlags = 0; + FLMUINT uiRflToken = 0; + FLMUINT64 ui64MyNodeId; + FLMBOOL bIsIndexed; + eDomNodeType eNodeType; + + // Start a transaction if necessary + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + uiCollection = getCollection(); + eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, getParentId(), &pCurNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + rc = pCurNode->deleteAttribute( pDb, m_uiAttrNameId); + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // See if the node can be deleted + + if( getModeFlags() & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE)) + { + rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); + goto Exit; + } + + if( isRootNode()) + { + // Set flags to FLM_UPD_INTERNAL_CHANGE to prevent the node + // from being added to the document list or constraint + // checking list - no need since we are deleting the root node. + + uiFlags = FLM_UPD_INTERNAL_CHANGE; + + // If we are deleting the root node of a document in the dictionary + // collection, before deleting it, we must allow the dictionary + // to be updated. + + if( uiCollection == XFLM_DICT_COLLECTION) + { + // Call dictDocumentDone with bDeleting flag set to TRUE. + + if( RC_BAD( rc = pDb->dictDocumentDone( + getNodeId(), TRUE, NULL))) + { + goto Exit; + } + + pDb->m_pDatabase->m_DocumentList.removeNode( uiCollection, + getNodeId(), 0); + } + } + bMustAbortOnError = TRUE; + + // Traverse the tree and delete all nodes below and including the + // node we are starting on. + + ui64MyNodeId = ui64CurNode = getNodeId(); + bStartOfUpdate = TRUE; + for (;;) + { + if (RC_BAD( rc = pDb->getNode( uiCollection, ui64CurNode, &pCurNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + // If the current node has children, go to those children + + if( pCurNode->getLastChildId()) + { + ui64CurNode = pCurNode->getLastChildId(); + } + else if( pCurNode->hasAttributes()) + { + if( pCurNode->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + flmAssert( pCurNode->m_pCachedNode->m_uiAttrCount); + + if( RC_BAD( rc = pCurNode->deleteAttributes( pDb, 0, uiFlags))) + { + goto Exit; + } + } + else if (pCurNode->getAnnotationId()) + { + ui64CurNode = pCurNode->getAnnotationId(); + } + else + { + // Node has no children, no attributes, and no annotations. It is + // therefore a leaf node that can be purged. + + FLMUINT64 ui64ParentId; + FLMBOOL bWasDataNode = FALSE; + + // Save the node's parent node before purging it. That is the + // node we want to return to. + + if( RC_BAD( rc = pCurNode->getParentId( pDb, &ui64ParentId))) + { + goto Exit; + } + + // Update the index + + if( pCurNode->getNodeType() == DATA_NODE) + { + bWasDataNode = TRUE; + + // Data nodes MUST be children to an element node. + + flmAssert( ui64ParentId); + if (RC_BAD( rc = pDb->getNode( uiCollection, ui64ParentId, + (IF_DOMNode **)&pParentNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pParentNode, IX_DEL_NODE_VALUE, + bStartOfUpdate))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pCurNode, IX_DEL_NODE_VALUE, + bStartOfUpdate, &bIsIndexed))) + { + goto Exit; + } + bStartOfUpdate = FALSE; + + if( bIsIndexed) + { + if (RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pCurNode, IX_UNLINK_NODE, + bStartOfUpdate))) + { + goto Exit; + } + } + } + + bStartOfUpdate = FALSE; + flmAssert( pCurNode->getNodeType() != ATTRIBUTE_NODE); + + if (RC_BAD( rc = pCurNode->unlinkNode( pDb, uiFlags))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->purgeNode( uiCollection, ui64CurNode))) + { + goto Exit; + } + + pCurNode->Release(); + pCurNode = NULL; + + if( bWasDataNode) + { + flmAssert( pParentNode); + if( RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pParentNode, IX_ADD_NODE_VALUE, + bStartOfUpdate))) + { + goto Exit; + } + + bStartOfUpdate = FALSE; + } + + // Did we just delete the primary target or root node? + // Do NOT access m_pCachedNode after this point, because it + // may have been set to NULL by the call to purgeNode. + + if (ui64CurNode == ui64MyNodeId || !ui64ParentId) + { + break; + } + + // Go back to the parent node. + + ui64CurNode = ui64ParentId; + } + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeDelete( pDb, uiCollection, ui64MyNodeId))) + { + goto Exit; + } + +Exit: + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( pCurNode) + { + pCurNode->Release(); + } + + if( pParentNode) + { + pParentNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::deleteChildren( + IF_Db * ifpDb, + FLMUINT uiNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NextNode; + F_DOMNode * pCurNode = NULL; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiCollection; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + eDomNodeType eNodeType; + + // Start a transaction if necessary + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + uiCollection = getCollection(); + eNodeType = getNodeType(); + + // Not supported on attribute nodes + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // See if the node can be deleted + + if( getModeFlags() & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE)) + { + rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); + goto Exit; + } + + // Turn of RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Iterate over the children + + bMustAbortOnError = TRUE; + ui64NextNode = getFirstChildId(); + + while( ui64NextNode) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NextNode, XFLM_EXACT, &pCurNode))) + { + goto Exit; + } + + ui64NextNode = pCurNode->getNextSibId(); + + if( !uiNameId || uiNameId == pCurNode->getNameId()) + { + if( RC_BAD( rc = pCurNode->deleteNode( pDb))) + { + goto Exit; + } + } + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeChildrenDelete( + pDb, uiCollection, getNodeId(), uiNameId))) + { + goto Exit; + } + +Exit: + + if( pCurNode) + { + pCurNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::_syncFromDb( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDOMNode = this; + + // If we get to this point, we are going to read the node + // from the database. This instance of the node should + // not be dirty. + + flmAssert( !nodeIsDirty()); + + // Should not have any input streams open on the cached node + + if( getStreamUseCount()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->retrieveNode( pDb, + getCollection(), m_pCachedNode->getNodeId(), &pDOMNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); + } + goto Exit; + } + + if( m_uiAttrNameId) + { + if( !m_pCachedNode->m_uiAttrCount || + !m_pCachedNode->getAttribute( m_uiAttrNameId, NULL)) + { + rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getFirstAttribute( + IF_Db * ifpDb, + IF_DOMNode ** ifppAttr) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pAttrNode = NULL; + F_AttrItem * pAttrItem; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( (pAttrItem = m_pCachedNode->getFirstAttribute()) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttrNode->m_pCachedNode = m_pCachedNode; + m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; + + if( ifppAttr) + { + if( *ifppAttr) + { + (*ifppAttr)->Release(); + } + + *ifppAttr = (IF_DOMNode *)pAttrNode; + pAttrNode = NULL; + } + +Exit: + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getLastAttribute( + IF_Db * ifpDb, + IF_DOMNode ** ifppAttr) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pAttrNode = NULL; + F_AttrItem * pAttrItem; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( (pAttrItem = m_pCachedNode->getLastAttribute()) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttrNode->m_pCachedNode = m_pCachedNode; + m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; + + if( ifppAttr) + { + if( *ifppAttr) + { + (*ifppAttr)->Release(); + } + + *ifppAttr = (IF_DOMNode *)pAttrNode; + pAttrNode = NULL; + } + +Exit: + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::deleteAttribute( + IF_Db * ifpDb, + FLMUINT uiAttrName) +{ + RCODE rc = NE_XFLM_OK; + + if( !uiAttrName) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = deleteAttributes( (F_Db *)ifpDb, + uiAttrName, 0))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::deleteAttributes( + F_Db * pDb, + FLMUINT uiAttrToDelete, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiCollection; + FLMUINT uiAttrName; + F_DOMNode * pAttrNode = NULL; + F_AttrItem * pAttrItem; + FLMUINT uiPos; + FLMBOOL bIsIndexed; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Disable logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( !m_pCachedNode->m_uiAttrCount) + { + goto Exit; + } + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + uiCollection = getCollection(); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttrNode->m_pCachedNode = m_pCachedNode; + m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + for( ;;) + { + if( !uiAttrToDelete) + { + pAttrItem = m_pCachedNode->getFirstAttribute(); + uiPos = 0; + } + else + { + if( (pAttrItem = m_pCachedNode->getAttribute( uiAttrToDelete, + &uiPos)) == NULL) + { + break; + } + } + + if( uiAttrToDelete && + (pAttrItem->m_uiFlags & (FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + rc = RC_SET( NE_XFLM_DELETE_NOT_ALLOWED); + goto Exit; + } + + uiAttrName = pAttrItem->m_uiNameId; + pAttrNode->m_uiAttrNameId = uiAttrName; + + if( RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pAttrNode, IX_DEL_NODE_VALUE, + TRUE, &bIsIndexed))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + uiCollection, pAttrNode, IX_UNLINK_NODE, FALSE))) + { + goto Exit; + } + } + + // Free the attribute + + if (RC_BAD( rc = m_pCachedNode->freeAttribute( pAttrItem, uiPos))) + { + goto Exit; + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttributeDelete( pDb, uiCollection, + getNodeId(), uiAttrName))) + { + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + + if( !m_pCachedNode->m_uiAttrCount) + { + break; + } + } + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, uiFlags))) + { + goto Exit; + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::hasAttribute( + IF_Db * ifpDb, + FLMUINT uiNameId, + IF_DOMNode ** ifppAttr) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_DOMNode * pAttrNode = NULL; + F_AttrItem * pAttrItem; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = checkAttrList())) + { + goto Exit; + } + + if( (pAttrItem = m_pCachedNode->getAttribute( uiNameId, NULL)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( ifppAttr) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttrNode->m_pCachedNode = m_pCachedNode; + m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; + + if( *ifppAttr) + { + (*ifppAttr)->Release(); + } + + *ifppAttr = (IF_DOMNode *)pAttrNode; + pAttrNode = NULL; + } + +Exit: + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::insertChildElm( + FLMUINT uiChildElmOffset, + FLMUINT uiChildElmNameId, + FLMUINT64 ui64ChildElmNodeId) +{ + RCODE rc = NE_XFLM_OK; + NODE_ITEM * pChildElmNode; + + if( RC_BAD( rc = resizeChildElmList( m_nodeInfo.uiChildElmCount + 1, FALSE))) + { + goto Exit; + } + + // Remember, m_nodeInfo.uiChildElmCount has been incremented by + // resizeChildElmList, so there really isn't anything in the + // m_nodeInfo.uiChildElmCount - 1 slot. + + pChildElmNode = &m_pNodeList [ uiChildElmOffset]; + if( m_nodeInfo.uiChildElmCount > 1 && + uiChildElmOffset < m_nodeInfo.uiChildElmCount - 1) + { + f_memmove( &m_pNodeList [ uiChildElmOffset + 1], pChildElmNode, + sizeof( NODE_ITEM) * (m_nodeInfo.uiChildElmCount - + uiChildElmOffset - 1)); + + } + + pChildElmNode->uiNameId = uiChildElmNameId; + pChildElmNode->ui64NodeId = ui64ChildElmNodeId; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::createAttribute( + IF_Db * ifpDb, + FLMUINT uiNameId, + IF_DOMNode ** ifppAttr) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartedTrans = FALSE; + F_AttrElmInfo attrInfo; + F_DOMNode * pAttr = NULL; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + F_AttrItem * pAttrItem = NULL; + FLMBOOL bCreatedNewAttr = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Disable logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure our copy of the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // If this isn't an element node, return an error + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Force the transaction to abort on error beyond this point + + bMustAbortOnError = TRUE; + + // Check the attribute state + + if( RC_BAD( rc = pDb->checkAndUpdateState( + ATTRIBUTE_NODE, uiNameId))) + { + goto Exit; + } + + // Retrieve or create the attribute list node + + if( !m_pCachedNode->m_uiAttrCount || + (pAttrItem = m_pCachedNode->getAttribute( uiNameId, NULL)) == NULL) + { + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->createAttribute( + pDb, uiNameId, &pAttrItem))) + { + goto Exit; + } + + bCreatedNewAttr = TRUE; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttr->m_pCachedNode = m_pCachedNode; + m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + pAttr->m_uiAttrNameId = uiNameId; + + if( bCreatedNewAttr) + { + // Update the element. + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + + // Update the indexes + + if( RC_BAD( rc = pDb->updateIndexKeys( getCollection(), + pAttr, IX_LINK_AND_ADD_NODE, TRUE))) + { + goto Exit; + } + + // Log the attribute create + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logAttributeCreate( + pDb, getCollection(), getNodeId(), uiNameId, 0))) + { + goto Exit; + } + } + + if( ifppAttr) + { + if( *ifppAttr) + { + (*ifppAttr)->Release(); + } + + *ifppAttr = (IF_DOMNode *)pAttr; + pAttr = NULL; + } + +Exit: + + if( pAttr) + { + pAttr->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::createNode( + IF_Db * ifpDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ifppNewNode, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + F_DOMNode * pNewNode = NULL; + F_CachedNode * pNewCachedNode; + F_DOMNode * pRefNode = NULL; + F_DOMNode * pNewParent = NULL; + FLMUINT uiDataType = XFLM_NODATA_TYPE; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + + // Not supported for attributes + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Make sure an update transaction is active + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of this node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure the node type is valid + + if( eLocation == XFLM_FIRST_CHILD || eLocation == XFLM_LAST_CHILD) + { + if( RC_BAD( rc = isChildTypeValid( eNodeType))) + { + goto Exit; + } + } + else if( eLocation == XFLM_PREV_SIB || eLocation == XFLM_NEXT_SIB) + { + if( eNodeType != ELEMENT_NODE && eNodeType != DATA_NODE && + eNodeType != COMMENT_NODE && eNodeType != CDATA_SECTION_NODE && + eNodeType != PROCESSING_INSTRUCTION_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If the user is requesting a specific nodeId, then make sure + // the node is not already in use. + + if( pui64NodeId) + { + if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) + { + if( RC_BAD( rc = pDb->getNode( getCollection(), *pui64NodeId, &pNewNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + else + { + // Already in use + + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + else if( *pui64NodeId) + { + // Set to zero so we don't use it. We will return the + // new nodeId. + + *pui64NodeId = 0; + } + } + + // Look at the node's state (checking, etc.) and verify that + // the node's name ID is valid + // + // IMPORTANT NOTE: checkAndUpdateState may change m_pDict if it ends + // up calling changeItemState + + if( RC_BAD( rc = pDb->checkAndUpdateState( eNodeType, uiNameId))) + { + goto Exit; + } + + // Create the new node. + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, + getCollection(), + (FLMUINT64)(pui64NodeId + ? *pui64NodeId + : (FLMUINT64)0), + &pNewNode))) + { + goto Exit; + } + + pNewCachedNode = pNewNode->m_pCachedNode; + if( eNodeType == DATA_NODE) + { + flmAssert( getNodeType() == ELEMENT_NODE); + uiNameId = getNameId(); + } + + if( eNodeType == ELEMENT_NODE || eNodeType == DATA_NODE) + { + F_AttrElmInfo elmInfo; + + if( RC_BAD( rc = pDb->m_pDict->getElement( pDb, uiNameId, &elmInfo))) + { + goto Exit; + } + uiDataType = elmInfo.m_uiDataType; + + // Is this a node whose child elements must all be unique? + + if( eNodeType == ELEMENT_NODE && + elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) + { + flmAssert( uiDataType == XFLM_NODATA_TYPE); + pNewCachedNode->setFlags( FDOM_HAVE_CELM_LIST); + } + } + else + { + uiDataType = XFLM_NODATA_TYPE; + uiNameId = 0; + } + + pNewCachedNode->setNodeType( eNodeType); + pNewCachedNode->setDocumentId( getDocumentId()); + pNewCachedNode->setDataType( uiDataType); + + if( uiNameId) + { + pNewCachedNode->setNameId( uiNameId); + } + + if( RC_BAD( rc = pDb->updateNode( pNewCachedNode, FLM_UPD_ADD))) + { + goto Exit; + } + + if( eNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = pDb->updateIndexKeys( + pNewCachedNode->getCollection(), + pNewNode, IX_ADD_NODE_VALUE, TRUE))) + { + goto Exit; + } + } + + switch( eLocation) + { + case XFLM_FIRST_CHILD: + { + pNewParent = this; + pNewParent->AddRef(); + + if( getFirstChildId()) + { + if( RC_BAD( rc = pDb->getNode( + getCollection(), + getFirstChildId(), &pRefNode))) + { + goto Exit; + } + } + + break; + } + + case XFLM_LAST_CHILD: + { + pNewParent = this; + pNewParent->AddRef(); + break; + } + + case XFLM_PREV_SIB: + { + if( !getParentId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), + &pNewParent))) + { + goto Exit; + } + + pRefNode = this; + pRefNode->AddRef(); + break; + } + + case XFLM_NEXT_SIB: + { + if( !getParentId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( getCollection(), getParentId(), + &pNewParent))) + { + goto Exit; + } + + if( getNextSibId()) + { + if( RC_BAD( rc = pDb->getNode( getCollection(), + getNextSibId(), &pRefNode))) + { + goto Exit; + } + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( RC_BAD( rc = pNewParent->insertBefore( pDb, pNewNode, pRefNode))) + { + goto Exit; + } + + if( pui64NodeId) + { + *pui64NodeId = pNewCachedNode->getNodeId(); + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeCreate( + pDb, pNewNode->getCollection(), getNodeId(), + eNodeType, uiNameId, eLocation, pNewNode->getNodeId()))) + { + goto Exit; + } + + if( ifppNewNode) + { + if( *ifppNewNode) + { + (*ifppNewNode)->Release(); + } + + *ifppNewNode = (IF_DOMNode *)pNewNode; + pNewNode = NULL; + } + +Exit: + + if( pNewNode) + { + pNewNode->Release(); + } + + if( pRefNode) + { + pRefNode->Release(); + } + + if( pNewParent) + { + pNewParent->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::createChildElement( + IF_Db * ifpDb, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ifppNewNode, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + F_DOMNode * pTmpNode = NULL; + F_DOMNode * pNewNode = NULL; + F_CachedNode * pNewCachedNode; + F_AttrElmInfo elmInfo; + eDomNodeType eThisNodeType; + FLMUINT uiCollection; + FLMUINT uiDataType = XFLM_NODATA_TYPE; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + FLMBOOL bIsIndexed; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of this node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // Make sure the insert location is supported + + if( eLocation != XFLM_FIRST_CHILD && eLocation != XFLM_LAST_CHILD) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Make sure the node type is valid + + eThisNodeType = getNodeType(); + + if( eThisNodeType != ELEMENT_NODE && eThisNodeType != DOCUMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // A document node can only have one element node + + if( eThisNodeType == DOCUMENT_NODE && getFirstChildId()) + { + if( RC_BAD( rc = getChild( ifpDb, ELEMENT_NODE, + (IF_DOMNode **)&pTmpNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR); + goto Exit; + } + } + + // Setup misc. variables + + uiCollection = getCollection(); + + // If the user is requesting a specific nodeId, then make sure + // the node is not already in use. + + if( pui64NodeId) + { + if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) + { + if( RC_OK( rc = pDb->getNode( uiCollection, *pui64NodeId, &pNewNode))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + else if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else if( *pui64NodeId) + { + *pui64NodeId = 0; + } + } + + // Check the element's state + + if( RC_BAD( rc = pDb->checkAndUpdateState( ELEMENT_NODE, uiNameId))) + { + goto Exit; + } + + // Create the new node. + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, + uiCollection, + (FLMUINT64)(pui64NodeId + ? *pui64NodeId + : (FLMUINT64)0), + &pNewNode))) + { + goto Exit; + } + + pNewCachedNode = pNewNode->m_pCachedNode; + + // Make sure the parent node (this) can be updated + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + // Does the parent expect all children to be unique? + + if( getModeFlags() & FDOM_HAVE_CELM_LIST) + { + FLMUINT uiInsertPos; + + if( m_pCachedNode->findChildElm( uiNameId, &uiInsertPos)) + { + rc = RC_SET( NE_XFLM_DOM_DUPLICATE_ELEMENT); + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->insertChildElm( uiInsertPos, + uiNameId, pNewCachedNode->getNodeId()))) + { + goto Exit; + } + } + + // Update the element's state + + if( RC_BAD( rc = pDb->m_pDict->getElement( pDb, uiNameId, &elmInfo))) + { + goto Exit; + } + + uiDataType = elmInfo.m_uiDataType; + + // Is this a node whose children must all be unique? + + if( elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) + { + flmAssert( uiDataType == XFLM_NODATA_TYPE); + pNewCachedNode->setFlags( FDOM_HAVE_CELM_LIST); + } + + pNewCachedNode->setNodeType( ELEMENT_NODE); + pNewCachedNode->setParentId( getNodeId()); + pNewCachedNode->setDocumentId( getDocumentId()); + pNewCachedNode->setDataType( uiDataType); + pNewCachedNode->setNameId( uiNameId); + + // Set the sibling pointers + + if( eLocation == XFLM_LAST_CHILD) + { + if( getLastChildId()) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, + getLastChildId(), &pTmpNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + flmAssert( pTmpNode->getNextSibId() == 0); + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + pTmpNode->setNextSibId( pNewCachedNode->getNodeId()); + pNewCachedNode->setPrevSibId( getLastChildId()); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, 0))) + { + goto Exit; + } + } + else + { + setFirstChildId( pNewCachedNode->getNodeId()); + } + + setLastChildId( pNewCachedNode->getNodeId()); + } + else + { + flmAssert( eLocation == XFLM_FIRST_CHILD); + + if( getFirstChildId()) + { + if( RC_BAD( rc = pDb->getNode( + uiCollection, getFirstChildId(), &pTmpNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + flmAssert( pTmpNode->getPrevSibId() == 0); + + if( RC_BAD( rc = pTmpNode->makeWriteCopy( pDb))) + { + goto Exit; + } + + pTmpNode->setPrevSibId( pNewCachedNode->getNodeId()); + pNewCachedNode->setNextSibId( getFirstChildId()); + + if( RC_BAD( rc = pDb->updateNode( pTmpNode->m_pCachedNode, 0))) + { + goto Exit; + } + } + else + { + setLastChildId( pNewCachedNode->getNodeId()); + } + + setFirstChildId( pNewCachedNode->getNodeId()); + } + + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateNode( pNewCachedNode, FLM_UPD_ADD))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + pNewNode, IX_ADD_NODE_VALUE, TRUE, &bIsIndexed))) + { + goto Exit; + } + + if( bIsIndexed) + { + if( RC_BAD( rc = pDb->updateIndexKeys( uiCollection, + pNewNode, IX_LINK_NODE, FALSE, &bIsIndexed))) + { + goto Exit; + } + } + + if( pui64NodeId) + { + *pui64NodeId = pNewCachedNode->getNodeId(); + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeCreate( pDb, uiCollection, + pNewCachedNode->getParentId(), + ELEMENT_NODE, uiNameId, XFLM_LAST_CHILD, pNewCachedNode->getNodeId()))) + { + goto Exit; + } + + if( ifppNewNode) + { + if( *ifppNewNode) + { + (*ifppNewNode)->Release(); + } + + *ifppNewNode = (IF_DOMNode *)pNewNode; + pNewNode = NULL; + } + +Exit: + + if( pNewNode) + { + pNewNode->Release(); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::createAnnotation( + IF_Db * ifpDb, + IF_DOMNode ** ifppAnnotation, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_CachedNode * pCachedNode; + FLMBOOL bMustAbortOnError = FALSE; + F_Db * pDb = (F_Db *)ifpDb; + F_DOMNode ** ppAnnotation = (F_DOMNode **)ifppAnnotation; + FLMBOOL bStartedTrans = FALSE; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure our copy of this node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + // Not supported on attribute nodes + + if( eNodeType == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If the node already has an annotation, return an error + + if( getAnnotationId()) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + + // If the user is requesting a specific nodeId, then make sure + // the node is not already in use. + + if( pui64NodeId) + { + if( *pui64NodeId && (pDb->m_uiFlags & FDB_REBUILDING_DATABASE)) + { + if( RC_BAD( rc = pDb->getNode( getCollection(), *pui64NodeId, &pNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + else + { + // Already in use + + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + else if( *pui64NodeId) + { + // Set to zero so we don't use it. We will return the + // new nodeId. + *pui64NodeId = 0; + } + } + + // Create the new node. + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( pDb, + getCollection(), + (FLMUINT64)(pui64NodeId + ? *pui64NodeId + : (FLMUINT64)0), + &pNode))) + { + goto Exit; + } + + pCachedNode = pNode->m_pCachedNode; + pCachedNode->setNodeType( ANNOTATION_NODE); + pCachedNode->setDocumentId( getDocumentId()); + pCachedNode->setParentId( getNodeId()); + pCachedNode->setDataType( XFLM_NODATA_TYPE); + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pDb->updateNode( pCachedNode, FLM_UPD_ADD))) + { + goto Exit; + } + + // Link the annotation to this node + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + setAnnotationId( pCachedNode->getNodeId()); + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + if( bStartedTrans) + { + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + bStartedTrans = FALSE; + } + + if( pui64NodeId) + { + *pui64NodeId = pCachedNode->getNodeId(); + } + + // Release any node that the passed-in parameter may be + // pointing at + + if( *ppAnnotation) + { + (*ppAnnotation)->Release(); + } + + *ppAnnotation = pNode; + pNode = NULL; + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( RC_BAD( rc)) + { + if( bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::hasAnnotation( + IF_Db * ifpDb, + FLMBOOL * pbHasAnnotation) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + *pbHasAnnotation = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) + { + goto Exit; + } + + if( getAnnotationId()) + { + *pbHasAnnotation = TRUE; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getAnnotation( + IF_Db * ifpDb, + IF_DOMNode ** ifppAnnotation) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) + { + goto Exit; + } + + if( !getAnnotationId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( getCollection(), getAnnotationId(), + ifppAnnotation))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getDocumentId( + IF_Db * ifpDb, + FLMUINT64 * pui64DocId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + *pui64DocId = m_pCachedNode->getDocumentId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getNodeId( + IF_Db * ifpDb, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pui64NodeId = m_pCachedNode->getNodeId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: puiAttrNameId returns 0 if the node isn't an attribute. +******************************************************************************/ +RCODE F_DOMNode::getNodeId( + F_Db * pDb, + FLMUINT64 * pui64NodeId, + FLMUINT * puiAttrNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + *pui64NodeId = m_pCachedNode->getNodeId(); + + if( getNodeType() == ATTRIBUTE_NODE) + { + *puiAttrNameId = m_uiAttrNameId; + } + else + { + *puiAttrNameId = 0; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getParentId( + IF_Db * ifpDb, + FLMUINT64 * pui64ParentId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + *pui64ParentId = getParentId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: COM version of the getPrevSibId method. This method ensures that + the DOM node is up-to-date. +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getPrevSibId( + IF_Db * ifpDb, + FLMUINT64 * pui64PrevSibId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pui64PrevSibId = getPrevSibId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return rc; +} + + +/***************************************************************************** +Desc: COM version of the getNextSibId method. This method ensures that + the DOM node is up-to-date. +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getNextSibId( + IF_Db * ifpDb, + FLMUINT64 * pui64NextSibId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pui64NextSibId = getNextSibId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return rc; +} + +/***************************************************************************** +Desc: COM version of the getFirstChildId method. This method ensures that + the DOM node is up-to-date. +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getFirstChildId( + IF_Db * ifpDb, + FLMUINT64 * pui64FirstChildId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pui64FirstChildId = getFirstChildId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return rc; +} + + +/***************************************************************************** +Desc: COM version of the getLastChildId method. This method ensures that + the DOM node is up-to-date. +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getLastChildId( + IF_Db * ifpDb, + FLMUINT64 * pui64LastChildId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pui64LastChildId = getLastChildId(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return rc; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::isNamespaceDecl( + IF_Db * ifpDb, + FLMBOOL * pbIsNamespaceDecl) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + *pbIsNamespaceDecl = isNamespaceDecl(); + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::hasChildren( + IF_Db * ifpDb, + FLMBOOL * pbHasChildren) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( (F_Db *)ifpDb))) + { + goto Exit; + } + + if (getNodeType() == ATTRIBUTE_NODE) + { + *pbHasChildren = FALSE; + } + else + { + *pbHasChildren = getFirstChildId() ? TRUE : FALSE; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getNameId( + IF_Db * ifpDb, + FLMUINT * puiNameId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + if( eNodeType == ATTRIBUTE_NODE) + { + *puiNameId = m_uiAttrNameId; + } + else if( m_pCachedNode) + { + *puiNameId = getNameId(); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getEncDefId( + IF_Db * ifpDb, + FLMUINT * puiEncDefNumber) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getEncDefId( + m_uiAttrNameId, puiEncDefNumber))) + { + goto Exit; + } + } + else if( m_pCachedNode) + { + *puiEncDefNumber = getEncDefId(); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getAnnotationId( + IF_Db * ifpDb, + FLMUINT64 * pui64AnnotationId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + *pui64AnnotationId = 0; + } + else if( m_pCachedNode) + { + *pui64AnnotationId = getAnnotationId(); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::getDataType( + IF_Db * ifpDb, + FLMUINT * puiDataType) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getDataType( + m_uiAttrNameId, puiDataType))) + { + goto Exit; + } + } + else + { + *puiDataType = m_pCachedNode->getDataType(); + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getPrefixId( + IF_Db * ifpDb, + FLMUINT * puiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiPrefix = 0; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getPrefixId( + m_uiAttrNameId, &uiPrefix))) + { + goto Exit; + } + } + else + { + if( (uiPrefix = m_pCachedNode->getPrefixId()) != 0) + { + if( RC_BAD( rc = pDb->m_pDict->getPrefix( uiPrefix, NULL))) + { + if( rc != NE_XFLM_BAD_PREFIX) + { + goto Exit; + } + + rc = NE_XFLM_OK; + uiPrefix = 0; + } + } + } + + *puiPrefixId = uiPrefix; + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::hasAttributes( + IF_Db * ifpDb, + FLMBOOL * pbHasAttrs) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + *pbHasAttrs = FALSE; + goto Exit; + } + + *pbHasAttrs = m_pCachedNode->m_uiAttrCount ? TRUE : FALSE; + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::hasNextSibling( + IF_Db * ifpDb, + FLMBOOL * pbHasNextSibling) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pbHasNextSibling = (m_pCachedNode->getNextSibId() && + getParentId()) + ? TRUE + : FALSE; + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DOMNode::hasPreviousSibling( + IF_Db * ifpDb, + FLMBOOL * pbHasPreviousSibling) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + *pbHasPreviousSibling = (m_pCachedNode->getPrevSibId() && + getParentId()) + ? TRUE + : FALSE; + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getAncestorElement( + IF_Db * ifpDb, + FLMUINT uiNameId, + IF_DOMNode ** ifppAncestor) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_DOMNode * pTmpNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCollection; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + pTmpNode = this; + pTmpNode->AddRef(); + uiCollection = getCollection(); + + while( pTmpNode) + { + if( !pTmpNode->getParentId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( + uiCollection, pTmpNode->getParentId(), &pTmpNode))) + { + goto Exit; + } + + if( pTmpNode->getNameId() == uiNameId) + { + break; + } + } + + if( !pTmpNode) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( *ifppAncestor) + { + (*ifppAncestor)->Release(); + } + + *ifppAncestor = pTmpNode; + pTmpNode = NULL; + +Exit: + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getDescendantElement( + IF_Db * ifpDb, + FLMUINT uiNameId, + IF_DOMNode ** ifppDescendant) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_DOMNode * pContextNode = NULL; + F_DOMNode * pFoundNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCollection; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + pContextNode = this; + pContextNode->AddRef(); + + uiCollection = getCollection(); + while( pContextNode) + { + if( pContextNode->getFirstChildId()) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, + pContextNode->getFirstChildId(), &pContextNode))) + { + goto Exit; + } + } + else if( pContextNode->getNextSibId()) + { +Get_Next_Sib: + + if( pContextNode->getNodeId() == getNodeId()) + { + break; + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, + pContextNode->getNextSibId(), &pContextNode))) + { + goto Exit; + } + } + else + { + if( pContextNode->getNodeId() == getNodeId()) + { + break; + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, + pContextNode->getParentId(), &pContextNode))) + { + goto Exit; + } + + goto Get_Next_Sib; + } + + if( pContextNode->getNodeType() != ELEMENT_NODE) + { + continue; + } + + if( pContextNode->getNameId() == uiNameId) + { + pFoundNode = pContextNode; + pFoundNode->AddRef(); + break; + } + } + + if( *ifppDescendant) + { + (*ifppDescendant)->Release(); + } + + *ifppDescendant = pFoundNode; + pFoundNode = NULL; + +Exit: + + if( pContextNode) + { + pContextNode->Release(); + } + + if( pFoundNode) + { + pFoundNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getDocumentNode( + IF_Db * ifpDb, + IF_DOMNode ** ifppDoc) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( isRootNode()) + { + IF_DOMNode * pTmpNode = *ifppDoc; + + *ifppDoc = this; + (*ifppDoc)->AddRef(); + + if( pTmpNode) + { + pTmpNode->Release(); + } + + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( getCollection(), + getDocumentId(), ifppDoc))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getParentNode( + IF_Db * ifpDb, + IF_DOMNode ** ifppParent) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( !getParentId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( + getCollection(), getParentId(), ifppParent))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getFirstChild( + IF_Db * ifpDb, + IF_DOMNode ** ifppChild) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( !getFirstChildId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( getCollection(), + getFirstChildId(), ifppChild))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getLastChild( + IF_Db * ifpDb, + IF_DOMNode ** ifppChild) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( !getLastChildId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( getCollection(), + getLastChildId(), ifppChild))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getChild( + IF_Db * ifpDb, + eDomNodeType eNodeType, + IF_DOMNode ** ppChild) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pCurNode = NULL; + FLMUINT64 ui64NodeId; + F_Db * pDb = (F_Db *)ifpDb; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // We can do a quick lookup if this node is an element where we are + // maintaining a child element list. + + if( eNodeType == ELEMENT_NODE && + (getModeFlags() & FDOM_HAVE_CELM_LIST)) + { + if( getChildElmCount()) + { + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( + getCollection(), getChildElmNodeId( 0), ppChild))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + } + } + else + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + + goto Exit; + } + + ui64NodeId = getFirstChildId(); + + for( ;;) + { + if( !ui64NodeId) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( + getCollection(), ui64NodeId, (IF_DOMNode **)&pCurNode))) + { + goto Exit; + } + + if( pCurNode->getNodeType() == eNodeType) + { + if( *ppChild) + { + (*ppChild)->Release(); + } + + *ppChild = pCurNode; + pCurNode = NULL; + break; + } + + ui64NodeId = pCurNode->getNextSibId(); + } + +Exit: + + if( pCurNode) + { + pCurNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getChildElement( + IF_Db * ifpDb, + FLMUINT uiNameId, + IF_DOMNode ** ppChild, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pCurNode = NULL; + FLMUINT64 ui64NodeId; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiCollection; + FLMUINT uiElmPos; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // We can do a quick lookup if this node is an element where we are + // maintaining a child element list. + + if( getModeFlags() & FDOM_HAVE_CELM_LIST) + { + if( !getChildElmCount()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( !findChildElm( uiNameId, &uiElmPos) && + (!uiFlags || (uiFlags & XFLM_EXACT) || + uiElmPos >= getChildElmCount())) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + // At this point, if we did an exact match, we will be on the node. + // If we did an inclusive match, we will be either on the node or + // past it. If we did an exclusive match, we need to determine if + // we are on the node or past it. If we are on the node, we need + // to try to move past it, unless we are at the end of the list, in + // which case we cannot go exclusive. + + if( uiFlags & XFLM_EXCL) + { + // If we found the node, we need to go one past it, if there + // is one past it to go to. If not, we must return + // not found. + + if( getChildElmNameId( uiElmPos) == uiNameId) + { + if( uiElmPos == getChildElmCount() - 1) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + else + { + uiElmPos++; + } + } + } + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( getCollection(), + getChildElmNodeId( uiElmPos), ppChild))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + } + else + { + // Cannot set uiFlags for nodes that are not unique-child nodes. + + if( uiFlags) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_FLAG); + goto Exit; + } + + ui64NodeId = getFirstChildId(); + uiCollection = getCollection(); + for( ;;) + { + if( !ui64NodeId) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( + uiCollection, ui64NodeId, (IF_DOMNode **)&pCurNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if( pCurNode->getNodeType() == ELEMENT_NODE && + pCurNode->getNameId() == uiNameId) + { + if( *ppChild) + { + (*ppChild)->Release(); + } + + *ppChild = pCurNode; + pCurNode = NULL; + break; + } + + ui64NodeId = pCurNode->getNextSibId(); + } + } + +Exit: + + if( pCurNode) + { + pCurNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getSiblingElement( + IF_Db * ifpDb, + FLMUINT uiNameId, + FLMBOOL bNext, + IF_DOMNode ** ppSibling) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pCurNode = NULL; + FLMUINT64 ui64NodeId; + F_Db * pDb = (F_Db *)ifpDb; + FLMUINT uiCollection; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( bNext) + { + ui64NodeId = getNextSibId(); + } + else + { + ui64NodeId = getPrevSibId(); + } + + uiCollection = getCollection(); + for( ;;) + { + if( !ui64NodeId) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ((IF_Db *)pDb)->getNode( + uiCollection, ui64NodeId, (IF_DOMNode **)&pCurNode))) + { + goto Exit; + } + + if( pCurNode->getNodeType() == ELEMENT_NODE && + pCurNode->getNameId() == uiNameId) + { + if( *ppSibling) + { + (*ppSibling)->Release(); + } + + *ppSibling = pCurNode; + pCurNode = NULL; + break; + } + + if( bNext) + { + ui64NodeId = pCurNode->getNextSibId(); + } + else + { + ui64NodeId = pCurNode->getPrevSibId(); + } + } + +Exit: + + if( pCurNode) + { + pCurNode->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getPreviousSibling( + IF_Db * ifpDb, + IF_DOMNode ** ifppSib) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( !(*ifppSib)) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->getPrevSiblingNode( + m_uiAttrNameId, ifppSib))) + { + goto Exit; + } + } + else + { + if( !getPrevSibId() || !getParentId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( + getCollection(), getPrevSibId(), ifppSib))) + { + goto Exit; + } + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getNextSibling( + IF_Db * ifpDb, + IF_DOMNode ** ifppSib) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( getNodeType() == ATTRIBUTE_NODE) + { + if( !(*ifppSib)) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = m_pCachedNode->getNextSiblingNode( + m_uiAttrNameId, ifppSib))) + { + goto Exit; + } + } + else + { + if( !getNextSibId() || !getParentId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( getCollection(), + getNextSibId(), ifppSib))) + { + goto Exit; + } + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getPreviousDocument( + IF_Db * ifpDb, + IF_DOMNode ** ifppDoc) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pNode = NULL; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT64 ui64StartDocId; + FLMUINT64 ui64DocumentId; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + F_Btree * pBTree = NULL; + FLMUINT uiCollection; + F_COLLECTION * pCollection; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + uiCollection = getCollection(); + + if( RC_BAD( rc = syncFromDb( pDb))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + + ui64DocumentId = ui64StartDocId = getDocumentId(); + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64StartDocId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) + { + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + + goto Exit; + } + + for (;;) + { + // Need to go to the previous node. + + if( RC_BAD( rc = pBTree->btPrevEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + goto Exit; + } + + if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64DocumentId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64DocumentId, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + // If the node is a root node, we have a document we can + // process. + + if( pNode->isRootNode() && pNode->getNodeId() < ui64StartDocId) + { + if( *ifppDoc) + { + (*ifppDoc)->Release(); + } + + // Just use the reference on pNode for *ifppDoc + + *ifppDoc = pNode; + pNode = NULL; + goto Exit; + } + } + } + else + { + // If we are not at the root node of the document, + // jump to the root. + + if( !isRootNode()) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, getDocumentId(), &pNode))) + { + goto Exit; + } + } + else + { + pNode = this; + pNode->AddRef(); + } + + if( !pNode->getPrevSibId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( uiCollection, + pNode->getPrevSibId(), ifppDoc))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pBTree) + { + pBTree->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DOMNode::getNextDocument( + IF_Db * ifpDb, + IF_DOMNode ** ifppDoc) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + F_DOMNode * pNode = NULL; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocumentId; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + F_Btree * pBTree = NULL; + FLMUINT uiCollection; + F_COLLECTION * pCollection; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + uiCollection = getCollection(); + + if( RC_BAD( rc = syncFromDb( pDb))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + + ui64DocumentId = getDocumentId(); + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64DocumentId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_EXCL))) + { + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + + goto Exit; + } + + for (;;) + { + if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64DocumentId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( + uiCollection, ui64DocumentId, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + // If the node is a root node, we have a document we can + // process. + + if( pNode->isRootNode()) + { + if( *ifppDoc) + { + (*ifppDoc)->Release(); + } + + // Just use the reference on pNode for *ifppDoc + + *ifppDoc = pNode; + pNode = NULL; + goto Exit; + } + + // Need to go to the next node. + + if( RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + goto Exit; + } + } + } + else + { + // If we are not at the root node of the document, + // jump to the root. + + if( !isRootNode()) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, getDocumentId(), &pNode))) + { + goto Exit; + } + } + else + { + pNode = this; + pNode->AddRef(); + } + + if( !pNode->getNextSibId()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = ifpDb->getNode( + uiCollection, getNextSibId(), ifppDoc))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pBTree) + { + pBTree->Release(); + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadSEN( + IF_IStream * pIStream, + FLMUINT * puiValue, + FLMUINT * puiLength) +{ + RCODE rc; + FLMUINT64 ui64Tmp; + + if( RC_BAD( rc = flmReadSEN64( pIStream, &ui64Tmp, puiLength))) + { + goto Exit; + } + + if( ui64Tmp > ~((FLMUINT)0)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( puiValue) + { + *puiValue = (FLMUINT)ui64Tmp; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadSEN64( + IF_IStream * pIStream, + FLMUINT64 * pui64Value, + FLMUINT * puiLength) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + FLMUINT uiSENLength; + FLMBYTE ucBuffer[ FLM_MAX_NUM_BUF_SIZE]; + const FLMBYTE * pucBuffer; + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( + (char *)&ucBuffer[ 0], uiLen, &uiLen))) + { + goto Exit; + } + + uiSENLength = gv_ucSENLengthArray[ ucBuffer[ 0]]; + uiLen = uiSENLength - 1; + + if( puiLength) + { + *puiLength = uiSENLength; + } + + if( pui64Value) + { + pucBuffer = &ucBuffer[ 1]; + } + else + { + pucBuffer = NULL; + } + + if( uiLen) + { + if( RC_BAD( rc = pIStream->read( + (char *)pucBuffer, uiLen, &uiLen))) + { + goto Exit; + } + } + + if( pui64Value) + { + pucBuffer = &ucBuffer[ 0]; + if( RC_BAD( rc = flmDecodeSEN64( &pucBuffer, + &ucBuffer[ FLM_MAX_NUM_BUF_SIZE], pui64Value))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads the next UTF-8 character from the stream +****************************************************************************/ +RCODE flmReadUTF8CharAsUnicode( + IF_IStream * pIStream, + FLMUNICODE * puChar) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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_XFLM_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: +Notes: pucBuf must be able to contain at least 3 bytes +******************************************************************************/ +RCODE flmReadUTF8CharAsUTF8( + IF_IStream * pIStream, + FLMBYTE * pucBuf, + FLMUINT * puiLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + + if( *puiLen == 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( pucBuf, uiLen, &uiLen))) + { + goto Exit; + } + + if( pucBuf[ 0] <= 0x7F) + { + if( !pucBuf[ 0]) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + *puiLen = 1; + goto Exit; + } + + if( *puiLen < 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( &pucBuf[ 1], uiLen, &uiLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + } + goto Exit; + } + + if( (pucBuf[ 1] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + if( (pucBuf[ 0] >> 5) == 0x06) + { + *puiLen = 2; + goto Exit; + } + + if( *puiLen < 3) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + uiLen = 1; + if( RC_BAD( rc = pIStream->read( &pucBuf[ 2], uiLen, &uiLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + } + goto Exit; + } + + if( (pucBuf[ 0] >> 4) != 0x0E || (pucBuf[ 2] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + *puiLen = 3; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadStorageAsText( + IF_IStream * pIStream, + FLMBYTE * pucStorageData, + FLMUINT uiDataLen, + FLMUINT uiDataType, + void * pvBuffer, + FLMUINT uiBufLen, + eXFlmTextType eTextType, + FLMUINT uiMaxCharsToRead, + FLMUINT uiCharOffset, + FLMUINT * puiCharsRead, + FLMUINT * puiBufferBytesUsed) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucByte; + FLMUINT uiCharsDecoded = 0; + FLMUINT uiSENLen; + FLMUINT uiNumChars; + FLMUINT uiCharsOutput = 0; + FLMUNICODE * puzOutBuf = NULL; + FLMBYTE * pszOutBuf = NULL; + void * pvEnd = ((char *)pvBuffer) + uiBufLen; + const FLMBYTE * pucTmp; + FLMUINT uiLen; + FLMBYTE ucSENBuf[ 16]; + FLMBYTE ucConvBuf[ 64]; + FLMUINT uiLastUTFLen = 0; + IF_IStream * pStream = pIStream; + F_BufferIStream convStream; + F_BinaryToTextStream binaryToTextStream; + + // If the value is a number, convert to text + + if( uiDataType == XFLM_NUMBER_TYPE) + { + FLMBYTE ucNumBuf[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiNumBufLen; + + // Read the entire number into the temporary buffer. + // NOTE: Numbers are not encoded with a length. It + // is expected that the number of bytes remaining + // in the stream will be equal to the exact number of + // bytes representing the number. If this is not the case, + // either a corruption error or an incorrect value will + // be returned. + + uiNumBufLen = sizeof( ucNumBuf); + + if( pStream) + { + if( RC_BAD( rc = pStream->read( (char *)ucNumBuf, + uiNumBufLen, &uiNumBufLen))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + } + else + { + f_memcpy( ucNumBuf, pucStorageData, f_max( uiNumBufLen, uiDataLen)); + } + + // Convert the storage number to storage text + + uiDataLen = sizeof( ucConvBuf); + if( RC_BAD( rc = flmStorageNum2StorageText( ucNumBuf, uiNumBufLen, + ucConvBuf, &uiDataLen))) + { + goto Exit; + } + + if( RC_BAD( rc = convStream.open( ucConvBuf, uiDataLen))) + { + goto Exit; + } + + pStream = &convStream; + pucStorageData = NULL; + } + else if( uiDataType == XFLM_BINARY_TYPE) + { + if( !pStream) + { + if( RC_BAD( rc = convStream.open( pucStorageData, uiDataLen))) + { + goto Exit; + } + + pStream = &convStream; + } + + if( RC_BAD( rc = binaryToTextStream.open( pStream, + uiDataLen, &uiDataLen))) + { + goto Exit; + } + + pStream = &binaryToTextStream; + pucStorageData = NULL; + } + else if( uiDataType == XFLM_NODATA_TYPE) + { + goto Empty_String; + } + else if( uiDataType != XFLM_TEXT_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + // Determine the SEN length + + if( pStream) + { + uiLen = 1; + if( RC_BAD( rc = pStream->read( (char *)&ucSENBuf[ 0], uiLen, &uiLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { +Empty_String: + rc = NE_XFLM_OK; + if( eTextType == XFLM_UTF8_TEXT) + { + if( pvBuffer) + { + *((FLMBYTE *)pvBuffer) = 0; + } + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = 1; + } + } + else + { + flmAssert( eTextType == XFLM_UNICODE_TEXT); + if( pvBuffer) + { + *((FLMUNICODE *)pvBuffer) = 0; + } + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = sizeof( FLMUNICODE); + } + } + } + goto Exit; + } + uiDataLen -= uiLen; + } + else + { + if( !uiDataLen) + { + goto Empty_String; + } + + ucSENBuf[ 0] = *pucStorageData++; + uiDataLen--; + } + + if( (uiSENLen = flmGetSENLength( ucSENBuf[ 0])) > 1) + { + uiLen = uiSENLen - 1; + + if( pStream) + { + if( RC_BAD( rc = pStream->read( + (char *)&ucSENBuf[ 1], uiLen, &uiLen))) + { + goto Exit; + } + } + else + { + if( uiDataLen < uiLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_EOF_HIT); + goto Exit; + } + + f_memcpy( &ucSENBuf[ 1], pucStorageData, uiLen); + pucStorageData += uiLen; + } + + uiDataLen -= uiLen; + } + + pucTmp = &ucSENBuf[ 0]; + if( RC_BAD( rc = flmDecodeSEN( + &pucTmp, &ucSENBuf[ uiSENLen], &uiNumChars))) + { + goto Exit; + } + + // If only a length is needed (number of bytes), we can + // return that without parsing the string + + if( !pvBuffer) + { + uiCharsOutput = uiCharOffset >= uiNumChars + ? 0 + : uiNumChars - uiCharOffset; + + if( puiBufferBytesUsed) + { + if( eTextType == XFLM_UNICODE_TEXT) + { + *puiBufferBytesUsed = (uiCharsOutput + 1) * sizeof( FLMUNICODE); + } + else // UTF-8 + { + *puiBufferBytesUsed = uiDataLen; + } + } + + goto Exit; + } + + if( eTextType == XFLM_UTF8_TEXT) + { + if( !uiBufLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + pszOutBuf = (FLMBYTE *)pvBuffer; + } + else + { + flmAssert( eTextType == XFLM_UNICODE_TEXT); + if( uiBufLen < sizeof( FLMUNICODE)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + puzOutBuf = (FLMUNICODE *)pvBuffer; + } + + // If we have a zero-length string, jump to exit. + + if( !uiNumChars) + { + // Read the null terminator + + if( pStream) + { + uiLen = 1; + if( RC_BAD( rc = pStream->read( (char *)&ucByte, uiLen, &uiLen))) + { + goto Exit; + } + } + else + { + if( !uiDataLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + ucByte = *pucStorageData++; + uiDataLen--; + } + + if( ucByte != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + goto Empty_String; + } + + // Parse through the string, outputting data to the buffer as we go. + + uiCharsDecoded = 0; + if( eTextType == XFLM_UNICODE_TEXT) + { + FLMUNICODE uChar; + + while( uiCharsOutput < uiMaxCharsToRead) + { + if( pStream) + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( pStream, &uChar))) + { + if( rc == NE_XFLM_EOF_HIT) + { +Unicode_EOF_Hit: + if( uiCharsDecoded != uiNumChars) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + } + else + { + FLMBYTE ucTmpUni[ 3]; + + if( !uiDataLen) + { + goto Unicode_EOF_Hit; + } + + ucTmpUni[ 0] = *pucStorageData++; + uiDataLen--; + + if( ucTmpUni[ 0] <= 0x7F) + { + if( !ucTmpUni[ 0]) + { + goto Unicode_EOF_Hit; + } + + uChar = (FLMUNICODE)ucTmpUni[ 0]; + } + else + { + if( !uiDataLen) + { + goto Unicode_EOF_Hit; + } + + ucTmpUni[ 1] = *pucStorageData++; + uiDataLen--; + + if( (ucTmpUni[ 1] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + if( (ucTmpUni[ 0] >> 5) == 0x06) + { + uChar = ((FLMUNICODE)( ucTmpUni[ 0] - 0xC0) << 6) + + (FLMUNICODE)(ucTmpUni[ 1] - 0x80); + } + else + { + if( !uiDataLen) + { + goto Unicode_EOF_Hit; + } + + ucTmpUni[ 2] = *pucStorageData++; + uiDataLen--; + + if( (ucTmpUni[ 0] >> 4) != 0x0E || + (ucTmpUni[ 2] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + uChar = ((FLMUNICODE)(ucTmpUni[ 0] - 0xE0) << 12) + + ((FLMUNICODE)(ucTmpUni[ 1] - 0x80) << 6) + + (FLMUNICODE)(ucTmpUni[ 2] - 0x80); + } + } + } + + if( ++uiCharsDecoded > uiNumChars) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( uiCharOffset) + { + uiCharOffset--; + continue; + } + + if( puzOutBuf) + { + if( puzOutBuf + 1 >= pvEnd) + { + goto Overflow_Error; + } + + *puzOutBuf++ = uChar; + uiCharsOutput++; + } + else + { + if( uChar <= 0xFF) + { + if( pszOutBuf + 1 >= pvEnd) + { + goto Overflow_Error; + } + *pszOutBuf++ = f_tonative( (FLMBYTE)uChar); + uiCharsOutput++; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + } + } + else // UTF-8 + { + flmAssert( eTextType == XFLM_UTF8_TEXT); + while( uiCharsOutput < uiMaxCharsToRead) + { + if( (uiLen = ((FLMBYTE *)pvEnd) - pszOutBuf) == 0) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Overflow_Error; + } + + if( pStream) + { + if( RC_BAD( rc = flmReadUTF8CharAsUTF8( + pStream, pszOutBuf, &uiLen))) + { + if( rc == NE_XFLM_CONV_DEST_OVERFLOW) + { + goto Overflow_Error; + } + + if( rc == NE_XFLM_EOF_HIT) + { +UTF8_EOF_Hit: + if( uiCharsDecoded != uiNumChars) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + } + else + { + if( !uiDataLen) + { + goto UTF8_EOF_Hit; + } + + pszOutBuf[ 0] = *pucStorageData++; + uiDataLen--; + + if( pszOutBuf[ 0] <= 0x7F) + { + if( !pszOutBuf[ 0]) + { + goto UTF8_EOF_Hit; + } + + uiLen = 1; + } + else + { + if( uiLen < 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Overflow_Error; + } + + if( !uiDataLen) + { + goto UTF8_EOF_Hit; + } + + pszOutBuf[ 1] = *pucStorageData++; + uiDataLen--; + + if( (pszOutBuf[ 1] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + if( (pszOutBuf[ 0] >> 5) == 0x06) + { + uiLen = 2; + } + else + { + if( uiLen < 3) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Overflow_Error; + } + + if( !uiDataLen) + { + goto UTF8_EOF_Hit; + } + + pszOutBuf[ 2] = *pucStorageData++; + uiDataLen--; + + if( (pszOutBuf[ 0] >> 4) != 0x0E || + (pszOutBuf[ 2] >> 6) != 0x02) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_UTF8); + goto Exit; + } + + uiLen = 3; + } + } + } + + if( ++uiCharsDecoded > uiNumChars) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( uiCharOffset) + { + uiCharOffset--; + continue; + } + + if( pszOutBuf + uiLen >= pvEnd) + { + goto Overflow_Error; + } + + pszOutBuf += uiLen; + uiLastUTFLen = uiLen; + uiCharsOutput++; + } + } + + // There is room for the 0 terminating character, but we + // will not increment the return length, but we will output the + // number of buffer bytes used + + if( eTextType == XFLM_UTF8_TEXT && pszOutBuf < pvEnd) + { + *pszOutBuf = 0; + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = (FLMUINT)(pszOutBuf - (FLMBYTE *)pvBuffer) + 1; + } + } + else if( eTextType == XFLM_UNICODE_TEXT && &puzOutBuf[ 1] <= pvEnd) + { + *puzOutBuf = 0; + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = (FLMUINT)((FLMBYTE *)puzOutBuf - + (FLMBYTE *)pvBuffer) + sizeof( FLMUNICODE); + } + } + else + { +Overflow_Error: + + if( uiCharsOutput) + { + uiCharsOutput--; + if( puzOutBuf) + { + *(puzOutBuf - 1) = 0; + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = (FLMUINT)((FLMBYTE *)puzOutBuf - + (FLMBYTE *)pvBuffer); + } + } + else + { + pszOutBuf -= uiLastUTFLen; + *pszOutBuf = 0; + if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = (FLMUINT)(pszOutBuf - + (FLMBYTE *)pvBuffer) + 1; + } + } + } + else if( puiBufferBytesUsed) + { + *puiBufferBytesUsed = 0; + } + + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + +Exit: + + if( puiCharsRead) + { + *puiCharsRead = uiCharsOutput; + } + + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadStorageAsBinary( + IF_IStream * pIStream, + void * pvBuffer, + FLMUINT uiBufLen, + FLMUINT uiByteOffset, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + // Position to the requested offset + + if( uiByteOffset) + { + if( RC_BAD( rc = pIStream->read( + NULL, uiByteOffset, &uiByteOffset))) + { + goto Exit; + } + } + + // Read the requested bytes + + rc = pIStream->read( pucBuffer, uiBufLen, &uiBufLen); + + if( puiBytesRead) + { + *puiBytesRead = uiBufLen; + } + + if( RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadStorageAsNumber( + IF_IStream * pIStream, + FLMUINT uiDataType, + FLMUINT64 * pui64Number, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNeg = FALSE; + FLMUINT64 ui64Num = 0; + + switch( uiDataType) + { + case XFLM_NUMBER_TYPE : + { + FLMBYTE ucNumBuf[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiNumBufLen; + + // Read the entire number into the temporary buffer. + // NOTE: Numbers are not encoded with a length. It + // is expected that the number of bytes remaining + // in the stream will be equal to the exact number of + // bytes representing the number. If this is not the case, + // either a corruption error or an incorrect value will + // be returned. + + uiNumBufLen = sizeof( ucNumBuf); + if( RC_BAD( rc = pIStream->read( + (char *)ucNumBuf, uiNumBufLen, &uiNumBufLen))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + + if( RC_BAD( rc = flmStorageNumberToNumber( + ucNumBuf, uiNumBufLen, &ui64Num, &bNeg))) + { + goto Exit; + } + + break; + } + + case XFLM_TEXT_TYPE : + { + FLMUNICODE uChar; + FLMUINT uiLoop; + FLMBOOL bHex = FALSE; + FLMUINT uiIncrAmount = 0; + + // Skip the character count + + if( RC_BAD( rc = flmReadSEN64( pIStream, NULL, NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + // Empty string + + rc = NE_XFLM_OK; + } + goto Exit; + } + + for( uiLoop = 0;; uiLoop++) + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( + pIStream, &uChar))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + + if( uChar >= FLM_UNICODE_0 && uChar <= FLM_UNICODE_9) + { + uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_0); + } + else if( uChar >= FLM_UNICODE_A && uChar <= FLM_UNICODE_F) + { + if( bHex) + { + uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_A + 10); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( uChar >= FLM_UNICODE_a && uChar <= FLM_UNICODE_f) + { + if( bHex) + { + uiIncrAmount = (FLMUINT)(uChar - FLM_UNICODE_a + 10); + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( uChar == FLM_UNICODE_X || uChar == FLM_UNICODE_x) + { + if( !ui64Num && !bHex) + { + bHex = TRUE; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + else if( uChar == FLM_UNICODE_HYPHEN && !uiLoop) + { + bNeg = TRUE; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + + if( !bHex) + { + if( ui64Num > (~(FLMUINT64)0) / 10) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num *= (FLMUINT64)10; + } + else + { + if( ui64Num > (~(FLMUINT64)0) / 16) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num *= (FLMUINT64)16; + } + + if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)uiIncrAmount) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num += (FLMUINT64)uiIncrAmount; + } + + break; + } + + default : + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + } + + *pui64Number = ui64Num; + *pbNeg = bNeg; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadLine( + IF_IStream * pIStream, + FLMBYTE * pszBuffer, + FLMUINT * puiSize) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiMaxBytes = *puiSize; + FLMUINT uiOffset = 0; + FLMBYTE ucByte; + + *puiSize = 0; + + for( ;;) + { + if( RC_BAD( rc = pIStream->read( (char *)&ucByte, 1, NULL))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + if( ucByte == 0x0A || ucByte == 0x0D) + { + if( uiOffset) + { + break; + } + continue; + } + else + { + if( (uiOffset + 1) == uiMaxBytes) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + + pszBuffer[ uiOffset++] = (char)ucByte; + } + } + + pszBuffer[ uiOffset] = 0; + *puiSize = uiOffset; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_BTreeIStream::open( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT32 ui32BlkAddr, + FLMUINT uiOffsetIndex) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + F_Dict * pDict = pDb->m_pDict; + F_Btree * pBTree = NULL; + + if( RC_BAD( rc = pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) + { + goto Exit; + } + + // Set up the btree object + + if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = open( pDb, pBTree, XFLM_EXACT, uiCollection, ui64NodeId, + ui32BlkAddr, uiOffsetIndex))) + { + goto Exit; + } + + pBTree = NULL; + m_bReleaseBTree = TRUE; + +Exit: + + if( pBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); + } + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_BTreeIStream::open( + F_Db * pDb, + F_Btree * pBTree, + FLMUINT uiFlags, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT32 ui32BlkAddr, + FLMUINT uiOffsetIndex) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( !m_pBTree); + + m_pDb = pDb; + m_uiCollection = uiCollection; + m_pBTree = pBTree; + + // Save the key and key length + + m_uiKeyLength = sizeof( m_ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &m_uiKeyLength, + m_ucKey, FALSE, TRUE))) + { + goto Exit; + } + + m_ui32BlkAddr = ui32BlkAddr; + m_uiOffsetIndex = uiOffsetIndex; + + if( RC_BAD( rc = m_pBTree->btLocateEntry( + m_ucKey, sizeof( m_ucKey), &m_uiKeyLength, uiFlags, + NULL, &m_uiStreamSize, &m_ui32BlkAddr, &m_uiOffsetIndex))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + goto Exit; + } + + if( uiFlags == XFLM_EXACT) + { + m_ui64NodeId = ui64NodeId; + } + else + { + if( RC_BAD( rc = flmCollation2Number( m_uiKeyLength, m_ucKey, + &m_ui64NodeId, NULL, NULL))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_BTreeIStream::positionTo( + FLMUINT64 ui64Position) +{ + RCODE rc = NE_XFLM_OK; + + if( ui64Position >= m_uiBufferStartOffset && + ui64Position <= m_uiBufferStartOffset + m_uiBufferBytes) + { + m_uiBufferOffset = (FLMUINT)(ui64Position - m_uiBufferStartOffset); + } + else + { + if( !m_bDataEncrypted) + { + if( RC_BAD( rc = m_pBTree->btSetReadPosition( m_ucKey, + m_uiKeyLength, (FLMUINT)ui64Position))) + { + goto Exit; + } + + m_uiBufferStartOffset = (FLMUINT)ui64Position; + m_uiBufferOffset = 0; + m_uiBufferBytes = 0; + } + else + { + // When the data is encrypted, we can't just position the btree to a + // new read position. We must read the data chunk by chunk until we + // get to the buffer that holds the specified position. Then we can + // decrypt the buffer and set the correct position. + + m_bBufferDecrypted = FALSE; + + if( ui64Position > m_uiBufferStartOffset + m_uiBufferBytes) + { + while( ui64Position > m_uiBufferStartOffset + m_uiBufferBytes) + { + m_uiBufferStartOffset += m_uiBufferBytes; + } + } + else + { + while( ui64Position < m_uiBufferStartOffset) + { + m_uiBufferStartOffset -= f_min( m_uiBufferStartOffset, + FLM_ENCRYPT_CHUNK_SIZE); + } + } + + flmAssert( ui64Position >= m_uiBufferStartOffset && + ui64Position <= m_uiBufferStartOffset + m_uiBufferBytes); + + // If the new position uis out of range, we will get an error returned. + + if( RC_BAD( rc = m_pBTree->btSetReadPosition( m_ucKey, + m_uiKeyLength, m_uiBufferStartOffset))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBTree->btGetEntry( + m_ucKey, m_uiKeyLength, m_uiKeyLength, + m_pucBuffer, m_uiBufferSize, &m_uiBufferBytes))) + { + if( rc == NE_XFLM_EOF_HIT) + { + if( !m_uiBufferBytes) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); + + if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, + m_pucBuffer, m_uiBufferBytes, m_pucBuffer, m_uiBufferSize))) + { + goto Exit; + } + + // Check to see if we are at the end of the encrypted data. + + if( m_uiBufferStartOffset + m_uiBufferBytes >= m_uiDataLength) + { + // Trim back to the valid decrypted data. + + m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - + extraEncBytes( m_uiDataLength)); + } + + m_bBufferDecrypted = TRUE; + m_uiBufferOffset = (FLMUINT)(ui64Position - m_uiBufferStartOffset); + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_BTreeIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBufBytesAvail; + FLMUINT uiTmp; + FLMUINT uiOffset = 0; + + flmAssert( m_pBTree); + + if( m_bDataEncrypted && !m_bBufferDecrypted) + { + if( (m_uiBufferBytes - m_uiBufferOffset) > 0) + { + // Since we are now looking at encrypted data that was not + // decrypted, we will need to move the data to the front of the + // buffer, then read in enough data to fill the buffer (if there + // is any there) before we can decrypt it. + + f_memmove( &m_ucBuffer[0], &m_ucBuffer[ m_uiBufferOffset], + (m_uiBufferBytes - m_uiBufferOffset)); + m_uiBufferBytes -= m_uiBufferOffset; + m_uiBufferOffset = 0; + + // Fill the remainder of the buffer + + if( RC_BAD( rc = m_pBTree->btGetEntry( + m_ucKey, m_uiKeyLength, m_uiKeyLength, + &m_ucBuffer[ m_uiBufferBytes], m_uiBufferSize - m_uiBufferBytes, + &uiTmp))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + m_uiBufferBytes += uiTmp; + + flmAssert( extraEncBytes( m_uiBufferBytes) == 0); + flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); + + // Now decrypt what we have. + + uiTmp = m_uiBufferBytes; + if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, + m_ucBuffer, m_uiBufferBytes, m_ucBuffer, m_uiBufferSize))) + { + goto Exit; + } + + // Check for the end of the data. + + if( m_uiBufferStartOffset + m_uiBufferBytes > m_uiDataLength) + { + // Trim back the buffer to valid data only. + m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - + extraEncBytes( m_uiDataLength)); + } + + m_bBufferDecrypted = TRUE; + } + } + + while( uiBytesToRead) + { + if( (uiBufBytesAvail = m_uiBufferBytes - m_uiBufferOffset) != 0) + { + uiTmp = f_min( uiBufBytesAvail, uiBytesToRead); + if( pucBuffer) + { + f_memcpy( &pucBuffer[ uiOffset], + &m_pucBuffer[ m_uiBufferOffset], uiTmp); + } + m_uiBufferOffset += uiTmp; + uiOffset += uiTmp; + if( (uiBytesToRead -= uiTmp) == 0) + { + break; + } + } + else + { + m_uiBufferStartOffset += m_uiBufferBytes; + m_uiBufferOffset = 0; + + if( RC_BAD( rc = m_pBTree->btGetEntry( + m_ucKey, m_uiKeyLength, m_uiKeyLength, + m_pucBuffer, m_uiBufferSize, &m_uiBufferBytes))) + { + if( rc == NE_XFLM_EOF_HIT) + { + if( !m_uiBufferBytes) + { + goto Exit; + } + rc = NE_XFLM_OK; + continue; + } + goto Exit; + } + + // Check for encryption. + + if( m_bDataEncrypted) + { + flmAssert( m_uiBufferBytes <= FLM_ENCRYPT_CHUNK_SIZE); + + if( RC_BAD( rc = m_pDb->decryptData( m_uiEncDefId, m_ucIV, + m_pucBuffer, m_uiBufferBytes, m_pucBuffer, m_uiBufferSize))) + { + goto Exit; + } + + // Check to see if we are at the end of the encrypted data. + + if( m_uiBufferStartOffset + m_uiBufferBytes > m_uiDataLength) + { + // Trim back to the valid decrypted data. + + m_uiBufferBytes -= (ENCRYPT_MIN_CHUNK_SIZE - + extraEncBytes( m_uiDataLength)); + } + + m_bBufferDecrypted = TRUE; + } + } + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiOffset; + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::decryptData( + FLMUINT uiEncDefId, + FLMBYTE * pucIV, + void * pvInBuf, + FLMUINT uiInLen, + void * pvOutBuf, + FLMUINT uiOutBufSize) +{ + RCODE rc = NE_XFLM_OK; + F_Dict * pDict; + F_ENCDEF * pEncDef = NULL; + FLMBYTE * pucInBuf = (FLMBYTE *)pvInBuf; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvOutBuf; + FLMUINT uiEncLen; + FLMUINT uiOutLen; + + if( m_pDatabase->m_bInLimitedMode) + { + rc = RC_SET( m_pDatabase->m_rcLimitedCode); + goto Exit; + } + + flmAssert( extraEncBytes( uiInLen) == 0); + flmAssert( uiEncDefId); + + // Need the dictionary and encryption definition. + + if( RC_BAD( rc = getDictionary( &pDict))) + { + goto Exit; + } + + if( RC_BAD( rc = pDict->getEncDef( uiEncDefId, &pEncDef))) + { + goto Exit; + } + + flmAssert( pEncDef); + flmAssert( pEncDef->pCcs); + flmAssert( pEncDef->uiEncKeySize); + + while( uiInLen) + { + uiEncLen = f_min( uiInLen, FLM_ENCRYPT_CHUNK_SIZE); + uiOutLen = uiOutBufSize; + + if( RC_BAD( rc = pEncDef->pCcs->decryptFromStore( + pucInBuf, uiEncLen, pucOutBuf, &uiOutLen, pucIV))) + { + goto Exit; + } + + flmAssert( uiOutLen == uiEncLen); + + pucInBuf += uiEncLen; + uiInLen -= uiEncLen; + + pucOutBuf += uiEncLen; + uiOutBufSize -= uiEncLen; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +F_NodePool::~F_NodePool() +{ + F_BTreeIStream * pTmpBTreeIStream; + + while( (pTmpBTreeIStream = m_pFirstBTreeIStream) != NULL) + { + m_pFirstBTreeIStream = m_pFirstBTreeIStream->m_pNextInPool; + pTmpBTreeIStream->m_ui32RefCnt = 0; + pTmpBTreeIStream->m_pNextInPool = NULL; + delete pTmpBTreeIStream; + } + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_NodePool::setup( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void F_NodeCacheMgr::insertDOMNode( + F_DOMNode * pNode) +{ + + flmAssert( pNode->m_ui32RefCnt == 1); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pNode->resetDOMNode( TRUE); + pNode->m_pNextInPool = m_pFirstNode; + m_pFirstNode = pNode; + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_NodePool::allocBTreeIStream( + F_BTreeIStream ** ppBTreeIStream) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pFirstBTreeIStream) + { + f_mutexLock( m_hMutex); + if( !m_pFirstBTreeIStream) + { + f_mutexUnlock( m_hMutex); + } + else + { + f_resetStackInfo( m_pFirstBTreeIStream); + *ppBTreeIStream = m_pFirstBTreeIStream; + m_pFirstBTreeIStream = m_pFirstBTreeIStream->m_pNextInPool; + (*ppBTreeIStream)->m_pNextInPool = NULL; + + f_mutexUnlock( m_hMutex); + goto Exit; + } + } + + if( (*ppBTreeIStream = f_new F_BTreeIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void F_NodePool::insertBTreeIStream( + F_BTreeIStream * pBTreeIStream) +{ + flmAssert( pBTreeIStream->m_ui32RefCnt == 1); + + pBTreeIStream->reset(); + f_mutexLock( m_hMutex); + pBTreeIStream->m_pNextInPool = m_pFirstBTreeIStream; + m_pFirstBTreeIStream = pBTreeIStream; + f_mutexUnlock( m_hMutex); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DOMNode::getNamespaceURI( + FLMBOOL bUnicode, + IF_Db * ifpDb, + void * pvNamespaceURI, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiTag; + FLMUNICODE * puzNamespaceURI; + char * pszNamespaceURI; + FLMUINT uiChars = 0; + F_NameTable * pNameTable = NULL; + FLMUINT uiNameId; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + pNameTable = pDb->m_pDict->getNameTable(); + eNodeType = getNodeType(); + + if( eNodeType == ELEMENT_NODE) + { + uiTag = ELM_ELEMENT_TAG; + uiNameId = getNameId(); + } + else if( eNodeType == ATTRIBUTE_NODE) + { + uiTag = ELM_ATTRIBUTE_TAG; + uiNameId = m_uiAttrNameId; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + uiChars = uiBufSize; + if( bUnicode) + { + puzNamespaceURI = (FLMUNICODE *)pvNamespaceURI; + pszNamespaceURI = NULL; + } + else + { + puzNamespaceURI = NULL; + pszNamespaceURI = (char *)pvNamespaceURI; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( + pDb, uiTag, uiNameId, NULL, NULL, NULL, NULL, + puzNamespaceURI, pszNamespaceURI, &uiChars, FALSE))) + { + goto Exit; + } + +Exit: + + if( puiCharsReturned) + { + *puiCharsReturned = uiChars; + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DOMNode::getLocalName( + FLMBOOL bUnicode, + IF_Db * ifpDb, + void * pvLocalName, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiTag; + F_NameTable * pNameTable = NULL; + FLMUINT uiChars = 0; + FLMUINT uiNameId; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + pNameTable = pDb->m_pDict->getNameTable(); + eNodeType = getNodeType(); + + if( eNodeType == ELEMENT_NODE) + { + uiTag = ELM_ELEMENT_TAG; + uiNameId = getNameId(); + } + else if( eNodeType == ATTRIBUTE_NODE) + { + uiTag = ELM_ATTRIBUTE_TAG; + uiNameId = m_uiAttrNameId; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + uiChars = uiBufSize; + + if( bUnicode) + { + if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, + uiTag, uiNameId, (FLMUNICODE *)pvLocalName, NULL, &uiChars))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, + uiTag, uiNameId, NULL, (char *)pvLocalName, &uiChars))) + { + goto Exit; + } + } + +Exit: + + if( puiCharsReturned) + { + *puiCharsReturned = uiChars; + } + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Return the prefix name, either as Unicode or as Native. +*****************************************************************************/ +RCODE F_DOMNode::getPrefix( + FLMBOOL bUnicode, + IF_Db * ifpDb, + void * pvPrefix, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiPrefix; + FLMUNICODE * puzPrefix = (FLMUNICODE *)pvPrefix; + char * pszPrefix = (char *)pvPrefix; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + eNodeType = getNodeType(); + + if( eNodeType == ELEMENT_NODE) + { + uiPrefix = getPrefixId(); + } + else if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getPrefixId( + m_uiAttrNameId, &uiPrefix))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( uiPrefix) + { + if( bUnicode) + { + if( RC_BAD( rc = pDb->m_pDict->getPrefix( + uiPrefix, puzPrefix, uiBufSize, puiCharsReturned))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->m_pDict->getPrefix( + uiPrefix, pszPrefix, uiBufSize, puiCharsReturned))) + { + goto Exit; + } + } + } + else + { + if( uiBufSize) + { + if( puzPrefix) + { + *puzPrefix = 0; + } + } + + if( puiCharsReturned) + { + *puiCharsReturned = 0; + } + + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getQualifiedName( + IF_Db * ifpDb, + FLMUNICODE * puzQualifiedName, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCharsReturned; + FLMUINT uiTmp; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = getPrefix( ifpDb, puzQualifiedName, + uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if( uiCharsReturned) + { + if( puzQualifiedName) + { + uiBufSize -= (sizeof( FLMUNICODE) * uiCharsReturned); + if( uiBufSize < sizeof( FLMUNICODE) * 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + puzQualifiedName[ uiCharsReturned] = FLM_UNICODE_COLON; + uiCharsReturned++; + puzQualifiedName += uiCharsReturned; + uiBufSize -= sizeof( FLMUNICODE); + } + else + { + uiCharsReturned++; + } + } + + if( RC_BAD( rc = getLocalName( ifpDb, puzQualifiedName, uiBufSize, &uiTmp))) + { + goto Exit; + } + + uiCharsReturned += uiTmp; + + if( puiCharsReturned) + { + *puiCharsReturned = uiCharsReturned; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::getQualifiedName( + IF_Db * ifpDb, + char * pszQualifiedName, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCharsReturned; + FLMUINT uiTmp; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = getPrefix( ifpDb, + pszQualifiedName, uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if( uiCharsReturned) + { + if( pszQualifiedName) + { + uiBufSize -= uiCharsReturned; + if( uiBufSize < 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pszQualifiedName[ uiCharsReturned] = ASCII_COLON; + uiCharsReturned++; + pszQualifiedName += uiCharsReturned; + uiBufSize--; + } + else + { + uiCharsReturned++; + } + } + + if( RC_BAD( rc = getLocalName( ifpDb, + pszQualifiedName, uiBufSize, &uiTmp))) + { + goto Exit; + } + + uiCharsReturned += uiTmp; + + if( puiCharsReturned) + { + *puiCharsReturned = uiCharsReturned; + } + +Exit: + + if( bStartedTrans) + { + pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Method to set the prefix on a DOM node. Either a unicode or native + buffer may be used. ** private ** +*****************************************************************************/ +RCODE F_DOMNode::setPrefix( + FLMBOOL bUnicode, + IF_Db * ifpDb, + void * pvPrefix) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPrefixId; + F_Db * pDb = (F_Db *)ifpDb; + FLMBOOL bStartedTrans = FALSE; + char * pszPrefix = (char *)pvPrefix; + FLMUNICODE * puzPrefix = (FLMUNICODE *)pvPrefix; + eDomNodeType eNodeType; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // If the node is read-only, don't allow it to be changed + + if( getModeFlags() & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + + eNodeType = getNodeType(); + + // Make sure this is an element or attribute node + + if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + uiPrefixId = 0; + if( puzPrefix && bUnicode) + { + // Find the prefix ID + + if( RC_BAD( rc = pDb->m_pDict->getPrefixId( pDb, + puzPrefix, &uiPrefixId))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PREFIX); + } + goto Exit; + } + } + else if( pszPrefix) + { + // Find the prefix ID + + if( RC_BAD( rc = pDb->m_pDict->getPrefixId( pDb, + pszPrefix, &uiPrefixId))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PREFIX); + } + goto Exit; + } + } + + // Set the prefix + + if( RC_BAD( rc = setPrefixId( ifpDb, uiPrefixId))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DOMNode::setPrefixId( + IF_Db * ifpDb, + FLMUINT uiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = (F_Db *)ifpDb; + F_Rfl * pRfl = pDb->m_pDatabase->m_pRfl; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + eDomNodeType eNodeType; + FLMUINT uiTmp; + + if( RC_BAD( rc = pDb->checkTransaction( + XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Make sure the node is current + + if( RC_BAD( rc = syncFromDb( pDb))) + { + goto Exit; + } + + // If the node is read-only, don't allow it to be changed + + if( getModeFlags() & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + + eNodeType = getNodeType(); + + // If the prefix isn't changing, don't do anything + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->getPrefixId( + m_uiAttrNameId, &uiTmp))) + { + goto Exit; + } + + if( uiPrefixId == uiTmp) + { + goto Exit; + } + } + else if( eNodeType == ELEMENT_NODE) + { + if( uiPrefixId == getPrefixId()) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Verify that the prefix is valid + + if( RC_BAD( rc = pDb->m_pDict->getPrefix( + uiPrefixId, (char *)NULL, 0, NULL))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Set the new prefix + + if( RC_BAD( rc = makeWriteCopy( pDb))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = m_pCachedNode->setPrefixId( pDb, + m_uiAttrNameId, uiPrefixId))) + { + goto Exit; + } + } + else + { + setPrefixId( uiPrefixId); + } + + // Update the node + + if( RC_BAD( rc = pDb->updateNode( m_pCachedNode, 0))) + { + goto Exit; + } + + // Log the update + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeSetPrefixId( pDb, + getCollection(), getIxNodeId(), + m_uiAttrNameId, uiPrefixId))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc) && bMustAbortOnError) + { + if( bMustAbortOnError) + { + pDb->setMustAbortTrans( rc); + } + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + pDb->transAbort(); + } + else + { + rc = pDb->transCommit(); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Method to compare two dom nodes. +*****************************************************************************/ +FLMUINT XFLMAPI F_DOMNode::compareNode( + IF_DOMNode * pNode, + IF_Db * pDb1, + IF_Db * pDb2, + char * pszErrBuff, + FLMUINT uiErrBuffLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiEqual = 0; + F_DOMNode * pLeftNode = (F_DOMNode *)this; + F_DOMNode * pRightNode = (F_DOMNode *)pNode; + char szBuffer[ 100]; + FLMUNICODE * puzVal1 = NULL; + FLMUNICODE * puzVal2 = NULL; + char * pucBinary1 = NULL; + char * pucBinary2 = NULL; + FLMUINT uiBytesReturned1; + FLMUINT uiBytesReturned2; + FLMUINT uiDataLen1; + FLMUINT uiDataLen2; + FLMUINT uiTmp1; + FLMUINT uiTmp2; + FLMUINT64 ui64Tmp1; + FLMUINT64 ui64Tmp2; + +#define NODE_NOT_EQUAL 1 + + szBuffer[0] = '\0'; + + if( pLeftNode->getNodeType() != pRightNode->getNodeType()) + { + f_sprintf( szBuffer, "Node Type mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getDataType( pDb1, &uiTmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getDataType( pDb2, &uiTmp2))) + { + goto Exit; + } + + if( uiTmp1 != uiTmp2) + { + f_sprintf( szBuffer, "Data Type mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( pLeftNode->getCollection() != pRightNode->getCollection()) + { + f_sprintf( szBuffer, "Collection mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getPrefixId( pDb1, &uiTmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getPrefixId( pDb2, &uiTmp2))) + { + goto Exit; + } + + if( uiTmp1 != uiTmp2) + { + f_sprintf( szBuffer, "Prefix mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getNameId( pDb1, &uiTmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getNameId( pDb2, &uiTmp2))) + { + goto Exit; + } + + if( uiTmp1 != uiTmp2) + { + f_sprintf( szBuffer, "Name Id mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getEncDefId( pDb1, &uiTmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getEncDefId( pDb2, &uiTmp2))) + { + goto Exit; + } + + if( uiTmp1 != uiTmp2) + { + f_sprintf( szBuffer, "Encryption Id mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( (pLeftNode->getModeFlags() & FDOM_PERSISTENT_FLAGS) != + (pRightNode->getModeFlags() & FDOM_PERSISTENT_FLAGS)) + { + f_sprintf( szBuffer, "Flags mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getNodeId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getNodeId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Node Id mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getDocumentId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getDocumentId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Root Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getParentId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getParentId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Parent Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getFirstChildId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getFirstChildId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "First Child Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getLastChildId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getLastChildId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Last Child Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getPrevSibId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getPrevSibId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Previous Sibling Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getNextSibId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getNextSibId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Next Sibling Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getAnnotationId( pDb1, &ui64Tmp1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getAnnotationId( pDb2, &ui64Tmp2))) + { + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Annotation Node mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getDataLength( pDb1, &uiDataLen1))) + { + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getDataLength( pDb2, &uiDataLen2))) + { + goto Exit; + } + + if( uiDataLen1 != uiDataLen2) + { + f_sprintf( szBuffer, "Data Length mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( uiDataLen1) + { + switch( pLeftNode->getDataType()) + { + case XFLM_NODATA_TYPE: + { + goto Exit; + } + + case XFLM_TEXT_TYPE: + { + + if( RC_BAD( rc = pLeftNode->getUnicode( pDb1, &puzVal1))) + { + f_sprintf( szBuffer, "getUnicode failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getUnicode( pDb2, &puzVal2))) + { + f_sprintf( szBuffer, "getUnicode failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( f_unicmp( puzVal1, puzVal2) != 0) + { + f_sprintf( szBuffer, "Data Value mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + break; + } + + case XFLM_NUMBER_TYPE: + { + if( RC_BAD( rc = pLeftNode->getUINT64( pDb1, &ui64Tmp1))) + { + f_sprintf( szBuffer, "getUINT64 failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getUINT64( pDb2, &ui64Tmp2))) + { + f_sprintf( szBuffer, "getUINT64 failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( ui64Tmp1 != ui64Tmp2) + { + f_sprintf( szBuffer, "Data Value mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + break; + } + + case XFLM_BINARY_TYPE: + { + if( RC_BAD( rc = f_alloc( uiDataLen1 + 1, &pucBinary1))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( uiDataLen2 + 1, &pucBinary2))) + { + goto Exit; + } + + if( RC_BAD( rc = pLeftNode->getBinary( pDb1, pucBinary1, + 0, uiDataLen1, &uiBytesReturned1))) + { + f_sprintf( szBuffer, "getBinary failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( RC_BAD( rc = pRightNode->getBinary( + pDb2, pucBinary2, 0, uiDataLen2, &uiBytesReturned2))) + { + f_sprintf( szBuffer, "getBinary failed with rc==(%u)%s.", + rc, F_DbSystem::_errorString( rc)); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( uiBytesReturned1 != uiBytesReturned2) + { + f_sprintf( szBuffer, "Return data length mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + if( f_memcmp( pucBinary1, pucBinary2, uiBytesReturned1) != 0) + { + f_strcpy( szBuffer, "Data Value mismatch"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + + break; + } + + default: + { + f_strcpy( szBuffer, "Invalid Data Type"); + uiEqual = NODE_NOT_EQUAL; + goto Exit; + } + } + } + +Exit: + + f_memcpy( pszErrBuff, szBuffer, f_min( uiErrBuffLen, f_strlen( szBuffer))); + pszErrBuff[ f_min( uiErrBuffLen, f_strlen( szBuffer))] = '\0'; + + if( puzVal1) + { + f_free( &puzVal1); + } + + if( puzVal2) + { + f_free( &puzVal2); + } + + if( pucBinary1) + { + f_free( &pucBinary1); + } + + if( pucBinary2) + { + f_free( &pucBinary2); + } + + return( uiEqual); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Db::getDictionaryDef( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + IF_DOMNode ** ppDocumentNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_DataVector searchKey; + F_DataVector foundKey; + + searchKey.reset(); + foundKey.reset(); + + if( RC_BAD( rc = checkTransaction( + XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = searchKey.setUINT( 0, uiDictType))) + { + goto Exit; + } + + if( RC_BAD( rc = searchKey.setUINT( 1, uiDictNumber))) + { + goto Exit; + } + + if( RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, + foundKey.getDocumentID(), ppDocumentNode))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getElementNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT * puiElementNameId) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_ELEMENT_TAG, puzElementName, NULL, + TRUE, puzNamespaceURI, puiElementNameId))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getElementNameId( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT * puiElementNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzNamespaceURI = NULL; + FLMUNICODE * puzTmp; + const char * pszTmp; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( pszNamespaceURI && *pszNamespaceURI) + { + if( RC_BAD( rc = f_alloc( (f_strlen( pszNamespaceURI) + 1) * + sizeof( FLMUNICODE), &puzNamespaceURI))) + { + goto Exit; + } + + pszTmp = pszNamespaceURI; + puzTmp = puzNamespaceURI; + + while (*pszTmp) + { + *puzTmp = (FLMUNICODE)(*pszTmp); + pszTmp++; + puzTmp++; + } + + *puzTmp = 0; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_ELEMENT_TAG, NULL, pszElementName, + TRUE, puzNamespaceURI, puiElementNameId))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + if( puzNamespaceURI) + { + f_free( &puzNamespaceURI); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getAttributeNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzAttributeName, + FLMUINT * puiAttributeNameId) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_ATTRIBUTE_TAG, puzAttributeName, NULL, + TRUE, puzNamespaceURI, puiAttributeNameId))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getAttributeNameId( + const char * pszNamespaceURI, + const char * pszAttributeName, + FLMUINT * puiAttributeNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzNamespaceURI = NULL; + FLMUNICODE * puzTmp; + const char * pszTmp; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( pszNamespaceURI && *pszNamespaceURI) + { + if( RC_BAD( rc = f_alloc( (f_strlen( pszNamespaceURI) + 1) * + sizeof( FLMUNICODE), &puzNamespaceURI))) + { + goto Exit; + } + + pszTmp = pszNamespaceURI; + puzTmp = puzNamespaceURI; + + while (*pszTmp) + { + *puzTmp = (FLMUNICODE)(*pszTmp); + pszTmp++; + puzTmp++; + } + + *puzTmp = 0; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_ATTRIBUTE_TAG, NULL, pszAttributeName, + TRUE, puzNamespaceURI, puiAttributeNameId))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + if( puzNamespaceURI) + { + f_free( &puzNamespaceURI); + } + + return( rc); +} + +/***************************************************************************** +Desc: Get a collection number from collection name. +******************************************************************************/ +RCODE XFLMAPI F_Db::getCollectionNumber( + const char * pszCollectionName, + FLMUINT * puiCollectionNumber) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_COLLECTION_TAG, NULL, pszCollectionName, + FALSE, NULL, puiCollectionNumber))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Get a collection number from collection name. +******************************************************************************/ +RCODE XFLMAPI F_Db::getCollectionNumber( + const FLMUNICODE * puzCollectionName, + FLMUINT * puiCollectionNumber) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_COLLECTION_TAG, puzCollectionName, NULL, + FALSE, NULL, puiCollectionNumber))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Get an index number from index name. +******************************************************************************/ +RCODE XFLMAPI F_Db::getIndexNumber( + const char * pszIndexName, + FLMUINT * puiIndexNumber) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_INDEX_TAG, NULL, pszIndexName, + FALSE, NULL, puiIndexNumber))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Get an index number from index name. +******************************************************************************/ +RCODE XFLMAPI F_Db::getIndexNumber( + const FLMUNICODE * puzIndexName, + FLMUINT * puiIndexNumber) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + this, ELM_INDEX_TAG, puzIndexName, NULL, + FALSE, NULL, puiIndexNumber))) + { + goto Exit; + } + +Exit: + + if( pNameTable) + { + pNameTable->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocument, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + + if( uiCollection == XFLM_MAINT_COLLECTION) + { + // Users are not allowed to create documents in the + // maintenance collection + + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = createRootNode( uiCollection, 0, + DOCUMENT_NODE, (F_DOMNode **)ppDocument, pui64NodeId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createRootElement( + FLMUINT uiCollection, + FLMUINT uiNameId, + IF_DOMNode ** ppElement, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + + if( uiCollection == XFLM_MAINT_COLLECTION) + { + // Users are not allowed to create documents in the + // maintenance collection + + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = createRootNode( uiCollection, uiNameId, + ELEMENT_NODE, (F_DOMNode **)ppElement, pui64NodeId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::checkAndUpdateState( + eDomNodeType eNodeType, + FLMUINT uiNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bUserDefined = FALSE; + FLMUINT uiDictType = 0; + F_AttrElmInfo defInfo; + + // The F_AttrElmInfo constructor should have set the state + // to active. + + flmAssert( defInfo.m_uiState == ATTR_ELM_STATE_ACTIVE); + + if( eNodeType == ATTRIBUTE_NODE) + { + uiDictType = ELM_ATTRIBUTE_TAG; + if( RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) + { + goto Exit; + } + + bUserDefined = attributeIsUserDefined( uiNameId); + } + else if( eNodeType == ELEMENT_NODE) + { + uiDictType = ELM_ELEMENT_TAG; + if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) + { + goto Exit; + } + + bUserDefined = elementIsUserDefined( uiNameId); + } + else if( eNodeType == DATA_NODE) + { + if( uiNameId) + { + uiDictType = ELM_ELEMENT_TAG; + if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) + { + goto Exit; + } + + bUserDefined = elementIsUserDefined( uiNameId); + } + } + else if( eNodeType != COMMENT_NODE && + eNodeType != DOCUMENT_NODE && + eNodeType != ANNOTATION_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + if( bUserDefined && defInfo.m_uiState != ATTR_ELM_STATE_ACTIVE) + { + if( defInfo.m_uiState == ATTR_ELM_STATE_PURGE ) + { + // Marked as 'purged'. So, user is not allowed to add + // new instances of this field. + + rc = (RCODE)(eNodeType == ELEMENT_NODE + ? RC_SET( NE_XFLM_ELEMENT_PURGED) + : RC_SET( NE_XFLM_ATTRIBUTE_PURGED)); + goto Exit; + } + else if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) + { + // Because an occurance is being added, update the + // state to be 'active' + + if( RC_BAD( rc = changeItemState( uiDictType, uiNameId, + XFLM_ACTIVE_OPTION_STR))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::_updateNode( + F_CachedNode * pCachedNode, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection = pCachedNode->getCollection(); + F_COLLECTION * pCollection; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bAdd = (uiFlags & FLM_UPD_ADD) ? TRUE : FALSE; + F_AttrElmInfo defInfo; + + // Logging should be done at a higher level + + flmAssert( !m_pDatabase->m_pRfl->isLoggingEnabled()); + flmAssert( uiCollection); + + // Mark the node as being dirty + + pCachedNode->setNodeDirty( this, bAdd); + + if( bAdd) + { + // Get a pointer to the collection + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + // If the nodeId is greater than or equal to the next nodeId, we + // will set the next nodeId to 1 greater than the new nodeId to avoid + // running into the same nodeId later. + // + // Node ID should already be set at this point. + + flmAssert( pCachedNode->getNodeId()); + + if( pCachedNode->getNodeId() >= pCollection->ui64NextNodeId) + { + pCollection->ui64NextNodeId = pCachedNode->getNodeId() + 1; + pCollection->bNeedToUpdateNodes = TRUE; + } + + bMustAbortOnError = TRUE; + } + else if( !pCachedNode->getNodeId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + + // Add the document to the list of documents that need to have + // documentDone called at commit time. + + if( !(uiFlags & FLM_UPD_INTERNAL_CHANGE) && + uiCollection == XFLM_DICT_COLLECTION) + { + if( RC_BAD( rc = m_pDatabase->m_DocumentList.addNode( + pCachedNode->getCollection(), pCachedNode->getDocumentId(), 0))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc) && bMustAbortOnError) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::getCachedBTree( + FLMUINT uiCollection, + F_Btree ** ppBTree) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( m_pCachedBTree) + { + flmAssert( m_pCachedBTree->getRefCount() == 1); + m_pCachedBTree->btClose(); + } + else + { + // Reserve a B-Tree from the pool + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( + &m_pCachedBTree))) + { + goto Exit; + } + } + + // Set up the btree object + + if( RC_BAD( rc = m_pCachedBTree->btOpen( this, + &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + + m_pCachedBTree->AddRef(); + *ppBTree = m_pCachedBTree; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::flushNode( + F_Btree * pBTree, + F_CachedNode * pCachedNode) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + FLMUINT64 ui64NodeId = pCachedNode->getNodeId(); + FLMBYTE ucKeyBuf[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT uiHeaderStorageSize; + F_DynaBuf dynaBuf( m_pDatabase->m_pucUpdBuffer, m_pDatabase->m_uiUpdBufferSize); + FLMBOOL bMustAbortOnError = FALSE; + IF_PosIStream * pIStream = NULL; + FLMBYTE * pucSrc = NULL; + FLMBYTE * pucTmp; + FLMBOOL bOutputNodeData; + FLMBOOL bTruncateOnReplace; + FLMUINT32 ui32BlkAddr; + FLMUINT uiOffsetIndex; + FLMBYTE * pucIV = NULL; + FLMUINT uiIVLen = 0; + FLMUINT uiEncDefId = pCachedNode->getEncDefId(); + eDomNodeType eNodeType = pCachedNode->getNodeType(); + FLMUINT uiNodeDataLength; + + // Node should be dirty + + flmAssert( pCachedNode->nodeIsDirty()); + + // Transaction IDs should match + + if( pCachedNode->getLowTransId() != getTransID()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + uiNodeDataLength = pCachedNode->m_nodeInfo.uiDataLength; + + // Output the header + + if( (pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER) == 0) + { + if( RC_BAD( rc = dynaBuf.allocSpace( MAX_DOM_HEADER_SIZE, + (void **)&pucTmp))) + { + goto Exit; + } + + if( RC_BAD( rc = pCachedNode->headerToBuf( FALSE, + pucTmp, &uiHeaderStorageSize, NULL, NULL))) + { + goto Exit; + } + + dynaBuf.truncateData( uiHeaderStorageSize); + } + else + { + if( RC_BAD( rc = dynaBuf.allocSpace( FIXED_DOM_HEADER_SIZE, + (void **)&pucTmp))) + { + goto Exit; + } + + if( RC_BAD( rc = pCachedNode->headerToBuf( TRUE, pucTmp, + &uiHeaderStorageSize, NULL, NULL))) + { + goto Exit; + } + } + + // Get a pointer to the collection + + if( RC_BAD( rc = m_pDict->getCollection( pCachedNode->getCollection(), + &pCollection))) + { + goto Exit; + } + + uiKeyLen = sizeof( ucKeyBuf); + if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, + &uiKeyLen, ucKeyBuf, FALSE, TRUE))) + { + goto Exit; + } + + if( eNodeType == ELEMENT_NODE) + { + FLMUINT uiLoop; + NODE_ITEM * pNodeItem; + FLMUINT uiNodeCount = pCachedNode->getChildElmCount(); + FLMUINT uiTmpOffset; + FLMUINT uiPrevNameId = 0; + FLMUINT64 ui64ElmNodeId = pCachedNode->getNodeId(); + + // Go through the child element list and output them to the buffer + // Note that the child element node count has already been output + // as part of the node header. + + pNodeItem = pCachedNode->m_pNodeList; + for( uiLoop = 0; uiLoop < uiNodeCount; pNodeItem++, uiLoop++) + { + uiTmpOffset = dynaBuf.getDataLength(); + + if( RC_BAD( rc = dynaBuf.allocSpace( FLM_MAX_SEN_LEN * 2, + (void **)&pucTmp))) + { + goto Exit; + } + + flmAssert( pNodeItem->uiNameId > uiPrevNameId); + flmAssert( pNodeItem->ui64NodeId > ui64ElmNodeId); + + uiTmpOffset += flmEncodeSEN( pNodeItem->uiNameId - uiPrevNameId, + &pucTmp); + + uiTmpOffset += flmEncodeSEN( pNodeItem->ui64NodeId - ui64ElmNodeId, + &pucTmp); + + uiPrevNameId = pNodeItem->uiNameId; + dynaBuf.truncateData( uiTmpOffset); + } + + // Export any attributes on the element + + if( pCachedNode->m_uiAttrCount) + { + if( RC_BAD( rc = pCachedNode->exportAttributeList( this, + &dynaBuf, NULL))) + { + goto Exit; + } + } + } + + // Set up to output data + + if( uiNodeDataLength) + { + if( pCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) + { + if( pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER) + { + bOutputNodeData = FALSE; + bTruncateOnReplace = FALSE; + } + else + { + // If the value is on disk and we don't have a fixed-size header, + // we'll have to read and output the entire node value. + + flmAssert( eNodeType != ELEMENT_NODE); + + if( RC_BAD( rc = pCachedNode->getIStream( this, NULL, &pIStream))) + { + goto Exit; + } + + bOutputNodeData = TRUE; + bTruncateOnReplace = TRUE; + } + } + else + { + pucSrc = pCachedNode->getDataPtr(); + bOutputNodeData = TRUE; + bTruncateOnReplace = TRUE; + } + } + else + { + bOutputNodeData = FALSE; + bTruncateOnReplace = TRUE; + } + + if( bOutputNodeData) + { + FLMUINT uiDataOutputSize = uiNodeDataLength; + + if( uiEncDefId) + { + F_ENCDEF * pEncDef; + + if( RC_BAD( rc = m_pDict->getEncDef( uiEncDefId, &pEncDef))) + { + goto Exit; + } + + uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( uiIVLen == 8 || uiIVLen == 16); + + if( RC_BAD( rc = dynaBuf.allocSpace( uiIVLen, (void **)&pucIV))) + { + goto Exit; + } + + if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, pucIV))) + { + goto Exit; + } + + uiDataOutputSize = getEncLen( uiNodeDataLength); + } + + if( RC_BAD( rc = dynaBuf.allocSpace( uiDataOutputSize, + (void **)&pucTmp))) + { + goto Exit; + } + + if( pIStream) + { + if( RC_BAD( rc = pIStream->read( pucTmp, uiNodeDataLength, NULL))) + { + goto Exit; + } + } + else + { + f_memcpy( pucTmp, pucSrc, uiNodeDataLength); + } + + if( uiEncDefId) + { + if( RC_BAD( rc = encryptData( uiEncDefId, pucIV, + pucTmp, uiDataOutputSize, uiNodeDataLength, &uiDataOutputSize))) + { + goto Exit; + } + } + } + + ui32BlkAddr = pCachedNode->getBlkAddr(); + uiOffsetIndex = pCachedNode->getOffsetIndex(); + + if( pCachedNode->nodeIsNew()) + { + // If this is a new node, the value will not be on disk. + // This routine is only called for values that we are + // keeping in memory. If we stream a value out through + // multiple buffers, it is written right away and the + // node's dirty and new flags will be unset as soon as + // the writing is done. + + if( pCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) + { + // This shouldn't happen + + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = pBTree->btInsertEntry( + ucKeyBuf, sizeof( ucKeyBuf), uiKeyLen, + dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), + TRUE, TRUE, &ui32BlkAddr, &uiOffsetIndex))) + { + if( rc == NE_XFLM_NOT_UNIQUE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS); + } + + goto Exit; + } + } + else + { + // Replace the node on disk. + + if( RC_BAD( rc = pBTree->btReplaceEntry( + ucKeyBuf, sizeof( ucKeyBuf), uiKeyLen, + dynaBuf.getBufferPtr(), dynaBuf.getDataLength(), + TRUE, TRUE, bTruncateOnReplace, &ui32BlkAddr, + &uiOffsetIndex))) + { + goto Exit; + } + } + + pCachedNode->setBlkAddr( ui32BlkAddr); + pCachedNode->setOffsetIndex( uiOffsetIndex); + + // Clear the dirty flag and the new flag. + + pCachedNode->unsetNodeDirtyAndNew( this); + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::encryptData( + FLMUINT uiEncDefId, + FLMBYTE * pucIV, + FLMBYTE * pucBuffer, + FLMUINT uiBufferSize, + FLMUINT uiDataLen, + FLMUINT * puiOutputLength) +{ + RCODE rc = NE_XFLM_OK; + F_Dict * pDict; + F_ENCDEF * pEncDef; + FLMUINT uiEncLen; + FLMUINT uiTmpLen; + FLMUINT uiEncBuffLen; + FLMUINT uiDataToEncrypt = uiDataLen; + FLMUINT uiChunkLen; + FLMBYTE * pucEncTmp; + FLMBYTE ucEncryptBuffer[ FLM_ENCRYPT_CHUNK_SIZE]; + + if( m_pDatabase->m_bInLimitedMode) + { + *puiOutputLength = uiDataLen; + rc = RC_SET( m_pDatabase->m_rcLimitedCode); + goto Exit; + } + + // Need to retrieve the encryption key. + + if( RC_BAD( rc = getDictionary( &pDict))) + { + goto Exit; + } + + if( RC_BAD( rc = pDict->getEncDef( uiEncDefId, &pEncDef))) + { + goto Exit; + } + + flmAssert( pEncDef); + flmAssert( pEncDef->pCcs); + + // Check first to make sure we wil be able to encrypt the entire buffer + // since we must return the encrypted data in the source buffer. + + uiEncLen = getEncLen( uiDataLen); + + if( uiEncLen > uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Encrypt the buffer in chunks + + uiEncBuffLen = 0; + pucEncTmp = &ucEncryptBuffer[0]; + + while( uiDataToEncrypt) + { + FLMUINT uiDataEncrypted; + + uiDataEncrypted = uiChunkLen = ((uiDataToEncrypt > FLM_ENCRYPT_CHUNK_SIZE) + ? FLM_ENCRYPT_CHUNK_SIZE + : uiDataToEncrypt); + + if( extraEncBytes( uiChunkLen) != 0) + { + // If we are padding, we *MUST* be on the last piece to encrypt! + + flmAssert( uiChunkLen == uiDataToEncrypt); + uiChunkLen += (ENCRYPT_MIN_CHUNK_SIZE - extraEncBytes( uiChunkLen)); + } + + flmAssert( uiChunkLen <= FLM_ENCRYPT_CHUNK_SIZE); + + uiTmpLen = uiChunkLen; + + if( RC_BAD( rc = pEncDef->pCcs->encryptToStore( + pucBuffer, uiChunkLen, pucEncTmp, &uiTmpLen, pucIV))) + { + goto Exit; + } + + flmAssert( uiTmpLen == uiChunkLen); + f_memcpy( pucBuffer, pucEncTmp, uiChunkLen); + + pucBuffer += uiChunkLen; + uiDataToEncrypt -= uiDataEncrypted; + uiEncBuffLen += uiChunkLen; + + flmAssert( uiEncBuffLen <= uiEncLen); + } + + flmAssert( uiEncBuffLen == uiEncLen); + *puiOutputLength = uiEncLen; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createRootNode( + FLMUINT uiCollection, + FLMUINT uiElementNameId, + eDomNodeType eNodeType, + F_DOMNode ** ppNewNode, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + F_DOMNode * pPrevSib = NULL; + F_DOMNode * pNewNode = NULL; + F_CachedNode * pCachedNode; + F_COLLECTION * pCollection; + FLMBOOL bMustAbortOnError = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( eNodeType != ELEMENT_NODE && eNodeType != DOCUMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // If a specific node Id is requested, check to make sure it is not + // already in use. + + if( pui64NodeId) + { + if( *pui64NodeId && (m_uiFlags & FDB_REBUILDING_DATABASE)) + { + if( RC_OK( rc = getNode( uiCollection, *pui64NodeId, &pNewNode))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + else if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + // Set the value to zero so we don't use it. We will + // return the new nodeId. + + *pui64NodeId = 0; + } + } + + bMustAbortOnError = TRUE; + + // Check the state if this is a root element + + if( eNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = checkAndUpdateState( ELEMENT_NODE, uiElementNameId))) + { + goto Exit; + } + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->createNode( this, + uiCollection, + (FLMUINT64)(pui64NodeId + ? *pui64NodeId + : (FLMUINT64)0), + &pNewNode))) + { + goto Exit; + } + + // Clone the dictionary since the first/last document ID + // values of the collection will be changed + + if( !(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if( RC_BAD( rc = dictClone())) + { + goto Exit; + } + } + + // Get a pointer to the collection + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + pCachedNode = pNewNode->m_pCachedNode; + + if( !pCollection->ui64FirstDocId) + { + pCollection->ui64FirstDocId = pCollection->ui64NextNodeId; + pCollection->ui64LastDocId = pCollection->ui64NextNodeId; + } + else + { + pCachedNode->setPrevSibId( pCollection->ui64LastDocId); + pCollection->ui64LastDocId = pCollection->ui64NextNodeId; + } + pCollection->bNeedToUpdateNodes = TRUE; + + if( eNodeType == ELEMENT_NODE) + { + F_AttrElmInfo elmInfo; + + if( RC_BAD( rc = m_pDict->getElement( this, uiElementNameId, &elmInfo))) + { + goto Exit; + } + + pCachedNode->setNameId( uiElementNameId); + pCachedNode->setDataType( elmInfo.m_uiDataType); + + // Is this a node whose child elements must all be unique? + + if( elmInfo.m_uiFlags & ATTR_ELM_UNIQUE_SUBELMS) + { + flmAssert( elmInfo.m_uiDataType == XFLM_NODATA_TYPE); + pCachedNode->setFlags( FDOM_HAVE_CELM_LIST); + } + } + else + { + pCachedNode->setDataType( XFLM_NODATA_TYPE); + } + + pCachedNode->setNodeType( eNodeType); + pCachedNode->setDocumentId( pCachedNode->getNodeId()); + + // Link the document into the document list + + bMustAbortOnError = TRUE; + + if( pui64NodeId && *pui64NodeId) + { + pCollection->ui64LastDocId = *pui64NodeId; + pCollection->bNeedToUpdateNodes = TRUE; + } + + // Output the node + + if( RC_BAD( rc = updateNode( pCachedNode, FLM_UPD_ADD))) + { + goto Exit; + } + + // Retrieve the previous sibling and set its next sibling + // value + + if( pCachedNode->getPrevSibId()) + { + if( RC_BAD( rc = getNode( uiCollection, + pCachedNode->getPrevSibId(), &pPrevSib))) + { + goto Exit; + } + + if( RC_BAD( rc = pPrevSib->makeWriteCopy( this))) + { + goto Exit; + } + + pPrevSib->setNextSibId( pCachedNode->getNodeId()); + + if( RC_BAD( rc = updateNode( pPrevSib->m_pCachedNode, + FLM_UPD_INTERNAL_CHANGE))) + { + goto Exit; + } + } + + // Check indexing if it is an element + + if( eNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = updateIndexKeys( + uiCollection, pNewNode, IX_ADD_NODE_VALUE, TRUE))) + { + goto Exit; + } + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logNodeCreate( this, pCachedNode->getCollection(), + pCachedNode->getNodeId(), eNodeType, uiElementNameId, + XFLM_ROOT, pNewNode->getNodeId()))) + { + goto Exit; + } + + if( pui64NodeId) + { + *pui64NodeId = pCachedNode->getNodeId(); + } + + if( ppNewNode) + { + if( *ppNewNode) + { + (*ppNewNode)->Release(); + } + + *ppNewNode = pNewNode; + pNewNode = NULL; + } + +Exit: + + if( pNewNode) + { + pNewNode->Release(); + } + + if( pPrevSib) + { + pPrevSib->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::findNode( + FLMUINT uiCollection, + FLMUINT64 * pui64NodeId, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + F_BTreeIStream btreeIStream; + + // Determine the node's B-Tree address + + if( RC_BAD( rc = getCachedBTree( uiCollection, &pBTree))) + { + goto Exit; + } + + // At this point, we know that uiFlags is NOT XFLM_EXACT, but is + // XFLM_INCL, XFLM_EXCL, XFLM_FIRST, or XFLM_LAST. So we will + // need to reassign ui64NodeId once we have located the node. + + if( RC_BAD( rc = btreeIStream.open( this, pBTree, + uiFlags, uiCollection, *pui64NodeId, 0, 0))) + { + goto Exit; + } + *pui64NodeId = btreeIStream.m_ui64NodeId; + + // Close the input stream + + btreeIStream.close(); + +Exit: + + if( pBTree) + { + pBTree->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::getNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiFlags, + F_DOMNode ** ppNode) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Quick check to see if the user is re-retrieving the same + // node that is currently pointed to by pNode + + if( uiFlags == XFLM_EXACT) + { + F_DOMNode * pTmpNode; + + if( ((pTmpNode = *ppNode) != NULL) && + pTmpNode->m_pCachedNode && pTmpNode->m_uiAttrNameId == 0) + { + if( pTmpNode->getNodeId() == ui64NodeId && + pTmpNode->getCollection() == uiCollection && + pTmpNode->getDatabase() == m_pDatabase) + { + if( RC_BAD( rc = pTmpNode->syncFromDb( this))) + { + if( rc == NE_XFLM_DOM_NODE_DELETED) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + } + + goto Exit; + } + } + } + else + { + if( getTransType() == XFLM_UPDATE_TRANS) + { + // Need to flush dirty nodes through to the B-Tree so that + // look-ups will work correctly + + if( RC_BAD( rc = flushDirtyNodes())) + { + goto Exit; + } + } + + if( RC_BAD( rc = findNode( uiCollection, &ui64NodeId, uiFlags))) + { + goto Exit; + } + } + + // Retrieve the node into cache. + + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->retrieveNode( this, + uiCollection, ui64NodeId, &pNode))) + { + goto Exit; + } + + if( *ppNode) + { + (*ppNode)->Release(); + } + + *ppNode = pNode; + pNode = NULL; + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getAttribute( + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiAttrName, + IF_DOMNode ** ifppAttr) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pElementNode = NULL; + F_DOMNode * pAttrNode = NULL; + F_AttrItem * pAttrItem; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = getNode( uiCollection, ui64ElementId, + XFLM_EXACT, &pElementNode))) + { + goto Exit; + } + + if( pElementNode->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( (pAttrItem = pElementNode->m_pCachedNode->getAttribute( uiAttrName, + NULL)) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttrNode))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttrNode->m_pCachedNode = pElementNode->m_pCachedNode; + pAttrNode->m_pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + pAttrNode->m_uiAttrNameId = pAttrItem->m_uiNameId; + + if( ifppAttr) + { + if( *ifppAttr) + { + (*ifppAttr)->Release(); + } + + *ifppAttr = (IF_DOMNode *)pAttrNode; + pAttrNode = NULL; + } + +Exit: + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( pElementNode) + { + pElementNode->Release(); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getFirstDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( !pCollection->ui64FirstDocId) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64FirstDocId, + ppDocumentNode))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getLastDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + if( !pCollection->ui64LastDocId) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64LastDocId, + ppDocumentNode))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_Db::getDocument( + FLMUINT uiCollection, + FLMUINT uiFlags, + FLMUINT64 ui64DocumentId, + IF_DOMNode ** ppDocumentNode) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + FLMBOOL bStartedTrans = FALSE; + F_Btree * pBTree = NULL; + F_DOMNode * pNode = NULL; + FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + FLMUINT64 ui64NodeId; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + switch (uiFlags) + { + case XFLM_FIRST: + { + if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64FirstDocId, + ppDocumentNode))) + { + goto Exit; + } + + break; + } + + case XFLM_LAST: + { + if( RC_BAD( rc = getNode( uiCollection, pCollection->ui64LastDocId, + ppDocumentNode))) + { + goto Exit; + } + + break; + } + + case XFLM_EXACT: + { + if( RC_BAD( rc = getNode( uiCollection, ui64DocumentId, &pNode))) + { + goto Exit; + } + + if( !pNode->isRootNode()) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( *ppDocumentNode) + { + (*ppDocumentNode)->Release(); + } + + // No need to do an AddRef on ppDocumentNode - just use the reference + // from pNode and set pNode to NULL so it won't be released + + *ppDocumentNode = pNode; + pNode = NULL; + break; + } + + case XFLM_INCL: + case XFLM_EXCL: + { + if( getTransType() == XFLM_UPDATE_TRANS) + { + // Need to flush dirty nodes through to the B-Tree so that + // look-ups will work correctly + + if( RC_BAD( rc = flushDirtyNodes())) + { + goto Exit; + } + } + + // Get a btree + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btOpen( this, &pCollection->lfInfo, + FALSE, TRUE))) + { + goto Exit; + } + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64DocumentId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + goto Exit; + } + + // Make sure we hit a root node. If not, continue reading until we do + // or until we hit the end. Root nodes are always linked together in + // ascending order, so if there is another document, we will find it + // simply by searching forward from where we are. Then we can follow + // document links. + + for (;;) + { + if( RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64NodeId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + if( RC_BAD( rc = getNode( uiCollection, ui64NodeId, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + // If the node is a root node, we have a document we can + // process. + + if( pNode->isRootNode()) + { + if( uiFlags == XFLM_EXCL && ui64NodeId == ui64DocumentId) + { + if( RC_BAD( rc = pNode->getNextDocument( this, ppDocumentNode))) + { + goto Exit; + } + } + else + { + if( *ppDocumentNode) + { + (*ppDocumentNode)->Release(); + } + + // No need to do an AddRef on ppDocumentNode - just use the reference + // from pNode and set pNode to NULL so it won't be released + + *ppDocumentNode = pNode; + pNode = NULL; + } + + break; + } + + // Need to go to the next node. + + if( RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + goto Exit; + } + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +Notes: This routine assumes that the node has been unlinked. It doesn't + perform any checks to guarantee that removing the node won't cause + referential integrity problems. +******************************************************************************/ +RCODE F_Db::purgeNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + FLMUINT uiKeyLen; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + + // Make sure we're in an update transaction + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Remove the node from the collection + + if( RC_BAD( rc = getCachedBTree( uiCollection, &pBTree))) + { + goto Exit; + } + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, + &uiKeyLen, ucKey, FALSE, TRUE))) + { + goto Exit; + } + + bMustAbortOnError = TRUE; + + if( RC_BAD( rc = pBTree->btRemoveEntry( ucKey, sizeof( ucKey), uiKeyLen))) + { + if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + + // Item may not have been flushed from cache yet + + rc = NE_XFLM_OK; + } + + // Remove the node from the cache + + gv_XFlmSysData.pNodeCacheMgr->removeNode( this, uiCollection, ui64NodeId); + +Exit: + + if( pBTree) + { + pBTree->Release(); + } + + if( RC_BAD( rc)) + { + if( bMustAbortOnError) + { + setMustAbortTrans( rc); + } + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::documentDone( + FLMUINT uiCollection, + FLMUINT64 ui64DocId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( !m_pDatabase->m_DocumentList.isNodeInList( uiCollection, ui64DocId, 0)) + { + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + + if( uiCollection == XFLM_DICT_COLLECTION) + { + FLMUINT uiDictDefType; + + if( RC_BAD( rc = dictDocumentDone( ui64DocId, FALSE, &uiDictDefType))) + { + goto Exit; + } + + // If this is an encryption definition, we need to log the encryption + // key + + if( uiDictDefType == ELM_ENCDEF_TAG && !(m_uiFlags & FDB_REPLAYING_RFL)) + { + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf keyBuffer( ucDynaBuf, sizeof( ucDynaBuf)); + FLMUINT uiKeySize; + + if( RC_BAD( rc = getNode( uiCollection, ui64DocId, + XFLM_EXACT, &pNode))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if( RC_BAD( rc = pNode->getAttributeValueBinary( this, + ATTR_ENCRYPTION_KEY_TAG, &keyBuffer))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if( RC_BAD( rc = pNode->getAttributeValueUINT( this, + ATTR_ENCRYPTION_KEY_SIZE_TAG, &uiKeySize))) + { + goto Exit; + } + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logEncDefKey( this, (FLMUINT)ui64DocId, + keyBuffer.getBufferPtr(), keyBuffer.getDataLength(), uiKeySize))) + { + goto Exit; + } + + pRfl->disableLogging( &uiRflToken); + } + } + + m_pDatabase->m_DocumentList.removeNode( uiCollection, ui64DocId, 0); + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logDocumentDone( this, uiCollection, ui64DocId))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::documentDone( + IF_DOMNode * pDocNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiCollection; + FLMUINT64 ui64DocId; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = pDocNode->getCollection( this, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pDocNode->getDocumentId( this, &ui64DocId))) + { + goto Exit; + } + + if( RC_BAD( rc = documentDone( uiCollection, ui64DocId))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::import( + IF_IStream * ifpStream, + FLMUINT uiCollection, + IF_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + XFLM_IMPORT_STATS * pImportStats) +{ + RCODE rc = NE_XFLM_OK; + F_XMLImport xmlImport; + F_DOMNode * pNode = NULL; + F_DOMNode * pNewNode = NULL; + F_IStream * pStream = (F_IStream *)ifpStream; + + if( RC_BAD( rc = xmlImport.setup())) + { + goto Exit; + } + + if( (pNode = (F_DOMNode *)pNodeToLinkTo) != NULL) + { + pNode->AddRef(); + } + + for( ;;) + { + if( RC_BAD( rc = xmlImport.import( pStream, this, uiCollection, + FLM_XML_COMPRESS_WHITESPACE_FLAG | + FLM_XML_TRANSLATE_ESC_FLAG | + FLM_XML_EXTEND_DICT_FLAG, pNode, eInsertLoc, + &pNewNode, pImportStats))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + + if( RC_BAD( rc = documentDone( pNewNode))) + { + goto Exit; + } + + // If pNode is NULL, we are creating separate documents + // and pNode should remain NULL. Otherwise, it should be + // set to the newly created node, and all subsequent trees + // should be linked as next siblings after this one. + + if( pNode) + { + pNode->Release(); + pNode = pNewNode; + + // No need to do pNode->AddRef() and pNewNode->Release() since + // there is already a reference on pNewNode. + + pNewNode = NULL; + eInsertLoc = XFLM_NEXT_SIB; + } + else + { + pNewNode->Release(); + pNewNode = NULL; + } + + xmlImport.reset(); + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pNewNode) + { + pNewNode->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::importDocument( + IF_IStream * ifpStream, + FLMUINT uiCollection, + IF_DOMNode ** ppDoc, + XFLM_IMPORT_STATS * pImportStats) +{ + RCODE rc = NE_XFLM_OK; + F_XMLImport xmlImport; + F_DOMNode * pNode = NULL; + F_IStream * pIStream = (F_IStream *)ifpStream; + + if( pNode) + { + pNode->AddRef(); + } + + if( RC_BAD( rc = xmlImport.setup())) + { + goto Exit; + } + + if( RC_BAD( rc = xmlImport.import( pIStream, this, uiCollection, + FLM_XML_COMPRESS_WHITESPACE_FLAG | + FLM_XML_TRANSLATE_ESC_FLAG | + FLM_XML_EXTEND_DICT_FLAG, NULL, XFLM_LAST_CHILD, &pNode, pImportStats))) + { + goto Exit; + } + + if( RC_BAD( rc = documentDone( pNode))) + { + goto Exit; + } + + if( ppDoc) + { + if( *ppDoc) + { + (*ppDoc)->Release(); + } + + *ppDoc = pNode; + pNode = NULL; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createElemOrAttrDef( + FLMBOOL bElement, + FLMBOOL bUnicode, + const void * pvNamespaceURI, + const void * pvLocalName, + FLMUINT uiDataType, + FLMBOOL bUniqueChildElms, + FLMUINT * puiNameId, + F_DOMNode ** ppDocumentNode) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, + bElement ? ELM_ELEMENT_TAG : ELM_ATTRIBUTE_TAG, ELEMENT_NODE, &pDoc))) + { + goto Exit; + } + + if( pvNamespaceURI) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_TARGET_NAMESPACE_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( + this, (FLMUNICODE *)pvNamespaceURI))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvNamespaceURI))) + { + goto Exit; + } + } + } + + if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvLocalName))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvLocalName))) + { + goto Exit; + } + } + + if( puiNameId && *puiNameId) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( this, *puiNameId))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_TYPE_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUTF8( this, + (FLMBYTE *)fdictGetDataTypeStr( uiDataType)))) + { + goto Exit; + } + + if( bUniqueChildElms && bElement) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_UNIQUE_SUB_ELEMENTS_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)"true"))) + { + goto Exit; + } + } + + if( RC_BAD( rc = documentDone( pDoc))) + { + goto Exit; + } + + if( puiNameId) + { + if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( this, puiNameId))) + { + goto Exit; + } + } + + if( ppDocumentNode) + { + if( (*ppDocumentNode)) + { + (*ppDocumentNode)->Release(); + } + + *ppDocumentNode = pDoc; + pDoc = NULL; + } + +Exit: + + if( pDoc) + { + pDoc->Release(); + } + + if( pAttr) + { + flmAssert( pAttr->m_ui32RefCnt <= 2); + pAttr->Release(); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + else if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createPrefixDef( + FLMBOOL bUnicode, + const void * pvPrefixName, + FLMUINT * puiPrefixNumber) +{ + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + F_DOMNode * pNumAttr = NULL; + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, + ELM_PREFIX_TAG, ELEMENT_NODE, &pDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvPrefixName))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvPrefixName))) + { + goto Exit; + } + } + + // Create the prefix number attribute if passed in. + + if( puiPrefixNumber && *puiPrefixNumber) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pNumAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pNumAttr->setUINT( this, *puiPrefixNumber))) + { + goto Exit; + } + } + + if( RC_BAD( rc = documentDone( pDoc))) + { + goto Exit; + } + + // Change the modes on the definition so that the name cannot be modified + // and the definition cannot be deleted. This needs to be done after + // calling documentDone() because it sets the flags on the nodes of the + // definition according to a set of default rules that do not correspond + // to what we need to have in this case. + + if( RC_BAD( rc = pAttr->addModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( puiPrefixNumber) + { + if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, + (IF_DOMNode **)&pNumAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pNumAttr->getUINT( this, puiPrefixNumber))) + { + goto Exit; + } + } + +Exit: + + if( pDoc) + { + pDoc->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( pNumAttr) + { + pNumAttr->Release(); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + else if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::createEncDef( + FLMBOOL bUnicode, + const void * pvEncType, + const void * pvEncName, + FLMUINT uiKeySize, + FLMUINT * puiEncDefNumber) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, + ELM_ENCDEF_TAG, ELEMENT_NODE, &pDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = pDoc->createAttribute( this, ATTR_NAME_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvEncName))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvEncName))) + { + goto Exit; + } + } + + // Create the encdef id attribute if passed in. + + if( puiEncDefNumber && *puiEncDefNumber) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( this, *puiEncDefNumber))) + { + goto Exit; + } + } + + // Create the algorithm attribute + + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_TYPE_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( this, (FLMUNICODE *)pvEncType))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvEncType))) + { + goto Exit; + } + } + + // Create the key size attribute + + if( uiKeySize) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_ENCRYPTION_KEY_SIZE_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( this, uiKeySize))) + { + goto Exit; + } + } + + // Call documentDone to complete it all. + + if( RC_BAD( rc = documentDone( pDoc))) + { + goto Exit; + } + + if( puiEncDefNumber) + { + if( RC_BAD( rc = pDoc->getAttribute( this, ATTR_DICT_NUMBER_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( this, puiEncDefNumber))) + { + goto Exit; + } + } + +Exit: + + if( pDoc) + { + pDoc->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + else if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Db::getPrefixId( + const FLMUNICODE * puzPrefixName, + FLMUINT * puiPrefixNumber) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getPrefixId( + this, puzPrefixName, puiPrefixNumber))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Db::getPrefixId( + const char * pszPrefixName, + FLMUINT * puiPrefixNumber) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getPrefixId( + this, pszPrefixName, puiPrefixNumber))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Db::getEncDefId( + const char * pszEncDefName, + FLMUINT * puiEncDefNumber) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getEncDefId( this, + pszEncDefName, puiEncDefNumber))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Db::getEncDefId( + const FLMUNICODE * puzEncDefName, + FLMUINT * puiEncDefNumber) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDict->getEncDefId( + this, puzEncDefName, puiEncDefNumber))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} + +/***************************************************************************** +Desc: Create a new c ollection definition in the dictionary. +******************************************************************************/ +RCODE F_Db::createCollectionDef( + FLMBOOL bUnicode, + const void * pvCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncNumber) +{ + F_DOMNode * pDoc = NULL; + F_DOMNode * pAttr = NULL; + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // First, create the root element to hold the collection definition + + if( RC_BAD( rc = createRootNode( XFLM_DICT_COLLECTION, + ELM_COLLECTION_TAG, ELEMENT_NODE, &pDoc))) + { + goto Exit; + } + + // Create the collection name attruibute + + if( RC_BAD( rc = pDoc->createAttribute( + this, ATTR_NAME_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( bUnicode) + { + if( RC_BAD( rc = pAttr->setUnicode( + this, (FLMUNICODE *)pvCollectionName))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pvCollectionName))) + { + goto Exit; + } + } + + // Create the collection number attribute if passed in. + + if( puiCollectionNumber && *puiCollectionNumber) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( this, *puiCollectionNumber))) + { + goto Exit; + } + } + + if( uiEncNumber) + { + if( RC_BAD( rc = pDoc->createAttribute( this, + ATTR_ENCRYPTION_ID_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( this, uiEncNumber))) + { + goto Exit; + } + } + + if( RC_BAD( rc = documentDone( pDoc))) + { + goto Exit; + } + + if( puiCollectionNumber) + { + if( RC_BAD( rc = pDoc->getAttribute( + this, ATTR_DICT_NUMBER_TAG, (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( this, puiCollectionNumber))) + { + goto Exit; + } + } + +Exit: + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + if( pDoc) + { + pDoc->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::flushDirtyNodes( void) +{ + RCODE rc = NE_XFLM_OK; + F_CachedNode * pNode; + F_Btree * pBtree = NULL; + FLMUINT uiRflToken = 0; + FLMUINT uiCollection = 0; + + if( !m_uiDirtyNodeCount) + { + goto Exit; + } + + // Disable RFL logging + + m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + + // All of the dirty nodes should be at the front of the list. + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + while ((pNode = m_pDatabase->m_pFirstNode) != NULL) + { + if( !pNode->nodeIsDirty()) + { + break; + } + + // Flushing the node should remove it from the front of the list. + // Need to increment the use count on the node to prevent it from + // being moved while we are flushing it to disk. + +#ifdef FLM_CACHE_PROTECT + pNode->unprotectCachedItem(); +#endif + pNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + if( uiCollection != pNode->getCollection()) + { + if( pBtree) + { + pBtree->Release(); + } + + uiCollection = pNode->getCollection(); + if( RC_BAD( rc = getCachedBTree( uiCollection, &pBtree))) + { + goto Exit; + } + } + + rc = flushNode( pBtree, pNode); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pNode->decrNodeUseCount(); +#ifdef FLM_CACHE_PROTECT + pNode->protectCachedItem(); +#endif + + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + if( RC_BAD( rc)) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + } + + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + flmAssert( !m_uiDirtyNodeCount); + +Exit: + + if( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if( pBtree) + { + pBtree->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Db::flushDirtyNode( + F_CachedNode * pNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiRflToken = 0; + F_Btree * pBTree = NULL; + + m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + + if( !pNode->nodeIsDirty()) + { + goto Exit; + } + + // Get a B-Tree object + + if( RC_BAD( rc = getCachedBTree( pNode->getCollection(), &pBTree))) + { + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + +#ifdef FLM_CACHE_PROTECT + pNode->unprotectCachedItem(); +#endif + + pNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = FALSE; + + rc = flushNode( pBTree, pNode); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + + pNode->decrNodeUseCount(); +#ifdef FLM_CACHE_PROTECT + pNode->protectCachedItem(); +#endif + + if( RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + if( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + if( pBTree) + { + pBTree->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_Database::startPendingInput( + FLMUINT uiPendingType, + F_CachedNode * pPendingNode) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pPendingInput) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit; + } + + // Not supported on element nodes + + if( pPendingNode->getNodeType() == ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + m_uiPendingType = uiPendingType; + m_pPendingInput = pPendingNode; + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + m_pPendingInput->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + m_uiUpdCharCount = 0; + m_bUpdFirstBuf = TRUE; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void F_Database::endPendingInput( void) +{ + if( m_pPendingInput) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + m_pPendingInput->decrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + m_pPendingInput = NULL; + } + + if( m_pPendingBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pPendingBTree); + m_pPendingBTree = NULL; + } + + m_uiPendingType = XFLM_NODATA_TYPE; + m_bUpdFirstBuf = TRUE; + m_uiUpdByteCount = 0; + m_uiUpdCharCount = 0; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::createAttribute( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_AttrItem ** ppAttrItem) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem = NULL; + FLMUINT uiInsertPos; + + // Attribute should not exist + + if (getAttribute( uiAttrNameId, &uiInsertPos) != NULL) + { + flmAssert( 0); + } + else + { + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Allocate the new attribute + + if( RC_BAD( rc = allocAttribute( pDb, uiAttrNameId, NULL, uiInsertPos, + &pAttrItem, FALSE))) + { + goto Exit; + } + } + +Exit: + + if( ppAttrItem) + { + *ppAttrItem = pAttrItem; + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_AttrItem * F_CachedNode::getAttribute( + FLMUINT uiAttrNameId, + FLMUINT * puiInsertPos) +{ + FLMUINT uiLoop; + F_AttrItem * pAttrItem = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblNameId; + + if (!m_uiAttrCount) + { + if (puiInsertPos) + { + *puiInsertPos = 0; + } + goto Exit; + } + + // If the attribute count is <= 4, do a sequential search through + // the array. Otherwise, do a binary search. + + if ((uiTblSize = m_uiAttrCount) <= 4) + { + for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) + { + pAttrItem = m_ppAttrList [uiLoop]; + if (pAttrItem->m_uiNameId == uiAttrNameId) + { + break; + } + else if (pAttrItem->m_uiNameId > uiAttrNameId) + { + pAttrItem = NULL; + break; + } + } + if (uiLoop == m_uiAttrCount) + { + pAttrItem = NULL; + } + if (puiInsertPos) + { + *puiInsertPos = uiLoop; + } + } + else + { + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + uiTblNameId = m_ppAttrList [uiMid]->m_uiNameId; + if (uiTblNameId == uiAttrNameId) + { + // Found Match + + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + pAttrItem = m_ppAttrList [uiMid]; + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + // Done, item not found + + if (puiInsertPos) + { + *puiInsertPos = (uiAttrNameId < uiTblNameId) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (uiAttrNameId < uiTblNameId) + { + 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( pAttrItem); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getPrevSiblingNode( + FLMUINT uiCurrentNameId, + IF_DOMNode ** ppSib) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + F_DOMNode * pAttr = NULL; + + if( (pAttrItem = getPrevSibling( uiCurrentNameId)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttr->m_uiAttrNameId = pAttrItem->m_uiNameId; + pAttr->m_pCachedNode = this; + incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + if( ppSib) + { + if( *ppSib) + { + (*ppSib)->Release(); + } + + *ppSib = (IF_DOMNode *)pAttr; + pAttr = NULL; + } + +Exit: + + if( pAttr) + { + pAttr->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getNextSiblingNode( + FLMUINT uiCurrentNameId, + IF_DOMNode ** ppSib) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + F_DOMNode * pAttr = NULL; + + if( (pAttrItem = getNextSibling( uiCurrentNameId)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocDOMNode( &pAttr))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pAttr->m_uiAttrNameId = pAttrItem->m_uiNameId; + pAttr->m_pCachedNode = this; + incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + if( ppSib) + { + if( *ppSib) + { + (*ppSib)->Release(); + } + + *ppSib = (IF_DOMNode *)pAttr; + pAttr = NULL; + } + +Exit: + + if( pAttr) + { + pAttr->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::allocAttribute( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_AttrItem * pCopyFromItem, + FLMUINT uiInsertPos, + F_AttrItem ** ppAttrItem, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + F_AttrElmInfo defInfo; + F_AttrItem * pAttrItem = NULL; + FLMUINT uiSize; + FLMBOOL bMutexLocked = FALSE; + + if( RC_BAD( rc = pDb->m_pDict->getAttribute( + pDb, uiAttrNameId, &defInfo))) + { + flmAssert( 0); + goto Exit; + } + + if( !bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + } + + if( (pAttrItem = new F_AttrItem) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( pCopyFromItem) + { + pAttrItem->copyFrom( pCopyFromItem); + } + else + { + if( defInfo.getFlags() & ATTR_ELM_NS_DECL) + { + pAttrItem->m_uiFlags |= FDOM_NAMESPACE_DECL; + } + } + + // Find where this thing goes in the attribute list + // If we are copying, we don't need to resize the list as it will + // already be the correct size. + + if (!pCopyFromItem) + { + if (RC_BAD( rc = resizeAttrList( m_uiAttrCount + 1, bMutexLocked))) + { + goto Exit; + } + + // NOTE: m_uiAttrCount will have been incremented to accommodate + // the new attribute. + + // Move everything above the [uiInsertPos] slot up. Remember, no need to + // preserve the item at [m_uiAttrCount - 1], because we just increased + // the size of the array, and there is nothing there right now. + + if (uiInsertPos < m_uiAttrCount - 1) + { + f_memmove( &m_ppAttrList [uiInsertPos + 1], + &m_ppAttrList [uiInsertPos], + sizeof( F_AttrItem *) * (m_uiAttrCount - uiInsertPos - 1)); + } + } + m_ppAttrList [uiInsertPos] = pAttrItem; + + pAttrItem->m_pCachedNode = this; + pAttrItem->m_uiDataType = defInfo.getDataType(); + pAttrItem->m_uiNameId = uiAttrNameId; + *ppAttrItem = pAttrItem; + + uiSize = pAttrItem->memSize(); + m_uiTotalAttrSize += uiSize; + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; + } + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; + + // Set to NULL so it won't get deleted below. + + pAttrItem = NULL; + +Exit: + + if (pAttrItem) + { + delete pAttrItem; + } + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::freeAttribute( + F_AttrItem * pAttrItem, + FLMUINT uiPos) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + + // Move everything above the [uiPos] slot down. + + if (uiPos < m_uiAttrCount - 1) + { + f_memmove( &m_ppAttrList [uiPos], + &m_ppAttrList [uiPos + 1], + sizeof( F_AttrItem *) * (m_uiAttrCount - uiPos - 1)); + } + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + if (RC_BAD( rc = resizeAttrList( m_uiAttrCount - 1, bMutexLocked))) + { + goto Exit; + } + + delete pAttrItem; + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::setNumber64( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 ui64Value, + FLMBOOL bNeg, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMUINT uiValLen = 0; + FLMUINT uiEncryptedLen; + FLMBYTE ucNumBuf[ 32]; + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Get a pointer to the attribute list item + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) + { + goto Exit; + } + } + else + { + if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + } + + pAttrItem->m_ui64QuickVal = ui64Value; + + if( bNeg) + { + pAttrItem->m_uiFlags |= FDOM_SIGNED_QUICK_VAL; + } + else + { + pAttrItem->m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; + } + + switch( pAttrItem->m_uiDataType) + { + case XFLM_NUMBER_TYPE: + { + if( ui64Value <= 0x7F && !uiEncDefId) + { + if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, 0, 1, FALSE, + FALSE))) + { + goto Exit; + } + + *(pAttrItem->getAttrDataPtr()) = (FLMBYTE)ui64Value; + goto Exit; + } + else + { + uiValLen = sizeof( ucNumBuf); + if( RC_BAD( rc = flmNumber64ToStorage( + ui64Value, &uiValLen, ucNumBuf, bNeg, FALSE))) + { + goto Exit; + } + } + + break; + } + + case XFLM_TEXT_TYPE: + { + FLMBYTE * pucSen; + FLMUINT uiSenLen; + + if( bNeg) + { + ucNumBuf[ 1] = '-'; + f_ui64toa( ui64Value, (char *)&ucNumBuf[ 2]); + } + else + { + f_ui64toa( ui64Value, (char *)&ucNumBuf[ 1]); + } + + uiValLen = f_strlen( &ucNumBuf[ 1]); + pucSen = &ucNumBuf [0]; + uiSenLen = flmEncodeSEN( uiValLen, &pucSen, (FLMUINT)0); + flmAssert( uiSenLen == 1); + uiValLen += uiSenLen + 1; + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + } + + if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, + uiValLen, TRUE, FALSE))) + { + goto Exit; + } + + if( uiValLen) + { + f_memcpy( pAttrItem->getAttrDataPtr(), ucNumBuf, uiValLen); + + if( uiEncDefId) + { + if( RC_BAD( rc = pDb->encryptData( uiEncDefId, + pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), + pAttrItem->getAttrDataBufferSize(), uiValLen, &uiEncryptedLen))) + { + goto Exit; + } + } + } + else + { + pAttrItem->m_uiPayloadLen = 0; + } + + pAttrItem->m_uiDecryptedDataLen = uiValLen; + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getNumber64( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Value, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNeg; + FLMUINT64 ui64Value; + F_AttrItem * pAttrItem; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + else if( pAttrItem->m_uiFlags & FDOM_UNSIGNED_QUICK_VAL) + { + *pbNeg = FALSE; + *pui64Value = pAttrItem->m_ui64QuickVal; + } + else if( pAttrItem->m_uiFlags & FDOM_SIGNED_QUICK_VAL) + { + *pbNeg = TRUE; + *pui64Value = pAttrItem->m_ui64QuickVal; + } + else + { + if( RC_BAD( rc = getIStream( pDb, uiAttrNameId, &bufferIStream, &pIStream))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsNumber( + pIStream, pAttrItem->m_uiDataType, &ui64Value, &bNeg))) + { + goto Exit; + } + + *pui64Value = ui64Value; + *pbNeg = bNeg; + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::setBinary( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiValueLen, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMUINT uiDecryptedValLen = 0; + FLMUINT uiEncryptedLen; + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Get a pointer to the attribute list item + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) + { + goto Exit; + } + } + else + { + if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + } + + switch( pAttrItem->m_uiDataType) + { + case XFLM_BINARY_TYPE: + { + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, + uiValueLen, TRUE, FALSE))) + { + goto Exit; + } + + if( uiValueLen) + { + f_memcpy( pAttrItem->getAttrDataPtr(), pvValue, uiValueLen); + + if( uiEncDefId) + { + uiDecryptedValLen = uiValueLen; + if( RC_BAD( rc = pDb->encryptData( uiEncDefId, + pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), + pAttrItem->getAttrDataBufferSize(), uiValueLen, &uiEncryptedLen))) + { + goto Exit; + } + + flmAssert( uiEncryptedLen == pAttrItem->getAttrDataBufferSize()); + } + } + + pAttrItem->m_uiDecryptedDataLen = uiValueLen; + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::getBinary( + F_Db * pDb, + FLMUINT uiAttrNameId, + void * pvBuffer, + FLMUINT uiBufferLen, + FLMUINT * puiDataLen) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + + // If a NULL buffer is passed in, just return the + // data length + + if( !pvBuffer) + { + rc = getDataLength( uiAttrNameId, puiDataLen); + goto Exit; + } + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( !pAttrItem->m_uiEncDefId) + { + FLMUINT uiCopySize = f_min( uiBufferLen, + pAttrItem->getAttrDataLength()); + + if( uiCopySize) + { + f_memcpy( pvBuffer, pAttrItem->getAttrDataPtr(), uiCopySize); + } + + if( puiDataLen) + { + *puiDataLen = uiCopySize; + } + } + else + { + if( RC_BAD( rc = getIStream( pDb, uiAttrNameId, &bufferIStream, &pIStream))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadStorageAsBinary( pIStream, + pvBuffer, uiBufferLen, 0, puiDataLen))) + { + goto Exit; + } + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::setUTF8( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiNumBytesInValue, + FLMUINT uiNumCharsInValue, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMUINT uiValLen = 0; + FLMUINT uiDecryptedValLen = 0; + FLMUINT uiEncryptedLen; + FLMUINT uiSenLen; + FLMBYTE * pucValue = (FLMBYTE *)pvValue; + FLMBOOL bNullTerminate = FALSE; + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Get a pointer to the attribute list item + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) + { + goto Exit; + } + } + else + { + if( pAttrItem->m_uiFlags & FDOM_READ_ONLY) + { + rc = RC_SET( NE_XFLM_READ_ONLY); + goto Exit; + } + pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + } + + switch( pAttrItem->m_uiDataType) + { + case XFLM_TEXT_TYPE: + { + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + if( pvValue && uiNumBytesInValue) + { + if( pucValue[ uiNumBytesInValue - 1] != 0) + { + bNullTerminate = TRUE; + } + + uiSenLen = flmGetSENByteCount( uiNumCharsInValue); + uiValLen = uiNumBytesInValue + uiSenLen + (bNullTerminate ? 1 : 0); + } + else + { + uiSenLen = 0; + uiValLen = 0; + } + + if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, + uiValLen, TRUE, FALSE))) + { + goto Exit; + } + + if( uiValLen) + { + FLMBYTE * pucTmp = pAttrItem->getAttrDataPtr(); + + flmEncodeSENKnownLength( uiNumCharsInValue, uiSenLen, &pucTmp); + f_memcpy( pucTmp, pucValue, uiNumBytesInValue); + + if( bNullTerminate) + { + pucTmp[ uiNumBytesInValue] = 0; + } + + if( uiEncDefId) + { + uiDecryptedValLen = uiValLen; + if( RC_BAD( rc = pDb->encryptData( uiEncDefId, + pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), + pAttrItem->getAttrDataBufferSize(), uiValLen, &uiEncryptedLen))) + { + goto Exit; + } + } + } + + pAttrItem->m_uiDecryptedDataLen = uiValLen; + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::setStorageValue( + F_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiValueLen, + FLMUINT uiEncDefId) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMUINT uiDecryptedValLen = 0; + FLMUINT uiEncryptedLen; + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Get a pointer to the attribute list item + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + if( RC_BAD( rc = createAttribute( pDb, uiAttrNameId, &pAttrItem))) + { + goto Exit; + } + } + else + { + pAttrItem->m_uiFlags &= ~(FDOM_UNSIGNED_QUICK_VAL | FDOM_SIGNED_QUICK_VAL); + } + + if( pAttrItem->m_uiDataType == XFLM_UNKNOWN_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = pAttrItem->setupAttribute( pDb, uiEncDefId, + uiValueLen, TRUE, FALSE))) + { + goto Exit; + } + + if( uiValueLen) + { + f_memcpy( pAttrItem->getAttrDataPtr(), pvValue, uiValueLen); + + if( uiEncDefId) + { + uiDecryptedValLen = uiValueLen; + if( RC_BAD( rc = pDb->encryptData( uiEncDefId, + pAttrItem->getAttrIVPtr(), pAttrItem->getAttrDataPtr(), + pAttrItem->getAttrDataBufferSize(), uiValueLen, &uiEncryptedLen))) + { + goto Exit; + } + + flmAssert( uiEncryptedLen == pAttrItem->getAttrDataBufferSize()); + } + } + + pAttrItem->m_uiDecryptedDataLen = uiValueLen; + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::addModeFlags( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + + F_UNREFERENCED_PARM( pDb); + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + pAttrItem->m_uiFlags |= uiFlags; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::removeModeFlags( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + + F_UNREFERENCED_PARM( pDb); + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + pAttrItem->m_uiFlags &= ~uiFlags; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::setPrefixId( + F_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + + F_UNREFERENCED_PARM( pDb); + + // Logging should be done by the caller + + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + pAttrItem->m_uiPrefixId = uiPrefixId; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_CachedNode::getIStream( + F_Db * pDb, + FLMUINT uiAttrNameId, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType, + FLMUINT * puiDataLength) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + F_NodeBufferIStream * pNodeBufferIStream = NULL; + FLMBYTE * pucAllocatedBuffer = NULL; + + if( (pAttrItem = getAttribute( uiAttrNameId, NULL)) == NULL) + { + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if( pStackStream) + { + pNodeBufferIStream = pStackStream; + pStackStream->AddRef(); + flmAssert( !pStackStream->m_pCachedNode); + } + else + { + if( (pNodeBufferIStream = f_new F_NodeBufferIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if( pAttrItem->m_uiEncDefId) + { + flmAssert( pAttrItem->m_uiIVLen); + + if( RC_BAD( rc = pNodeBufferIStream->open( NULL, + pAttrItem->getAttrDataBufferSize(), &pucAllocatedBuffer))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->decryptData( + pAttrItem->m_uiEncDefId, pAttrItem->getAttrIVPtr(), + pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataBufferSize(), + pucAllocatedBuffer, (FLMUINT)pNodeBufferIStream->totalSize()))) + { + goto Exit; + } + + pNodeBufferIStream->truncate( pAttrItem->getAttrDataLength()); + } + else + { + if( RC_BAD( rc = pNodeBufferIStream->open( + pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength()))) + { + goto Exit; + } + } + + if( !pStackStream) + { + pNodeBufferIStream->m_pCachedNode = this; + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + incrNodeUseCount(); + incrStreamUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + if( puiDataType) + { + *puiDataType = pAttrItem->m_uiDataType; + } + + if( puiDataLength) + { + *puiDataLength = (FLMUINT)pNodeBufferIStream->remainingSize(); + } + + if( *ppIStream) + { + (*ppIStream)->Release(); + } + + *ppIStream = pNodeBufferIStream; + pNodeBufferIStream = NULL; + +Exit: + + if( pNodeBufferIStream) + { + pNodeBufferIStream->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_AttrItem::resizePayloadBuffer( + FLMUINT uiTotalNeeded, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurrentSize = m_uiPayloadLen; + FLMBOOL bMutexLocked = FALSE; + + if( uiCurrentSize != uiTotalNeeded) + { + FLMUINT uiOldSize = memSize(); + FLMUINT uiNewSize; + FLMUINT uiSize; + + if( uiTotalNeeded <= sizeof( FLMBYTE *)) + { + if( uiCurrentSize && uiCurrentSize > sizeof( FLMBYTE *)) + { + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + } + m_pucPayload -= sizeof( F_AttrItem *); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( m_uiPayloadLen + + sizeof( F_AttrItem *), &m_pucPayload); + } + else + { + // NOTE: Mutex is NOT locked here, because + // nothing will be changed that requires the + // mutex to be locked. Nor will the size + // change. If the size were to change, we + // would want to lock the mutex because we + // would be incrementing/decrementing size + // counts below. + + m_pucPayload = NULL; + } + } + else + { + F_AttrItem * pAttrItem = this; + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + } + + if( uiCurrentSize && uiCurrentSize > sizeof( FLMBYTE *)) + { + m_pucPayload -= sizeof( F_AttrItem *); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_attrBufferRelocator, + m_uiPayloadLen + sizeof( F_AttrItem *), + uiTotalNeeded + sizeof( F_AttrItem *), + &pAttrItem, sizeof( F_AttrItem *), &m_pucPayload))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_attrBufferRelocator, + uiTotalNeeded + sizeof( F_AttrItem *), + &pAttrItem, sizeof( F_AttrItem *), &m_pucPayload))) + { + goto Exit; + } + } + + flmAssert( *((F_AttrItem **)m_pucPayload) == this); + m_pucPayload += sizeof( F_AttrItem *); + } + m_uiPayloadLen = uiTotalNeeded; + uiNewSize = memSize(); + if (uiNewSize > uiOldSize) + { + uiSize = uiNewSize - uiOldSize; + m_pCachedNode->m_uiTotalAttrSize += uiSize; + if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) + { + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; + } + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; + } + else if (uiNewSize < uiOldSize) + { + uiSize = uiOldSize - uiNewSize; + flmAssert( m_pCachedNode->m_uiTotalAttrSize >= uiSize); + m_pCachedNode->m_uiTotalAttrSize -= uiSize; + if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + } + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; + } + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_AttrItem::setupAttribute( + F_Db * pDb, + FLMUINT uiEncDefId, + FLMUINT uiDataSizeNeeded, + FLMBOOL bOkToGenerateIV, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTotalNeeded = uiDataSizeNeeded; + FLMUINT uiIVLen = 0; + FLMBOOL bGenerateIV = FALSE; + F_ENCDEF * pEncDef = NULL; + + if( uiEncDefId) + { + if( RC_BAD( rc = pDb->m_pDict->getEncDef( + uiEncDefId, &pEncDef))) + { + goto Exit; + } + + uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( uiIVLen == 8 || uiIVLen == 16); + m_uiEncDefId = uiEncDefId; + m_uiIVLen = uiIVLen; + + if( bOkToGenerateIV) + { + bGenerateIV = TRUE; + } + + uiTotalNeeded += m_uiIVLen + + (getEncLen( uiDataSizeNeeded) - uiDataSizeNeeded); + } + else + { + m_uiEncDefId = 0; + m_uiIVLen = 0; + } + +#ifdef FLM_DEBUG + if( uiEncDefId) + { + flmAssert( uiTotalNeeded >= 8 + uiDataSizeNeeded); + } +#endif + + if( RC_BAD( rc = resizePayloadBuffer( uiTotalNeeded, bMutexAlreadyLocked))) + { + goto Exit; + } + + if( bGenerateIV) + { + if( RC_BAD( rc = pEncDef->pCcs->generateIV( uiIVLen, getAttrIVPtr()))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/version5/src/fdynbtre.cpp b/version5/src/fdynbtre.cpp new file mode 100644 index 0000000..6bfbabf --- /dev/null +++ b/version5/src/fdynbtre.cpp @@ -0,0 +1,1198 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the FFixedBlk, FBtreeRoot, FBtreeLeaf, +// FBtreeNonLeaf, and FBtreeBlk classes. +// +// 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: fdynbtre.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fdynsset.h" + +// Make sure that the extension is in lower case characters. + +#define FRSET_FILENAME_EXTENSION "frs" + +/**************************************************************************** + +Organization: + + These modules are orgianized by function and NOT by class. + For example, all of the searchEntry modules are together. + All of the split and getFirst/next/last... modules are together. + +****************************************************************************/ + + +/**************************************************************************** + Constructors and Setup Methods +****************************************************************************/ + +/**************************************************************************** +Desc: Set common variables +****************************************************************************/ +FFixedBlk::FFixedBlk() +{ + m_fnCompare = NULL; + m_pvUserData = NULL; + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; + m_pucBlkBuf = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FBtreeRoot::FBtreeRoot() +{ + int i; + + m_pFileHdl = NULL; + m_pszFileName = NULL; + m_eBlkType = ACCESS_BTREE_ROOT; + m_uiEntryOvhd = 4; + m_uiLRUCount = 1; + m_uiLevels = 1; + m_uiNewBlkAddr = 0; + m_uiHighestWrittenBlkAddr = 0; + m_uiTotalEntries = 0; + + // Initialize the cache blocks. + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + m_CacheBlks[i].uiBlkAddr = 0xFFFFFFFF; + m_CacheBlks[i].uiLRUValue = 0; + m_CacheBlks[i].pBlk = NULL; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FBtreeRoot::~FBtreeRoot() +{ + int i; + + closeFile(); + + for( i = 0; i < FBTREE_CACHE_BLKS; i++) + { + if( m_CacheBlks[i].pBlk) + { + m_CacheBlks[i].pBlk->Release(); + } + } +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE FBtreeLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + reset( ACCESS_BTREE_LEAF); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE FBtreeNonLeaf::setup( + FLMUINT uiEntrySize) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_NON_LEAF); + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate structures and set entry size. +****************************************************************************/ +RCODE FBtreeRoot::setup( + FLMUINT uiEntrySize, + char * pszFileName) +{ + RCODE rc; + + if (RC_BAD( rc = f_calloc( DYNSSET_BLOCK_SIZE, &m_pucBlkBuf))) + { + goto Exit; + } + + m_uiEntrySize = uiEntrySize; + m_pvUserData = (void *) uiEntrySize; + reset( ACCESS_BTREE_ROOT); + m_pszFileName = pszFileName; + nextBlk( FBTREE_END); + prevBlk( FBTREE_END); + lemBlk( FBTREE_END); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup the block as a new block +****************************************************************************/ +void FBtreeBlk::reset( + FBlkTypes eBlkType) +{ + m_eBlkType = eBlkType; + + if (eBlkType == ACCESS_BTREE_ROOT || eBlkType == ACCESS_BTREE_NON_LEAF) + { + m_uiEntryOvhd = 4; + } + else + { + m_uiEntryOvhd = 0; + } + m_uiNumSlots = (DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)) / + ( m_uiEntrySize + m_uiEntryOvhd); + entryCount( 0); + m_uiPosition = DYNSSET_POSITION_NOT_SET; + m_bDirty = FALSE; +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE FBtreeBlk::getNext( + void * pvEntryBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPos = m_uiPosition; + + // Position to the next/first entry. + + if (uiPos == DYNSSET_POSITION_NOT_SET) + { + uiPos = 0; + } + else + { + if (++uiPos > entryCount()) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + f_memcpy( pvEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the last entry in the result set. +****************************************************************************/ +RCODE FBtreeBlk::getLast( + void * pvEntryBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPos = entryCount(); + + // Position to the next/first entry. + + if (uiPos == 0) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + uiPos--; + f_memcpy( pvEntryBuffer, ENTRY_POS(uiPos), m_uiEntrySize); + m_uiPosition = uiPos; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Search a btree. Position for get* or for insert. +****************************************************************************/ +RCODE FBtreeRoot::search( + void * pvEntry, + void * pvFoundEntry) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurLevel = m_uiLevels - 1; // Min 2 levels + FLMUINT uiBlkAddr; + + // Reset the stack - only needed for debugging. + //f_memset( m_BTStack, 0, sizeof(FBtreeBlk *) * FBTREE_MAX_LEVELS); + + // Search this root block. + + m_BTStack[ uiCurLevel] = this; + (void) searchEntry( pvEntry, &uiBlkAddr); + + while( uiCurLevel--) + { + // Read the next block and place at uiCurLevel (backwards from FS). + + if( RC_BAD( rc = readBlk( uiBlkAddr, + uiCurLevel ? ACCESS_BTREE_NON_LEAF : ACCESS_BTREE_LEAF, + &m_BTStack[ uiCurLevel] ))) + { + goto Exit; + } + + // Set the rc - only for the leaf block, otherwise rc should be ignored. + + rc = m_BTStack[ uiCurLevel]->searchEntry( pvEntry, &uiBlkAddr, + uiCurLevel ? NULL : pvFoundEntry); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Search a single block tree. Position for get* or for insert. + Do a binary search on all of the entries to find a match. + If no match then position to the entry where an insert + will take place. +****************************************************************************/ +RCODE FBtreeBlk::searchEntry( + void * pvEntry, + FLMUINT * puiChildAddr, + void * pvFoundEntry) +{ + RCODE rc = RC_SET( NE_XFLM_NOT_FOUND); + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblSize; + FLMINT iCompare; + + // check for zero entries. + + if (!entryCount()) + { + uiMid = 0; + goto Exit; + } + uiHigh = uiTblSize = entryCount() - 1; + uiLow = 0; + for(;;) + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + // Use compare routine + + if (m_fnCompare) + { + iCompare = m_fnCompare( pvEntry, ENTRY_POS( uiMid), m_pvUserData); + } + else + { + iCompare = f_memcmp( pvEntry, ENTRY_POS( uiMid), m_uiEntrySize); + } + + if (iCompare == 0) + { + if (pvFoundEntry) + { + f_memcpy( pvFoundEntry, ENTRY_POS( uiMid), m_uiEntrySize); + } + rc = NE_XFLM_OK; + goto Exit; + } + + // Check if we are done - where wLow equals uiHigh or mid is at end. + + if (iCompare < 0) + { + if (uiMid == uiLow || uiLow == uiHigh) + { + break; + } + uiHigh = uiMid - 1; // Too high + } + else + { + if (uiMid == uiHigh || uiLow == uiHigh) + { + + // Go up one for the correct position? + + uiMid++; + break; + } + uiLow = uiMid + 1; // Too low + } + } + +Exit: + + m_uiPosition = uiMid; + if (puiChildAddr && blkType() != ACCESS_BTREE_LEAF) + { + if (uiMid == entryCount()) + { + *puiChildAddr = lemBlk(); + } + else + { + FLMBYTE * pucChildAddr = ENTRY_POS(uiMid) + m_uiEntrySize; + *puiChildAddr = (FLMUINT)FB2UD( pucChildAddr); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the btree - should be positioned +****************************************************************************/ +RCODE FBtreeRoot::insert( + void * pvEntry) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurLevel; + FLMBYTE ucEntryBuf[FBTREE_MAX_LEVELS][DYNSSET_MAX_FIXED_ENTRY_SIZE]; + FLMUINT uiNewBlkAddr; + + if (RC_OK( rc = m_BTStack[0]->insert( pvEntry))) + { + goto Exit; + } + + // Failed to input at the left level. Do block split(s). + // This is an iterative and NOT a recursive split algorithm. + // The debugging, and cases to test should be lots easier this way. + + f_memcpy( ucEntryBuf[0], pvEntry, m_uiEntrySize); + uiCurLevel = 0; + uiNewBlkAddr = FBTREE_END; + for(;;) + { + + // Split while adding the element. + + if (RC_BAD( rc = (m_BTStack[uiCurLevel])->split( + this, + ucEntryBuf[ uiCurLevel], + uiNewBlkAddr, + ucEntryBuf[ uiCurLevel+1], + &uiNewBlkAddr))) + { + goto Exit; + } + + uiCurLevel++; + flmAssert( uiCurLevel < m_uiLevels); + + if (RC_OK( rc = m_BTStack[uiCurLevel]->insertEntry( + ucEntryBuf[uiCurLevel], uiNewBlkAddr))) + { + goto Exit; + } + + // Only returns NE_XFLM_OK or FAILURE. + + // Root split? + + if (uiCurLevel + 1 == m_uiLevels) + { + flmAssert( m_uiLevels + 1 <= FBTREE_MAX_LEVELS); + if (m_uiLevels + 1 > FBTREE_MAX_LEVELS) + { + rc = RC_SET( NE_XFLM_BTREE_FULL); + goto Exit; + } + + // Need to split the root block. + rc = ((FBtreeRoot *)m_BTStack[uiCurLevel])->split( + ucEntryBuf[uiCurLevel], uiNewBlkAddr ); + break; + } + } + +Exit: + + if (RC_OK(rc)) + { + m_uiTotalEntries++; + } + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE FBtreeBlk::insertEntry( + void * pvEntry, + FLMUINT uiChildAddr) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucCurEntry; + FLMUINT uiShiftBytes; // Always shift down + + if( entryCount() >= m_uiNumSlots) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + flmAssert( m_uiPosition != DYNSSET_POSITION_NOT_SET); + pucCurEntry = ENTRY_POS( m_uiPosition); + if ((uiShiftBytes = (entryCount() - m_uiPosition) * + (m_uiEntrySize + m_uiEntryOvhd)) != 0) + { + + // Big hairy assert. Finds coding bugs and corruptions. + + flmAssert( m_uiPosition * (m_uiEntrySize + m_uiEntryOvhd) + + uiShiftBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + + f_memmove( pucCurEntry + m_uiEntrySize + m_uiEntryOvhd, + pucCurEntry, uiShiftBytes); + } + f_memcpy( pucCurEntry, pvEntry, m_uiEntrySize); + if( m_uiEntryOvhd) + { + UD2FBA( (FLMUINT32)uiChildAddr, &pucCurEntry[m_uiEntrySize]); + } + entryCount( entryCount() + 1); + m_uiPosition++; + m_bDirty = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Move first half of entries into new block. Reset previous block + to point to new block. Add new last entry in new block to parent. + Fixup prev/next linkages. +****************************************************************************/ +RCODE FBtreeBlk::split( + FBtreeRoot * pRoot, + FLMBYTE * pucCurEntry, // (in) Contains entry to insert + FLMUINT uiCurBlkAddr, // (in) Blk addr if non-leaf + FLMBYTE * pucParentEntry, // (out) Entry to insert into parent. + FLMUINT * puiNewBlkAddr) // (out) New blk addr to insert into parent. +{ + RCODE rc = NE_XFLM_OK; + FBtreeBlk * pPrevBlk; + FBtreeBlk * pNewBlk = NULL; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucMidEntry; + FLMBYTE * pucChildAddr; + FLMUINT uiChildAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiMid; + FLMUINT uiPos; + FLMUINT uiMoveBytes; + FLMBOOL bInserted = FALSE; + + // Allocate a new block for the split. + + if (RC_BAD( rc = pRoot->newBlk( &pNewBlk, blkType() ))) + { + goto Exit; + } + pNewBlk->AddRef(); // Pin the block - may get flushed out. + + + // the last half into the new block, but that would force the parent + // entry to be changed. This may take a little longer, but it is much + // more easier to code. + + // Call search entry once just to setup for insert. + + (void) pNewBlk->searchEntry( ENTRY_POS( 0)); + + // get the count and move more then half into the new block. + + uiMid = (entryCount() + 5) >> 1; + uiChildAddr = FBTREE_END; + + for (uiPos = 0; uiPos < uiMid; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + if (blkType() != ACCESS_BTREE_LEAF) + { + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + } + + // m_uiPosition automatically gets incremented. + + if (RC_BAD( rc = pNewBlk->insertEntry( pucEntry, uiChildAddr))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + } + + if (m_uiPosition < uiMid) + { + + // Insert this entry now + + bInserted = TRUE; + (void) pNewBlk->searchEntry( pucCurEntry); + if (RC_BAD( rc = pNewBlk->insertEntry( pucCurEntry, uiCurBlkAddr))) + { + goto Exit; + } + } + + // Let caller insert into parent entry. This rids us of recursion. + + f_memcpy( pucParentEntry, pucEntry, m_uiEntrySize); + + // Move the rest down + + pucEntry = ENTRY_POS( 0); + pucMidEntry = ENTRY_POS( uiMid); + + entryCount( entryCount() - uiMid); + uiMoveBytes = entryCount() * (m_uiEntrySize + m_uiEntryOvhd); + flmAssert( uiMoveBytes < DYNSSET_BLOCK_SIZE - sizeof( FixedBlkHdr)); + f_memmove( pucEntry, pucMidEntry, uiMoveBytes); + + if( !bInserted) + { + + // m_uiPosition -= uiMid; + + (void) searchEntry( pucCurEntry); + if (RC_BAD( rc = insertEntry( pucCurEntry, uiCurBlkAddr))) + { + goto Exit; + } + } + + // VISIT: Could position stack to point to current element to insert. + + // Fixup the prev/next block linkages. + + if (prevBlk() != FBTREE_END) + { + if (RC_BAD( rc = pRoot->readBlk( prevBlk(), blkType(), &pPrevBlk ))) + { + goto Exit; + } + + pPrevBlk->nextBlk( pNewBlk->blkAddr()); + uiPrevBlkAddr = pPrevBlk->blkAddr(); + } + else + { + uiPrevBlkAddr = FBTREE_END; + } + pNewBlk->prevBlk( uiPrevBlkAddr); + pNewBlk->nextBlk( blkAddr()); + prevBlk( pNewBlk->blkAddr()); + + *puiNewBlkAddr = pNewBlk->blkAddr(); + +Exit: + + if (pNewBlk) + { + pNewBlk->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Reinsert all entries given a new root block. + Caller will release 'this'. Used ONLY for building the first + ROOT and two leaves of the tree. +****************************************************************************/ +RCODE FBtreeLeaf::split( + FBtreeRoot * pNewRoot) // New Non-leaf root +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEntry; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if (RC_BAD( rc = pNewRoot->setupTree( ENTRY_POS(uiMid), + ACCESS_BTREE_LEAF, NULL, NULL))) + { + goto Exit; + } + + for (uiPos = 0; uiPos < uiEntryCount; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + if ((rc = pNewRoot->search( pucEntry)) != NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = pNewRoot->insert( pucEntry))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Split the root block and make two new non-leaf blocks. + The secret here is that the root block never moves (cheers!). + This takes a little longer but is worth the work because the + root block never goes out to disk and is not in the cache. +****************************************************************************/ +RCODE FBtreeRoot::split( + void * pvCurEntry, + FLMUINT uiCurChildAddr) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEntry; + FLMBYTE * pucChildAddr; + FBtreeBlk * pLeftBlk; + FBtreeBlk * pRightBlk; + FBtreeBlk * pBlk; + FLMUINT uiChildAddr; + FLMUINT uiPos; + FLMUINT uiEntryCount = entryCount(); + FLMUINT uiMid = (uiEntryCount + 1) >> 1; + + if (RC_BAD( rc = setupTree( NULL, ACCESS_BTREE_NON_LEAF, + &pLeftBlk, &pRightBlk))) + { + goto Exit; + } + + // Call search entry once just to setup for insert. + + (void) pLeftBlk->searchEntry( ENTRY_POS( 0)); + + // Take the entries from the root block and move into leafs. + + for (uiPos = 0; uiPos <= uiMid; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + + if (RC_BAD( rc = pLeftBlk->insertEntry( pucEntry, uiChildAddr))) + { + goto Exit; + } + } + + // Call search entry once just to setup for insert. + + (void) pRightBlk->searchEntry( ENTRY_POS( 0)); + + for (uiPos = uiMid + 1; uiPos < uiEntryCount; uiPos++) + { + pucEntry = ENTRY_POS( uiPos); + pucChildAddr = pucEntry + m_uiEntrySize; + uiChildAddr = (FLMUINT)FB2UD(pucChildAddr); + + if ((rc = pRightBlk->searchEntry( pucEntry )) != NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = pRightBlk->insertEntry( pucEntry, uiChildAddr))) + { + goto Exit; + } + } + + // Reset the root block and insert new midpoint. + + entryCount( 0); + lemBlk( pRightBlk->blkAddr()); // Duplicated just in case. + pucEntry = ENTRY_POS( uiMid); + + if ((rc = searchEntry( pucEntry )) != NE_XFLM_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = insertEntry( pucEntry, pLeftBlk->blkAddr() ))) + { + goto Exit; + } + + // Insert the current entry (parameters) into the left or right blk. + // This could be done a number of different ways. + (void) searchEntry( pvCurEntry, &uiChildAddr); + if (RC_BAD( rc = readBlk( uiChildAddr, ACCESS_BTREE_NON_LEAF, &pBlk))) + { + goto Exit; + } + (void) pBlk->searchEntry( pvCurEntry); + if (RC_BAD( rc = pBlk->insertEntry( pvCurEntry, uiCurChildAddr))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup two child blocks for a root block. +****************************************************************************/ +RCODE FBtreeRoot::setupTree( + FLMBYTE * pucMidEntry, // If !NULL entry to insert into root. + FBlkTypes eBlkType, // Leaf or non-leaf + FBtreeBlk ** ppLeftBlk, // (out) + FBtreeBlk ** ppRightBlk) // (out) +{ + RCODE rc = NE_XFLM_OK; + FBtreeBlk * pLeftBlk = NULL; + FBtreeBlk * pRightBlk = NULL; + + if (RC_BAD( rc = newBlk( &pLeftBlk, eBlkType))) + { + goto Exit; + } + + if (RC_BAD( rc = newBlk( &pRightBlk, eBlkType))) + { + goto Exit; + } + + if (eBlkType == ACCESS_BTREE_NON_LEAF) + { + ((FBtreeNonLeaf *)pRightBlk)->lemBlk( lemBlk()); + } + + // Fix up the linkages + + pLeftBlk->nextBlk( pRightBlk->blkAddr()); + pRightBlk->prevBlk( pLeftBlk->blkAddr()); + lemBlk( pRightBlk->blkAddr()); + + if (pucMidEntry) + { + + // Add the midentry to the root block. Search to position and insert. + + searchEntry( pucMidEntry); + insertEntry( pucMidEntry, pLeftBlk->blkAddr()); + } + m_uiLevels++; + + if (ppLeftBlk) + { + *ppLeftBlk = pLeftBlk; + } + if (ppRightBlk) + { + *ppRightBlk = pRightBlk; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read in the block or get it from the cache. +****************************************************************************/ +RCODE FBtreeRoot::readBlk( + FLMUINT uiBlkAddr, // Blk address to read + FBlkTypes eBlkType, // Expected access type to read + FBtreeBlk ** ppBlk) // (out) Return block +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPos; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiLRUPos = 0; + FBtreeBlk * pNewBlk; + + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if (m_CacheBlks[uiPos].uiBlkAddr == uiBlkAddr) + { + goto Exit; + } + + // The ref count is used for pinning the block. + + if (m_CacheBlks[uiPos].pBlk && + m_CacheBlks[uiPos].pBlk->getRefCount() == 1 && + uiLRUValue > m_CacheBlks[uiPos].uiLRUValue) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + + // There better not be a hole by this point. + + flmAssert( m_CacheBlks[uiPos].pBlk != NULL); + } + uiPos = uiLRUPos; + + // Read from disk? + + flmAssert( m_pFileHdl != NULL); + + if (RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, eBlkType))) + { + goto Exit; + } + + // Pick the LRU block and make that object do the reading + // so it can reset all internals and get used to being a different blk. + + pNewBlk->blkAddr( uiBlkAddr); + m_CacheBlks[uiPos].uiBlkAddr = uiBlkAddr; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + if (RC_BAD( rc = pNewBlk->readBlk( m_pFileHdl, uiBlkAddr))) + { + + // Release the block because the reset() changed the object type. + // May hit the assert above. + + m_CacheBlks[uiPos].pBlk->Release(); + m_CacheBlks[uiPos].pBlk = NULL; + goto Exit; + } + +Exit: + + if (RC_OK(rc)) + { + *ppBlk = m_CacheBlks[uiPos].pBlk; + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + } + return( rc); +} + +/**************************************************************************** +Desc: Get a new block using an exising or newly allocated block from + the cache. Initializes the block. May be leaf or non-leaf + but NOT the root block. +****************************************************************************/ +RCODE FBtreeRoot::newBlk( + FBtreeBlk ** ppBlk, + FBlkTypes eBlkType) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLRUValue = (FLMUINT)~0; + FLMUINT uiPos; + FLMUINT uiLRUPos = 0; + FBtreeBlk * pNewBlk; + + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + + // The ref count is used for pinning the block. + + if (getRefCount() == 1 && + uiLRUValue > m_CacheBlks[uiPos].uiLRUValue) + { + uiLRUValue = m_CacheBlks[uiPos].uiLRUValue; + uiLRUPos = uiPos; + } + + // use the first hole. + + if (m_CacheBlks[uiPos].pBlk == NULL) + { + uiLRUPos = uiPos; + break; + } + } + uiPos = uiLRUPos; + if (RC_BAD( rc = newCacheBlk( uiPos, &pNewBlk, eBlkType))) + { + goto Exit; + } + + pNewBlk->blkAddr( newBlkAddr()); + m_CacheBlks[uiPos].uiBlkAddr = pNewBlk->blkAddr(); + m_CacheBlks[uiPos].uiLRUValue = m_uiLRUCount++; + + pNewBlk->entryCount(0); + pNewBlk->lemBlk( FBTREE_END); + pNewBlk->nextBlk( FBTREE_END); + pNewBlk->prevBlk( FBTREE_END); + *ppBlk = pNewBlk; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Release the existing cache block and setup and alloc new blk. +****************************************************************************/ +RCODE FBtreeRoot::newCacheBlk( + FLMUINT uiCachePos, + FBtreeBlk ** ppBlk, + FBlkTypes eBlkType) +{ + RCODE rc = NE_XFLM_OK; + FBtreeBlk * pNewBlk = NULL; + + if (m_CacheBlks[uiCachePos].pBlk) + { + if (m_CacheBlks[uiCachePos].pBlk->isDirty()) + { + if (RC_BAD( rc = writeBlk( uiCachePos))) + { + goto Exit; + } + } + } + + if (m_CacheBlks[uiCachePos].pBlk != NULL && + m_CacheBlks[uiCachePos].pBlk->blkType() == eBlkType) + { + + // If block is of the same type then reset it and use it. + + pNewBlk = m_CacheBlks[uiCachePos].pBlk; + pNewBlk->reset( eBlkType); + *ppBlk = pNewBlk; + goto Exit; + } + + if (m_CacheBlks[uiCachePos].pBlk) + { + m_CacheBlks[uiCachePos].pBlk->Release(); + } + if (eBlkType == ACCESS_BTREE_LEAF) + { + FBtreeLeaf * pLeafBlk; + if ((pLeafBlk = f_new FBtreeLeaf) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pLeafBlk->setup( m_uiEntrySize))) + { + pLeafBlk->Release(); + goto Exit; + } + pLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); + pNewBlk = (FBtreeBlk *) pLeafBlk; + } + else + { + FBtreeNonLeaf * pNonLeafBlk; + if ((pNonLeafBlk = f_new FBtreeNonLeaf) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pNonLeafBlk->setup( m_uiEntrySize))) + { + pNonLeafBlk->Release(); + goto Exit; + } + pNonLeafBlk->setCompareFunc( m_fnCompare, m_pvUserData); + pNewBlk = (FBtreeBlk *) pNonLeafBlk; + } + m_CacheBlks[uiCachePos].pBlk = pNewBlk; + *ppBlk = pNewBlk; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read the block from disk. +****************************************************************************/ +RCODE FBtreeBlk::readBlk( + IF_FileHdl * pFileHdl, + FLMUINT uiBlkAddr) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + + if (RC_BAD( rc = pFileHdl->Read( + uiBlkAddr * DYNSSET_BLOCK_SIZE, DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, &uiBytesRead))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if (blkAddr() != uiBlkAddr) + { + + // Most likely a coding error rather than an I/O error. + + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write the block to disk. +****************************************************************************/ +RCODE FBtreeBlk::writeBlk( + IF_FileHdl * pFileHdl) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesWritten; + FLMUINT uiBlkAddr = blkAddr(); + + if (RC_BAD( rc = pFileHdl->Write( + uiBlkAddr * DYNSSET_BLOCK_SIZE, + DYNSSET_BLOCK_SIZE, + m_pucBlkBuf, + &uiBytesWritten))) + { + goto Exit; + } + + m_bDirty = FALSE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write all blocks that are dirty and have an addrees lower + than the input block address and then write this block. + Write in order so that + we don't have to write zeros for any block. +****************************************************************************/ +RCODE FBtreeRoot::writeBlk( + FLMUINT uiWritePos) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPos; + FLMUINT uiHighBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + + if (!m_pFileHdl) + { + if (RC_BAD( rc = openFile())) + { + goto Exit; + } + } + for (uiPos = 0; uiPos < FBTREE_CACHE_BLKS; uiPos++) + { + if( (uiWritePos != uiPos) && + (m_CacheBlks[uiPos].pBlk) && + (m_CacheBlks[uiPos].uiBlkAddr >= m_uiHighestWrittenBlkAddr) && + (m_CacheBlks[uiPos].uiBlkAddr < uiHighBlkAddr) && + (m_CacheBlks[uiPos].pBlk->isDirty()) ) + { + + // Recursive call to write out lower blocks if needed. + + if (RC_BAD( rc = writeBlk( uiPos))) + { + goto Exit; + } + } + } + m_CacheBlks[ uiWritePos].pBlk->writeBlk( m_pFileHdl); + if (m_CacheBlks[uiWritePos].uiBlkAddr > m_uiHighestWrittenBlkAddr) + { + m_uiHighestWrittenBlkAddr = m_CacheBlks[uiWritePos].uiBlkAddr; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Close the file if previously opened and creates the file. +****************************************************************************/ +RCODE FBtreeRoot::openFile( void) +{ + RCODE rc = NE_XFLM_OK; + + if (!m_pFileHdl) + { + rc = gv_pFileSystem->CreateUnique( m_pszFileName, + FRSET_FILENAME_EXTENSION, + XFLM_IO_RDWR | XFLM_IO_CREATE_DIR, &m_pFileHdl); + } + return( rc); +} + +/**************************************************************************** +Desc: Closes and deletes the temporary file. +****************************************************************************/ +void FBtreeRoot::closeFile( void) +{ + if (m_pFileHdl) + { + m_pFileHdl->Close(); + gv_pFileSystem->Delete( m_pszFileName); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } +} diff --git a/version5/src/fdynbuf.cpp b/version5/src/fdynbuf.cpp new file mode 100644 index 0000000..9f1aede --- /dev/null +++ b/version5/src/fdynbuf.cpp @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the routines for the Flaim Dynamic Buffer Class. +// +// 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: fdynbuf.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fdynbuf.h" + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +RCODE F_DynamicBuffer::addChar( + FLMBYTE ucCharacter) +{ + RCODE rc = NE_XFLM_OK; + + if (!m_bSetup) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + + } + + f_mutexLock( m_hMutex); + + // Is there room for just one more character plus a terminator? + if ((m_uiBuffSize - m_uiUsedChars) > 1) + { + m_psBuffer[m_uiUsedChars++] = ucCharacter; + m_psBuffer[m_uiUsedChars] = 0; + } + else + { + // Allocate a new buffer or increase the size of the existing one. + if( !m_uiBuffSize) + { + if( RC_BAD( rc = f_alloc( 50, &m_psBuffer))) + { + goto Exit; + } + m_uiBuffSize = 50; + } + else + { + if( RC_BAD( rc = f_realloc( m_uiBuffSize + 50, &m_psBuffer))) + { + goto Exit; + } + m_uiBuffSize += 50; + } + + + m_psBuffer[m_uiUsedChars++] = ucCharacter; + m_psBuffer[m_uiUsedChars] = 0; + } + +Exit: + + if ( m_bSetup) + { + f_mutexUnlock( m_hMutex); + } + + return rc; +} + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +RCODE F_DynamicBuffer::addString( const char * pszString) +{ + RCODE rc = NE_XFLM_OK; + const char * pTemp = pszString; + FLMUINT uiTmpPos = m_uiUsedChars; + + + while( *pTemp) + { + if (RC_BAD( rc = addChar( (FLMBYTE)*pTemp))) + { + // Reset the buffer to its state prior to this call. + m_uiUsedChars = uiTmpPos; + if (m_uiBuffSize > 0) + { + m_psBuffer[ m_uiUsedChars] = 0; + } + goto Exit; + } + pTemp++; + } + +Exit: + + return rc; +} + +/****************************************************************** +Desc: Implements the addChar function of the DynamicBuffer class +*******************************************************************/ +const char * F_DynamicBuffer::printBuffer() +{ + return (char *)m_psBuffer; +} diff --git a/version5/src/fdynbuf.h b/version5/src/fdynbuf.h new file mode 100644 index 0000000..5b16ebb --- /dev/null +++ b/version5/src/fdynbuf.h @@ -0,0 +1,85 @@ +//------------------------------------------------------------------------------ +// Desc: Dynamic buffer +// +// 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: fdynbuf.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +/**************************************************************************** +Desc: Utility class to help prepare a character buffer for printing when the + output buffer size is not known in advance. +*****************************************************************************/ +class F_DynamicBuffer : public XF_RefCount, public XF_Base +{ +public: + + F_DynamicBuffer() + { + m_bSetup = FALSE; + m_psBuffer = NULL; + m_uiBuffSize = 0; + m_uiUsedChars = 0; + if (RC_OK( f_mutexCreate( &m_hMutex))) + { + m_bSetup = TRUE; + } + } + + ~F_DynamicBuffer() + { + f_free( &m_psBuffer); + m_psBuffer = NULL; + m_uiBuffSize = 0; + m_uiUsedChars = 0; + if (m_bSetup) + { + f_mutexDestroy( &m_hMutex); + m_bSetup = FALSE; + } + } + + RCODE addChar( FLMBYTE ucCharacter); + + RCODE addString( const char * pszString); + + const char * printBuffer(); + + FLMUINT getBufferSize( void) + { + return m_uiUsedChars; + } + + void reset( void) + { + f_free( &m_psBuffer); + m_psBuffer = NULL; + m_uiBuffSize = 0; + m_uiUsedChars = 0; + } + +private: + + FLMBOOL m_bSetup; + FLMBYTE * m_psBuffer; + FLMUINT m_uiBuffSize; + FLMUINT m_uiUsedChars; + F_MUTEX m_hMutex; +}; diff --git a/version5/src/fdynsset.cpp b/version5/src/fdynsset.cpp new file mode 100644 index 0000000..b229603 --- /dev/null +++ b/version5/src/fdynsset.cpp @@ -0,0 +1,328 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for FDynSearchSet 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: fdynsset.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fdynsset.h" + +#define HASH_POS(vp) \ + (((FLMUINT)(FB2UD((FLMBYTE*)vp)) % m_uiNumSlots) * m_uiEntrySize) + +static const FLMBYTE ucZeros [ DYNSSET_MAX_FIXED_ENTRY_SIZE ] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/**************************************************************************** +Desc: Setup the result set with input values. This method must be + called and only called once. +****************************************************************************/ +RCODE FDynSearchSet::setup( + char * pszTmpDir, + FLMUINT uiEntrySize) +{ + RCODE rc = NE_XFLM_OK; + FHashBlk * pHashBlk; + + // Set the input variables. + + if( pszTmpDir ) + { + f_strcpy( m_szFileName, pszTmpDir); // Dest <- src + } + else + { + f_memset( &m_szFileName, 0, F_PATH_MAX_SIZE); + } + m_uiEntrySize = uiEntrySize; + + if ((pHashBlk = f_new FHashBlk) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + pHashBlk->setup( uiEntrySize); + m_pAccess = (FFixedBlk *) pHashBlk; + m_pvUserData = (void *) uiEntrySize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add a fixed length entry to the dynamic search result set. +****************************************************************************/ +RCODE FDynSearchSet::addEntry( + void * pvEntry) +{ + RCODE rc = NE_XFLM_OK; + +Add_Again: + + if (RC_OK( rc = m_pAccess->search( pvEntry))) + { + rc = RC_SET( NE_XFLM_EXISTS); + } + else if (rc == NE_XFLM_NOT_FOUND) + { + + // Insert the entry. + + if ((rc = m_pAccess->insert( pvEntry)) == NE_XFLM_FAILURE) + { + // Find the type of access method implemented + + if (m_pAccess->blkType() == ACCESS_HASH) + { + FBtreeLeaf * pBtreeBlk; + FLMBYTE ucEntryBuffer[ DYNSSET_MAX_FIXED_ENTRY_SIZE]; + + // Go from a hash to a b-tree object. Alloc and move stuff over. + + if ((pBtreeBlk = f_new FBtreeLeaf) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + pBtreeBlk->setup( m_uiEntrySize); + pBtreeBlk->setCompareFunc( m_fnCompare, m_pvUserData); + for( rc = m_pAccess->getFirst( ucEntryBuffer ); + RC_OK(rc); + rc = m_pAccess->getNext( ucEntryBuffer) ) + { + // Call search to setup for insert. + (void) pBtreeBlk->search( ucEntryBuffer); + if (RC_BAD( rc = pBtreeBlk->insert( ucEntryBuffer))) + { + pBtreeBlk->Release(); + goto Exit; + } + } + rc = NE_XFLM_OK; + m_pAccess->Release(); + m_pAccess = pBtreeBlk; + goto Add_Again; + } + else if( m_pAccess->blkType() == ACCESS_BTREE_LEAF) + { + FBtreeRoot * pFullBtree; + + // Go from 1 block to 3 changing root blocks and free m_pAccess + // All new splits will be taken care of automatically. + + if ((pFullBtree = f_new FBtreeRoot) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if( RC_BAD( rc = pFullBtree->setup( m_uiEntrySize, m_szFileName))) + { + pFullBtree->Release(); + goto Exit; + } + pFullBtree->setCompareFunc( m_fnCompare, m_pvUserData); + if (RC_BAD( rc = ((FBtreeLeaf *)m_pAccess)->split( pFullBtree))) + { + goto Exit; + } + m_pAccess->Release(); + m_pAccess = pFullBtree; + goto Add_Again; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Find matching entry. Position for Get* or for insert. +****************************************************************************/ +RCODE FHashBlk::search( + void * pvEntry, + void * pvFoundEntry) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiHashPos = HASH_POS( pvEntry); + FLMINT iCompare; + + for (;;) + { + + // If all zeros then setup to insert at this position. + + if (!f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + if (m_fnCompare) + { + iCompare = m_fnCompare( pvEntry, &m_pucBlkBuf[ uiHashPos], + m_pvUserData); + } + else + { + iCompare = f_memcmp( pvEntry, &m_pucBlkBuf[ uiHashPos], + m_uiEntrySize); + } + + if (iCompare == 0) + { + + // Found match. + + if (pvFoundEntry) + { + f_memcpy( pvFoundEntry, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + } + break; + } + + // Go to the next entry + + uiHashPos += m_uiEntrySize; + if (uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + { + uiHashPos = 0; + } + } + +Exit: + + m_uiPosition = uiHashPos; + return( rc); +} + +/**************************************************************************** +Desc: Insert the entry into the buffer. +****************************************************************************/ +RCODE FHashBlk::insert( + void * pvEntry) +{ + RCODE rc = NE_XFLM_OK; + + if( getTotalEntries() > ((m_uiNumSlots * 7) / 10)) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + f_memcpy( &m_pucBlkBuf[ m_uiPosition], pvEntry, m_uiEntrySize); + m_uiTotalEntries++; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the next entry in the result set. If the result set + is not positioned then the first entry will be returned. +****************************************************************************/ +RCODE FHashBlk::getNext( + void * pvEntryBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + uiHashPos = 0; + } + else + { + uiHashPos = m_uiPosition + m_uiEntrySize; + } + + for ( ; ; uiHashPos += m_uiEntrySize) + { + if (uiHashPos >= DYNSSET_HASH_BUFFER_SIZE) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // If all zeros then setup to insert at this position. + + if (f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + } + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Returns the last entry in the result set. +****************************************************************************/ +RCODE FHashBlk::getLast( + void * pvEntryBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiHashPos; + + // Position to the next/first entry. + + uiHashPos = DYNSSET_HASH_BUFFER_SIZE; + + for( ; ; ) + { + uiHashPos -= m_uiEntrySize; + + // If all zeros then setup to insert at this position. + + if (f_memcmp( &m_pucBlkBuf[ uiHashPos], ucZeros, m_uiEntrySize)) + { + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ uiHashPos], m_uiEntrySize); + m_uiPosition = uiHashPos; + goto Exit; + } + if (uiHashPos == 0) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/version5/src/fdynsset.h b/version5/src/fdynsset.h new file mode 100644 index 0000000..a00da37 --- /dev/null +++ b/version5/src/fdynsset.h @@ -0,0 +1,672 @@ +//------------------------------------------------------------------------------ +// Desc: FLAIM Dynamic search result set class. +// +// Tabs: 3 +// +// Copyright (c) 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: fdynsset.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FDYNSSET_H +#define FDYNSSET_H + +/***************************************************************************** +***** +** Definitions +***** +*****************************************************************************/ + +/* +A block size of 8K will perform well in minimizing the number of reads +to obtain a block. A 6K may perform better if the file is located +across the network. +*/ + +#define DYNSSET_BLOCK_SIZE 0x4000 +#define DYNSSET_HASH_BUFFER_SIZE 0x2000 +#define DYNSSET_MIN_FIXED_ENTRY_SIZE 4 +// Change ucZeros in fdynsset.cpp if this changes. +#define DYNSSET_MAX_FIXED_ENTRY_SIZE 32 +#define DYNSSET_POSITION_NOT_SET 0xFFFFFFFF + +#define FBTREE_CACHE_BLKS 32 +#define FBTREE_END 0xFFFFFFFF +#define FBTREE_MAX_LEVELS 4 + +typedef int (* FDYNSET_COMPARE_FUNC)( + void * pvData1, + void * pvData2, + void * pvUserData); + + +/***************************************************************************** +***** +** Forward References +***** +*****************************************************************************/ + +class FDynSearchSet; +class FHashBlk; +class FBtreeBlk; +class FBtreeRoot; +class FBtreeNonLeaf; +class FBtreeLeaf; + +enum eBlkTypes +{ + ACCESS_HASH, + ACCESS_BTREE_LEAF, + ACCESS_BTREE_ROOT, + ACCESS_BTREE_NON_LEAF +}; +typedef enum eBlkTypes FBlkTypes; + + +/*=========================================================================== + Virtual FFixedBlk +===========================================================================*/ +class FFixedBlk : public XF_RefCount, public XF_Base +{ +public: + + FFixedBlk(); + ~FFixedBlk() + { + } + + /* virtual methods that must be implemented */ + + FBlkTypes blkType() + { + return m_eBlkType; + } + + virtual RCODE getCurrent( + void * pvEntryBuffer) = 0; + + virtual RCODE getFirst( + void * pvEntryBuffer) = 0; + + virtual RCODE getLast( + void * pvEntryBuffer) = 0; + + virtual RCODE getNext( + void * pvEntryBuffer) = 0; + + virtual FLMUINT getTotalEntries() = 0; + + virtual RCODE insert( + void * pvEntry) = 0; + + FINLINE FLMBOOL isDirty( void) + { + return( m_bDirty); + } + + virtual RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL) = 0; + + void setCompareFunc( + FDYNSET_COMPARE_FUNC fnCompare, + void * pvUserData) + { + m_fnCompare = fnCompare; + m_pvUserData = pvUserData; + } + +protected: + + // Variables + + FDYNSET_COMPARE_FUNC m_fnCompare; + void * m_pvUserData; + FBlkTypes m_eBlkType; + FLMUINT m_uiEntrySize; + FLMUINT m_uiNumSlots; + FLMUINT m_uiPosition; + FLMBOOL m_bDirty; + FLMBYTE * m_pucBlkBuf; +}; + + +/***************************************************************************** +***** +** Result Set Class Definitions +** Source: fdynsset.cpp +***** +*****************************************************************************/ +class FDynSearchSet : public XF_RefCount, public XF_Base +{ + +public: + + FDynSearchSet() + { + m_fnCompare = NULL; + m_pvUserData = NULL; + m_uiEntrySize = 0; + m_pAccess = NULL; + } + + ~FDynSearchSet() + { + if (m_pAccess) + { + m_pAccess->Release(); + } + } + + RCODE setup( + char * pszTmpDir, + FLMUINT uiEntrySize); + + FINLINE void setCompareFunc( + FDYNSET_COMPARE_FUNC fnCompare, + void * pvUserData + ) + { + m_fnCompare = fnCompare; + m_pvUserData = pvUserData; + m_pAccess->setCompareFunc( fnCompare, pvUserData); + } + + RCODE addEntry( + void * pvEntry); + + FINLINE RCODE findMatch( + void * pvMatchEntry, + void * pvFoundEntry + ) + { + return m_pAccess->search( pvMatchEntry, pvFoundEntry); + } + + FINLINE FLMUINT getEntrySize( void) + { + return m_uiEntrySize; + } + + FINLINE FLMUINT getTotalEntries( void) + { + return m_pAccess->getTotalEntries(); + } + +private: + + // Variables + + FDYNSET_COMPARE_FUNC m_fnCompare; + void * m_pvUserData; + FLMUINT m_uiEntrySize; + FFixedBlk * m_pAccess; + char m_szFileName [F_PATH_MAX_SIZE]; +}; + +/*=========================================================================== + Block Header Definition + +Desc: Actually stored as the first section of each block. + We can write this structure because the same process will + read the block header i.e. portability is not a problem. +===========================================================================*/ + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiNextBlkAddr; + FLMUINT uiLEMAddr; + FLMUINT uiNumEntries; +} FixedBlkHdr; + +/*=========================================================================== + Fixed Length HASH Access Method +===========================================================================*/ +class FHashBlk : public FFixedBlk +{ +public: + + FHashBlk() + { + // Base class constructors are called before this constructor is. + + m_eBlkType = ACCESS_HASH; + m_pucBlkBuf = m_ucHashBlk; + f_memset( m_ucHashBlk, 0, sizeof( m_ucHashBlk)); + m_uiTotalEntries = 0; + } + + ~FHashBlk() + { + // Set to NULL so we don't free the block + m_pucBlkBuf = NULL; + } + + FINLINE RCODE setup( + FLMUINT uiEntrySize + ) + { + m_uiEntrySize = uiEntrySize; + m_uiNumSlots = DYNSSET_HASH_BUFFER_SIZE / uiEntrySize; + return( NE_XFLM_OK); + } + + FINLINE RCODE getCurrent( + void * pvEntryBuffer + ) + { + + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return RC_SET( NE_XFLM_NOT_FOUND); + } + + f_memcpy( pvEntryBuffer, &m_pucBlkBuf[ m_uiPosition], m_uiEntrySize); + return( NE_XFLM_OK); + } + + FINLINE RCODE getFirst( + void * pvEntryBuffer + ) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return( getNext( pvEntryBuffer)); + } + + RCODE getLast( + void * pvEntryBuffer); + + RCODE getNext( + void * pvEntryBuffer); + + FINLINE FLMUINT getTotalEntries( void) + { + return m_uiTotalEntries; + } + + RCODE insert( + void * pvEntry); + + RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL); + +private: + + FLMUINT m_uiTotalEntries; + // Allocate the hash block to save 1 allocation. + // We need to make the hash as fast as possible. + FLMBYTE m_ucHashBlk[ DYNSSET_HASH_BUFFER_SIZE]; +}; + + +/*=========================================================================== + Virtual FBtreeBlk +===========================================================================*/ + +// Leaf and non-leaf entry position. Don't do any ++ or -- ! ! ! ! + +#define ENTRY_POS(uiPos) (m_pucBlkBuf + sizeof( FixedBlkHdr) + \ + (uiPos * (m_uiEntrySize+m_uiEntryOvhd))) + +class FBtreeBlk : public FFixedBlk +{ +public: + + FBtreeBlk() + { + } + + ~FBtreeBlk() + { + if (m_pucBlkBuf) + { + f_free( &m_pucBlkBuf); + } + } + + // virtual methods that must be implemented + + FINLINE RCODE getCurrent( + void * pvEntryBuffer + ) + { + // Position to the next/first entry. + + if (m_uiPosition == DYNSSET_POSITION_NOT_SET) + { + return RC_SET( NE_XFLM_NOT_FOUND); + } + + f_memcpy( pvEntryBuffer, ENTRY_POS( m_uiPosition), m_uiEntrySize); + return( NE_XFLM_OK); + } + + FINLINE RCODE getFirst( + void * pvEntryBuffer + ) + { + m_uiPosition = DYNSSET_POSITION_NOT_SET; + return( getNext( pvEntryBuffer)); + } + + RCODE getLast( + void * pvEntryBuffer); + + RCODE getNext( + void * pvEntryBuffer); + + RCODE readBlk( + IF_FileHdl * pFileHdl, + FLMUINT uiBlkAddr); + + void reset( + FBlkTypes eBlkType); + + RCODE split( + FBtreeRoot * pParent, + FLMBYTE * pucCurEntry, + FLMUINT uiCurBlkAddr, + FLMBYTE * pucParentEntry, + FLMUINT * puiNewBlkAddr); + + RCODE writeBlk( + IF_FileHdl * pFileHdl); + + // Virtual methods + + virtual FLMUINT getTotalEntries( void) = 0; + + virtual RCODE insert( + void * pvEntry) = 0; + + virtual RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL) = 0; + + virtual RCODE searchEntry( + void * pvEntry, + FLMUINT * puiChildAddr = NULL, + void * pvFoundEntry = NULL); + + // Implemented as inline functions. + // Even though these are b-tree specific - keep them here to + // avoid having a b-tree block class. Most are not used for hash. + + FINLINE FLMUINT blkAddr( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr); + } + + FINLINE void blkAddr( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + // Get and set the number of entries in the block + + FINLINE FLMUINT entryCount( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries); + } + + FINLINE void entryCount( + FLMUINT uiNumEntries) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNumEntries = uiNumEntries; + m_bDirty = TRUE; + } + + RCODE insertEntry( + void * pvEntry, + FLMUINT uiChildAddr = FBTREE_END); + + + // Get and set the last element marker in the block. + + FINLINE FLMUINT lemBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr); + } + + FINLINE void lemBlk( + FLMUINT uiLEMAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiLEMAddr = uiLEMAddr; + m_bDirty = TRUE; + } + + // Get and set the next block address element + + FINLINE FLMUINT nextBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr); + } + + FINLINE void nextBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiNextBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + + // Get and set the previous block address element + + FINLINE FLMUINT prevBlk( void) + { + return( ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr); + } + + FINLINE void prevBlk( + FLMUINT uiBlkAddr) + { + ((FixedBlkHdr *)m_pucBlkBuf)->uiPrevBlkAddr = uiBlkAddr; + m_bDirty = TRUE; + } + +protected: + + // Variables + + FLMUINT m_uiEntryOvhd; // Overhead in the entry. +}; + + +/*=========================================================================== + Fixed Length B-tree Leaf - may be a root +===========================================================================*/ + +class FBtreeLeaf : public FBtreeBlk +{ +public: + + FBtreeLeaf() + { + m_eBlkType = ACCESS_BTREE_LEAF; + m_uiEntryOvhd = 0; + } + + ~FBtreeLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return( (FLMUINT)entryCount()); + } + + FINLINE RCODE insert( + void * pvEntry) + { + return( insertEntry( pvEntry, FBTREE_END)); + } + + FINLINE RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL) + { + return( searchEntry( pvEntry, NULL, pvFoundEntry)); + } + + RCODE split( + FBtreeRoot * pNewRoot); + +}; + +typedef struct +{ + FLMUINT uiBlkAddr; + FLMUINT uiLRUValue; + FBtreeBlk * pBlk; // Points to leaf or non-leaf block +} FBTREE_CACHE; + +/*=========================================================================== + Fixed Length B-tree non-leaf block +===========================================================================*/ + +class FBtreeNonLeaf : public FBtreeBlk +{ +public: + + FBtreeNonLeaf() + { + m_eBlkType = ACCESS_BTREE_NON_LEAF; + m_uiEntryOvhd = sizeof( FLMUINT32); + } + + ~FBtreeNonLeaf() + { + } + + RCODE setup( + FLMUINT uiEntrySize); + + FINLINE FLMUINT getTotalEntries( void) + { + return( (FLMUINT) entryCount()); + } + + FINLINE RCODE insert( + void * // pvEntry + ) + { + return( NE_XFLM_OK); + } + + FINLINE RCODE search( + void *, // pvEntry, + void * pvFoundEntry = NULL + ) + { + F_UNREFERENCED_PARM( pvFoundEntry); + flmAssert( 0); + return( NE_XFLM_OK); + } +}; + +/*=========================================================================== + Fixed Length B-tree Non-Leaf Root +===========================================================================*/ +class FBtreeRoot : public FBtreeNonLeaf +{ +public: + FBtreeRoot(); + ~FBtreeRoot(); + + RCODE setup( + FLMUINT uiEntrySize, + char * pszFileName); + + + void closeFile( void); + + FINLINE FLMUINT getTotalEntries( void) + { + return( m_uiTotalEntries); + } + + RCODE insert( + void * pvEntry); + + RCODE newBlk( + FBtreeBlk ** ppBlk, + FBlkTypes eBlkType); + + FINLINE FLMUINT newBlkAddr( void) + { + return( m_uiNewBlkAddr++); + } + + RCODE newCacheBlk( + FLMUINT uiCachePos, + FBtreeBlk ** ppBlk, + FBlkTypes eBlkType); + + RCODE openFile( void); + + RCODE readBlk( + FLMUINT uiBlkAddr, + FBlkTypes eBlkType, + FBtreeBlk ** ppBlk); + + RCODE search( + void * pvEntry, + void * pvFoundEntry = NULL); + + RCODE setupTree( + FLMBYTE * pucMidEntry, // If !NULL entry to insert into root. + FBlkTypes eBlkType, // Leaf or non-leaf + FBtreeBlk ** ppLeftBlk, // (out) + FBtreeBlk ** ppRightBlk); // (out) + + RCODE split( + void * pvCurEntry, + FLMUINT uiCurChildAddr); + + RCODE writeBlk( + FLMUINT uiWritePos); + +private: + + FLMUINT m_uiLevels; // Number of levels in the b-tree + FLMUINT m_uiTotalEntries; // Count of total entries + FLMUINT m_uiNewBlkAddr; // Next new blk addr. + FLMUINT m_uiHighestWrittenBlkAddr; + + IF_FileHdl * m_pFileHdl; // File handle or NULL if not open. + char * m_pszFileName; // File created for result set or default. + + // Cache of 'n' blocks to apply LRU algorithm. + + FLMUINT m_uiLRUCount; // Count to find least rec. used. + FBTREE_CACHE m_CacheBlks[ FBTREE_CACHE_BLKS]; + + // B-tree stack. + FBtreeBlk * m_BTStack[ FBTREE_MAX_LEVELS]; +}; + +#endif // ifndef FRSET_H diff --git a/version5/src/ffilehdl.cpp b/version5/src/ffilehdl.cpp new file mode 100644 index 0000000..14ea1c2 --- /dev/null +++ b/version5/src/ffilehdl.cpp @@ -0,0 +1,602 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the F_FileHdlMgr and F_FileHdl classes. +// +// Tabs: 3 +// +// Copyright (c) 1997-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: ffilehdl.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Name: f_filecpy +Desc: quick and easy way to write a string to a file. The contents of + pszSourceFile becomes pszData. +****************************************************************************/ +RCODE f_filecpy( + const char * pszSourceFile, + const char * pszData) +{ + IF_FileHdl * pFileHdl = NULL; + F_FileSystem fileSystem; + RCODE rc = NE_XFLM_OK; + + //if it exists, delete it + if (RC_OK( rc = fileSystem.Exists( pszSourceFile))) + { + if ( RC_BAD( rc = fileSystem.Delete( pszSourceFile))) + { + goto Exit; + } + } + + if ( RC_BAD( rc = fileSystem.Create( pszSourceFile, XFLM_IO_RDWR, + &pFileHdl))) + { + goto Exit; + } + + { + FLMUINT uiBytesWritten = 0; + 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; +} + +/**************************************************************************** +Name: f_filecat +Desc: quick and easy way to append a string to a file. The contents of + pszData are appended to pszSourceFile. +****************************************************************************/ +RCODE f_filecat( + const char * pszSourceFile, + const char * pszData) +{ + IF_FileHdl * pFileHdl = NULL; + F_FileSystem fileSystem; + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = fileSystem.Exists( pszSourceFile))) + { + //create file if it doesn't already exist + if ( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = fileSystem.Create( pszSourceFile, XFLM_IO_RDWR, + &pFileHdl); + } + else + { + goto Exit; + } + } + else + { + rc = fileSystem.Open( pszSourceFile, + XFLM_IO_RDWR, &pFileHdl); + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + { + FLMUINT64 ui64FileSize = 0; + FLMUINT uiBytesWritten = 0; + + 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); +} + +/**************************************************************************** +Desc: Initializes variables +****************************************************************************/ +F_FileHdlMgr::F_FileHdlMgr() +{ + m_hMutex = F_MUTEX_NULL; + + //m_uiOpenThreshold = ~0; <- this creates a compiler warning! + m_uiOpenThreshold = 0xFFFF; // No limit - this should be enough + FLM_SECS_TO_TIMER_UNITS( 30 * 60, m_uiMaxAvailTime); // 30 minutes + 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_XFLM_OK; + + // Need to allocate a mutex + + 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, // Desired FileHdr's ID + FLMBOOL bReadOnlyFlag, // TRUE if file is read only + F_FileHdl ** ppFileHdl) // [out] returned FileHdl object. +{ + 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(); // LOCK WHILE MOVING FILE HDL + + removeFromList( TRUE, + pFileHdl, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + insertInList( TRUE, pFileHdl, FALSE, + &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + + // NOTE: DO NOT CALL RELEASE -- Keep reference for caller. + + 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) // FileHdl to move to the avail list. +{ + pFileHdl->m_uiAvailTime = (FLMUINT)FLM_GET_TIMER(); + + lockMutex( bMutexAlreadyLocked); + + // NOTE: To prevent this file handle from being freed this code + // performs an AddRef/Release while its being relinked. + + pFileHdl->AddRef(); // LOCK WHILE MOVING FILE HDL + + removeFromList( TRUE, + pFileHdl, &m_pFirstUsed, &m_pLastUsed, &m_uiNumUsed); + insertInList( TRUE, pFileHdl, TRUE, + &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + + pFileHdl->Release(); // UNLOCK NOW THAT MOVE IS DONE! + + // Release the caller's reference to the file handle + + 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) + { + break; // All files are newer so we are done. + } + removeFromList( TRUE, + m_pFirstAvail, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + } + + unlockMutex( FALSE); +} + +/**************************************************************************** +Desc: Do a partial copy from one file into another file. +****************************************************************************/ +RCODE flmCopyPartial( + 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_XFLM_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, XFLM_IO_SEEK_SET, + &ui64FileOffset))) + { + goto Exit; + } + + if( RC_BAD( rc = pDestFileHdl->Seek( ui64DestOffset, XFLM_IO_SEEK_SET, + &ui64FileOffset))) + { + goto Exit; + } + + // Begin copying the data + + while( ui64CopySize) + { + if( ui64CopySize > uiAllocSize) + { + uiBytesToRead = uiAllocSize; + } + else + { + uiBytesToRead = (FLMUINT)ui64CopySize; + } + + rc = pSrcFileHdl->Read( XFLM_IO_CURRENT_POS, uiBytesToRead, + pucBuffer, &uiBytesRead); + + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + + if (RC_BAD( rc)) + { + rc = RC_SET( NE_XFLM_IO_COPY_ERR); + goto Exit; + } + + uiBytesWritten = 0; + if( RC_BAD( rc = pDestFileHdl->Write( XFLM_IO_CURRENT_POS, uiBytesRead, + pucBuffer, &uiBytesWritten))) + { + if (rc == NE_XFLM_IO_DISK_FULL) + { + *pui64BytesCopiedRV += uiBytesWritten; + } + else + { + rc = RC_SET( NE_XFLM_IO_COPY_ERR); + } + + goto Exit; + } + + *pui64BytesCopiedRV += uiBytesWritten; + + if( uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_IO_END_OF_FILE); + goto Exit; + } + + ui64CopySize -= uiBytesRead; + } + +Exit: + + if (pucBuffer) + { + (void)f_free( &pucBuffer); + } + + return( rc); +} diff --git a/version5/src/ffilehdl.h b/version5/src/ffilehdl.h new file mode 100644 index 0000000..6580576 --- /dev/null +++ b/version5/src/ffilehdl.h @@ -0,0 +1,260 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's +// FileHdlMgr and FileHdl classes. +// +// Tabs: 3 +// +// Copyright (c) 1997-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: ffilehdl.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FFILEHDL_H +#define FFILEHDL_H + +class F_FileHdl; // Forward Reference +class F_FileHdlMgr; // Forward Reference +class F_FileHdlMgrPage; // Source: imonfmgr.cpp +class F_FileHdlPage; // Source: imonfhdl.cpp + +#define FHM_AVAIL_LIST 0 +#define FHM_USED_LIST 1 +#define FHM_LNODE_COUNT 2 + +RCODE DetermineLockMgr( + F_Database * pDatabase, + F_FileHdl * pFileHdl); + +RCODE flmCopyPartial( + IF_FileHdl * pSrcFileHdl, + FLMUINT64 ui64SrcOffset, + FLMUINT64 ui64SrcSize, + IF_FileHdl * pDestFileHdl, + FLMUINT64 ui64DestOffset, + FLMUINT64 * pui64BytesCopiedRV); + +RCODE f_filecpy( + const char * pszSourceFile, + const char * pszData); + +RCODE f_filecat( + const char * pszSourceFile, + const char * pszData); + +#include "fwin.h" +#include "fposix.h" + +/*=========================================================================== +Class: F_FileHdlMgr +Desc: The F_FileHdlMgr class manages F_FileHdl objects. + The F_FileHdlMgr maintains two lists: + 1) Available F_FileHdl's - F_FileHdl's not currently being used. + 2) Used F_FileHdl's - F_FileHdl's that have been checked out. +===========================================================================*/ +class F_FileHdlMgr : public XF_RefCount, public XF_Base +{ +public: + + F_FileHdlMgr(); + + FINLINE ~F_FileHdlMgr() + { + if (m_hMutex != F_MUTEX_NULL) + { + lockMutex( FALSE); + freeUsedList( TRUE); + freeAvailList( TRUE); + unlockMutex( FALSE); + f_mutexDestroy( &m_hMutex); + } + } + + RCODE setupFileHdlMgr( // Setup the F_FileHdlMgr object + FLMUINT uiOpenThreshold = XFLM_DEFAULT_OPEN_THRESHOLD, + // High water mark for open handles. + FLMUINT uiMaxAvailTime = XFLM_DEFAULT_MAX_AVAIL_TIME); + // Max avail time to wait to close files. + + 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); + } + + // Methods for Avail & Used List Management + + void findAvail( // Determines if the F_FileHdlMgr has an + // available F_FileHdl for the specified FileId + FLMUINT uiFileId, // Desired FileHdr's ID + FLMBOOL bReadOnlyFlag, // TRUE if looking for read only file + F_FileHdl ** ppFileHdl); // [out] returned F_FileHdl object. + + void makeAvailAndRelease( // Make the specified F_FileHdl available for + // someone else to use. + FLMBOOL bMutexAlreadyLocked, + F_FileHdl * pFileHdl); // F_FileHdl to move to the available list. + + void removeFileHdls( // Remove (close&free) all FileHdl's that + // have the specified FileId. + FLMUINT uiFileId); + + void checkAgedFileHdls( // Remove aged items older than + FLMUINT uiMinSecondsOpened); // minimum seconds opened. + + void FINLINE releaseOneAvail( + FLMBOOL bMutexAlreadyLocked + ) + { + lockMutex( bMutexAlreadyLocked); + if (m_pFirstAvail) + { + removeFromList( TRUE, + m_pFirstAvail, &m_pFirstAvail, &m_pLastAvail, &m_uiNumAvail); + } + unlockMutex( bMutexAlreadyLocked); + } + + // Manager Statistics Methods + + 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; + } + + // Misc. Methods + + 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); + } + } + + // Private variables + + F_MUTEX m_hMutex; + FLMUINT m_uiOpenThreshold; // FileHdl open threshold. + FLMUINT m_uiMaxAvailTime; // Time to close any available files. + + // Used list + + F_FileHdl * m_pFirstUsed; + F_FileHdl * m_pLastUsed; + FLMUINT m_uiNumUsed; + + // Avail list + + F_FileHdl * m_pFirstAvail; + F_FileHdl * m_pLastAvail; + FLMUINT m_uiNumAvail; + + FLMBOOL m_bIsSetup; // TRUE when list manager is set up. + FLMUINT m_uiFileIdCounter; // Used for getUniqueId() call. + +friend class F_FileHdlMgrPage; +friend class F_FileHdl; + +}; + +#endif // #ifndef FFILEHDL_H diff --git a/version5/src/ffilehdr.cpp b/version5/src/ffilehdr.cpp new file mode 100644 index 0000000..5a16e88 --- /dev/null +++ b/version5/src/ffilehdr.cpp @@ -0,0 +1,456 @@ +//------------------------------------------------------------------------------ +// Desc: Routines for accessing information in the database header. +// +// Tabs: 3 +// +// Copyright (c) 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: ffilehdr.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE verifyDbHdr( + XFLM_DB_HDR * pDbHdr); + +/******************************************************************** +Desc: This routine adjusts the block size that is passe in (wBlkSize) + to the nearest valid block size. +*********************************************************************/ +FLMUINT flmAdjustBlkSize( + FLMUINT uiBlkSize) +{ + FLMUINT uiTmpBlkSize; + + uiTmpBlkSize = XFLM_MIN_BLOCK_SIZE; + while (uiBlkSize > uiTmpBlkSize && uiTmpBlkSize < XFLM_MAX_BLOCK_SIZE) + { + uiTmpBlkSize <<= 1; + } + + return( uiTmpBlkSize); +} + +/******************************************************************** +Desc: This routine initializes a XFLM_DB_HDR structure. +*********************************************************************/ +void flmInitDbHdr( + XFLM_CREATE_OPTS * pCreateOpts, + FLMBOOL bCreatingDatabase, + FLMBOOL bTempDb, + XFLM_DB_HDR * pDbHdr) +{ + FLMUINT uiMinRflFileSize; + FLMUINT uiMaxRflFileSize; + + if (bCreatingDatabase) + { + f_memset( pDbHdr, 0, sizeof( XFLM_DB_HDR)); + } + + // If pCreateOpts is non-NULL, copy it into the file header. + + f_strcpy( pDbHdr->szSignature, XFLM_DB_SIGNATURE); + pDbHdr->ui8IsLittleEndian = XFLM_NATIVE_IS_LITTLE_ENDIAN; + + if (pCreateOpts) + { + pDbHdr->ui16BlockSize = (FLMUINT16)pCreateOpts->uiBlockSize; + pDbHdr->ui8DefaultLanguage = (FLMUINT8)pCreateOpts->uiDefaultLanguage; + if (pCreateOpts->bKeepRflFiles) + { + pDbHdr->ui8RflKeepFiles = 1; + } + if (pCreateOpts->bLogAbortedTransToRfl) + { + pDbHdr->ui8RflKeepAbortedTrans = 1; + } + + if( (uiMinRflFileSize = pCreateOpts->uiMinRflFileSize) == 0) + { + uiMinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; + } + + if( (uiMaxRflFileSize = pCreateOpts->uiMaxRflFileSize) == 0) + { + uiMaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; + } + } + else + { + + // If pCreateOpts is NULL, initialize some default values. + + pDbHdr->ui16BlockSize = XFLM_DEFAULT_BLKSIZ; + pDbHdr->ui8DefaultLanguage = XFLM_DEFAULT_LANG; + uiMinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; + uiMaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; + } + + // Make sure the RFL size limits are valid. + // Maximum must be enough to hold at least one packet plus + // the RFL header. Minimum must not be greater than the + // maximum. NOTE: Minimum and maximum are allowed to be + // equal, but in all cases, maximum takes precedence over + // minimum. We will first NOT exceed the maximum. Then, + // if possible, we will go above the minimum. + + if (uiMaxRflFileSize < RFL_MAX_PACKET_SIZE + 512) + { + uiMaxRflFileSize = RFL_MAX_PACKET_SIZE + 512; + } + if (uiMaxRflFileSize > gv_XFlmSysData.uiMaxFileSize) + { + uiMaxRflFileSize = gv_XFlmSysData.uiMaxFileSize; + } + if (uiMinRflFileSize > uiMaxRflFileSize) + { + uiMinRflFileSize = uiMaxRflFileSize; + } + pDbHdr->ui32RflMinFileSize = (FLMUINT32)uiMinRflFileSize; + pDbHdr->ui32RflMaxFileSize = (FLMUINT32)uiMaxRflFileSize; + + // Only allow database to be created with current version number + + pDbHdr->ui32DbVersion = XFLM_CURRENT_VERSION_NUM; + pDbHdr->ui8BlkChkSummingEnabled = 1; + + // Round block size up to nearest legal block size. + + pDbHdr->ui16BlockSize = + (FLMUINT16)flmAdjustBlkSize( (FLMUINT)pDbHdr->ui16BlockSize); + + if (!bTempDb) + { + pDbHdr->ui32FirstLFBlkAddr = (FLMUINT32)FSBlkAddress(1, 0); + } + + // If creating a database, initialize some more items. + + if (bCreatingDatabase) + { + + // Set the logical EOF. + + if (!bTempDb) + { + pDbHdr->ui32LogicalEOF = pDbHdr->ui32FirstLFBlkAddr + + (FLMUINT32)pDbHdr->ui16BlockSize; + } + else + { + pDbHdr->ui32LogicalEOF = (FLMUINT32)FSBlkAddress(1, 0); + } + pDbHdr->ui64CurrTransID = (FLMUINT64)0; + pDbHdr->ui32RflCurrFileNum = 1; + + // Putting a zero in this value tells the RFL code that the + // RFL file should be created - overwriting it if it already + // exists. + + pDbHdr->ui32RflLastCPFileNum = 1; + pDbHdr->ui32RflLastCPOffset = 512; + pDbHdr->ui32RblEOF = pDbHdr->ui16BlockSize; + + // Set the database serial number + + f_createSerialNumber( pDbHdr->ucDbSerialNum); + + // Set the "current" RFL serial number - will be stamped into the RFL + // file when it is first created. + + f_createSerialNumber( pDbHdr->ucLastTransRflSerialNum); + + // Set the "next" RFL serial number + + f_createSerialNumber( pDbHdr->ucNextRflSerialNum); + + // Set the incremental backup serial number and sequence number + + f_createSerialNumber( pDbHdr->ucIncBackupSerialNum); + pDbHdr->ui32IncBackupSeqNum = 1; + + // Set the file size limits + + pDbHdr->ui32MaxFileSize = (FLMUINT32)gv_XFlmSysData.uiMaxFileSize; + + // Need at least two blocks to create a database! + + flmAssert( (FLMUINT)pDbHdr->ui32MaxFileSize >= + (FLMUINT)pDbHdr->ui16BlockSize * 2); + } +} + +/*************************************************************************** +Desc: This routine changes the endian-ness of the passed in 64 bit number. + If it was big-endian, it will be converted to little-endian and + vice versa. +*****************************************************************************/ +void convert64( + FLMUINT64 * pui64Num + ) +{ + FLMBYTE * pucTmp = (FLMBYTE *)pui64Num; + FLMBYTE ucTmp; + + // Swap bytes 0 and 7 + + ucTmp = pucTmp [0]; + pucTmp [0] = pucTmp [7]; + pucTmp [7] = ucTmp; + + // Swap bytes 1 and 6 + + ucTmp = pucTmp [1]; + pucTmp [1] = pucTmp [6]; + pucTmp [6] = ucTmp; + + // Swap bytes 2 and 5 + + ucTmp = pucTmp [2]; + pucTmp [2] = pucTmp [5]; + pucTmp [5] = ucTmp; + + // Swap bytes 3 and 4 + + ucTmp = pucTmp [3]; + pucTmp [3] = pucTmp [4]; + pucTmp [4] = ucTmp; +} + +/*************************************************************************** +Desc: This routine changes the endian-ness of the passed in 32 bit number. + If it was big-endian, it will be converted to little-endian and + vice versa. +*****************************************************************************/ +void convert32( + FLMUINT32 * pui32Num + ) +{ + FLMBYTE * pucTmp = (FLMBYTE *)pui32Num; + FLMBYTE ucTmp; + + // Swap bytes 0 and 3 + + ucTmp = pucTmp [0]; + pucTmp [0] = pucTmp [3]; + pucTmp [3] = ucTmp; + + // Swap bytes 1 and 2 + + ucTmp = pucTmp [1]; + pucTmp [1] = pucTmp [2]; + pucTmp [2] = ucTmp; +} + +/*************************************************************************** +Desc: This routine changes the endian-ness of the passed in 16 bit number. + If it was big-endian, it will be converted to little-endian and + vice versa. +*****************************************************************************/ +void convert16( + FLMUINT16 * pui16Num + ) +{ + FLMBYTE * pucTmp = (FLMBYTE *)pui16Num; + FLMBYTE ucTmp; + + // Swap bytes 0 and 1 + + ucTmp = pucTmp [0]; + pucTmp [0] = pucTmp [1]; + pucTmp [1] = ucTmp; +} + +/*************************************************************************** +Desc: This routine changed a database header to native platform format. +*****************************************************************************/ +void convertDbHdr( + XFLM_DB_HDR * pDbHdr + ) +{ + + // This routine should only be called to convert a header to native + // format. + + flmAssert( hdrIsNonNativeFormat( pDbHdr)); + convert16( &pDbHdr->ui16BlockSize); + convert32( &pDbHdr->ui32DbVersion); + convert64( &pDbHdr->ui64LastRflCommitID); + convert32( &pDbHdr->ui32RflLastFileNumDeleted); + convert32( &pDbHdr->ui32RflCurrFileNum); + convert32( &pDbHdr->ui32RflLastTransOffset); + convert32( &pDbHdr->ui32RflLastCPFileNum); + convert32( &pDbHdr->ui32RflLastCPOffset); + convert64( &pDbHdr->ui64RflLastCPTransID); + convert32( &pDbHdr->ui32RflMinFileSize); + convert32( &pDbHdr->ui32RflMaxFileSize); + convert64( &pDbHdr->ui64CurrTransID); + convert64( &pDbHdr->ui64TransCommitCnt); + convert32( &pDbHdr->ui32RblEOF); + convert32( &pDbHdr->ui32RblFirstCPBlkAddr); + convert32( &pDbHdr->ui32FirstAvailBlkAddr); + convert32( &pDbHdr->ui32FirstLFBlkAddr); + convert32( &pDbHdr->ui32LogicalEOF); + convert32( &pDbHdr->ui32MaxFileSize); + convert64( &pDbHdr->ui64LastBackupTransID); + convert32( &pDbHdr->ui32IncBackupSeqNum); + convert32( &pDbHdr->ui32BlksChangedSinceBackup); + convert32( &pDbHdr->ui32HdrCRC); + pDbHdr->ui8IsLittleEndian = XFLM_NATIVE_IS_LITTLE_ENDIAN; +} + +/*************************************************************************** +Desc: This routine verifies that the header is a real FLAIM header. + It will also change the endian-ness if need be. This should always + be called immediately after reading a header from disk. +*****************************************************************************/ +FSTATIC RCODE verifyDbHdr( + XFLM_DB_HDR * pDbHdr + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + FLMUINT32 ui32CRC; + + // Calculate the checksum before doing any conversions. + + ui32CRC = calcDbHdrCRC( pDbHdr); + + // Convert the header to native platform format if necessary. + + if (hdrIsNonNativeFormat( pDbHdr)) + { + convertDbHdr( pDbHdr); + } + + // Check the signature. + + uiLen = f_strlen( XFLM_DB_SIGNATURE); + if (f_memcmp( pDbHdr->szSignature, XFLM_DB_SIGNATURE, uiLen) != 0) + { + rc = RC_SET( NE_XFLM_NOT_FLAIM); + goto Exit; + } + + // See if the database version is OK. + + switch (pDbHdr->ui32DbVersion) + { + case XFLM_VER_5_12: + break; + default: + if (pDbHdr->ui32DbVersion > XFLM_CURRENT_VERSION_NUM) + { + rc = RC_SET( NE_XFLM_NEWER_FLAIM); + } + else + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); + } + goto Exit; + } + + // Validate the checksum + + if (ui32CRC != pDbHdr->ui32HdrCRC) + { + rc = RC_SET( NE_XFLM_HDR_CRC); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads and verifies the information contained in the + file header and log header of a FLAIM database. +*****************************************************************************/ +RCODE flmReadAndVerifyHdrInfo( + XFLM_DB_STATS * pDbStats, + IF_FileHdl * pFileHdl, + XFLM_DB_HDR * pDbHdr, + FLMUINT32 * pui32CalcCRC) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + + // Read the database header. + + f_memset( pDbHdr, 0, sizeof( XFLM_DB_HDR)); + if (RC_BAD( rc = pFileHdl->Read( (FLMUINT)0, sizeof( XFLM_DB_HDR), + pDbHdr, &uiBytesRead))) + { + if (rc != NE_XFLM_IO_END_OF_FILE) + { + if (pDbStats) + { + pDbStats->uiReadErrors++; + } + } + else + { + if (pui32CalcCRC) + { + *pui32CalcCRC = calcDbHdrCRC( pDbHdr); + } + + // Get what we can out of the header. + + if (hdrIsNonNativeFormat( pDbHdr)) + { + convertDbHdr( pDbHdr); + } + } + goto Exit; + } + + if (pui32CalcCRC) + { + *pui32CalcCRC = calcDbHdrCRC( pDbHdr); + } + if (uiBytesRead < sizeof( XFLM_DB_HDR)) + { + + // Still get what we can out of the header. + + if (hdrIsNonNativeFormat( pDbHdr)) + { + convertDbHdr( pDbHdr); + } + rc = RC_SET( NE_XFLM_NOT_FLAIM); + goto Exit; + } + else + { + + // This routine will convert to native format if it is not + // in native format. + + if (RC_BAD( rc = verifyDbHdr( pDbHdr))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/version5/src/ffilesys.cpp b/version5/src/ffilesys.cpp new file mode 100644 index 0000000..35a76fb --- /dev/null +++ b/version5/src/ffilesys.cpp @@ -0,0 +1,942 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" + +/**************************************************************************** +Desc: Create a file, return a file handle to created file. +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::Create( + const char * pszFileName, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + IF_FileHdl ** ppFileHdl) // Returns open file handle object. +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::CreateBlockFile( + const char * pszFileName, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + FLMUINT uiBlockSize, // Block size for file + IF_FileHdl ** ppFileHdl) // Returns open file handle object. +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::CreateUnique( + const char * pszDirName, // Directory where file is to be created. + const char * pszFileExtension, // Extension to be used on file. + FLMUINT uiIoFlags, // Access and Mode flags. + IF_FileHdl ** ppFileHdl) // Returns open file handle object. +{ + RCODE rc; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::Open( + const char * pszFileName, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + IF_FileHdl ** ppFileHdl) // Returns open file handle object. +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::OpenBlockFile( + const char * pszFileName, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + FLMUINT uiBlockSize, // Block size for file + IF_FileHdl ** ppFileHdl) // Returns open file handle object. +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl * pFileHdl = NULL; + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::OpenDir( + const char * pszDirName, // Directory to be opened. + const char * pszPattern, // File name pattern. + IF_DirHdl ** ppDirHdl) // Returns open directory handle object. +{ + RCODE rc = NE_XFLM_OK; + F_DirHdl * pDirHdl = NULL; + + if ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileSystem::CreateDir( + const char * pszDirName) // Directory to be created. +{ + RCODE rc = NE_XFLM_OK; + F_DirHdl * pDirHdl = NULL; + + if ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + rc = pDirHdl->CreateDir( pszDirName); + +Exit: + + if (pDirHdl) + { + pDirHdl->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Remove a directory +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::RemoveDir( + const char * pszDirName, // Directory to delete + FLMBOOL bClear) // OK to delete files if dir is not empty? +{ + RCODE rc = NE_XFLM_OK; + IF_DirHdl * pDirHdl = NULL; + char szFilePath[ F_PATH_MAX_SIZE]; + + if( bClear) + { + // Note: '*' = all files + if( RC_BAD( rc = OpenDir( pszDirName, (char *)"*", &pDirHdl))) + { + goto Exit; + } + + // Delete everything in the directory + for ( rc = pDirHdl->Next(); RC_OK( rc) ; rc = pDirHdl->Next()) + { + pDirHdl->CurrentItemPath( szFilePath); + if( !pDirHdl->CurrentItemIsDir()) + { + if( RC_BAD( rc = Delete( szFilePath))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + else + { + if( RC_BAD( rc = RemoveDir( szFilePath, bClear))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_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 ((pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pDirHdl->RemoveDir( pszDirName))) + { + goto Exit; + } + +Exit: + + if (pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Determine if a file or directory exists. +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::Exists( + const char * pszPath) // Name of file or directory to check. +{ +#if defined( FLM_NLM) + + return( flmNetWareTestIfFileExists( pszPath)); + +#elif defined( FLM_WIN) + + DWORD dwFileAttr = GetFileAttributes( (LPTSTR)pszPath); + + if( dwFileAttr == (DWORD)-1) + return RC_SET( NE_XFLM_IO_PATH_NOT_FOUND); + + return NE_XFLM_OK; + +#else + + if( access( pszPath, F_OK) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_CHECKING_FILE_EXISTENCE)); + } + + return( NE_XFLM_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 XFLMAPI F_FileSystem::GetTimeStamp( + 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_XFLM_OK; + F_TMSTAMP tmstamp; + + hSearch = FindFirstFile( (LPTSTR)pszPath, &find_data); + if( hSearch == INVALID_HANDLE_VALUE) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_OPENING_FILE); + switch( rc) + { + case NE_XFLM_IO_NO_MORE_FILES: + rc = RC_SET( NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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 = flmLocalToUTC( *puiTimeStamp); + + return( rc); + +#else + + struct stat filestatus; + + if( stat( pszPath, &filestatus) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_GETTING_FILE_INFO)); + } + + *puiTimeStamp = (FLMUINT)filestatus.st_mtime; // st_mtime is UTC + return NE_XFLM_OK; + +#endif +} + +/**************************************************************************** +Desc: Determine if a path is a directory. +****************************************************************************/ +FLMBOOL XFLMAPI 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 XFLMAPI F_FileSystem::Delete( + const char * pszFileName) +{ +#if defined( FLM_NLM) + + return( flmNetWareDeleteFile( pszFileName)); + +#elif defined( FLM_WIN) + + if( DeleteFile( (LPTSTR)pszFileName) == FALSE) + { + return( MapWinErrorToFlaim( GetLastError(), NE_XFLM_IO_DELETING_FILE)); + } + + return( NE_XFLM_OK); + +#else + + struct stat FileStat; + + if( stat( (char *)pszFileName, &FileStat) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_GETTING_FILE_INFO)); + } + + // Ensure that the path does NOT designate a directory for deletion + + if( S_ISDIR(FileStat.st_mode)) + { + return( RC_SET( NE_XFLM_IO_ACCESS_DENIED)); + } + + // Delete the file + + if( unlink( (char *)pszFileName) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_IO_DELETING_FILE)); + } + + return( NE_XFLM_OK); + +#endif +} + +/**************************************************************************** +Desc: Copy a file. +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::Copy( + 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_XFLM_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 (Exists( pszDestFileName) == NE_XFLM_OK) + { + if (!bOverwrite) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + if (RC_BAD( rc = Delete( pszDestFileName))) + { + goto Exit; + } + } + + // Open the source file. + + if( RC_BAD( rc = Open( pszSrcFileName, XFLM_IO_RDONLY | XFLM_IO_SH_DENYNONE, + &pSrcFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pSrcFileHdl->Size( &ui64SrcSize))) + { + goto Exit; + } + + // Create the destination file. + + if( RC_BAD( rc = Create( pszDestFileName, XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE, + &pDestFileHdl))) + { + goto Exit; + } + bCreatedDest = TRUE; + + // Do the copy. + + if( RC_BAD( rc = flmCopyPartial( 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)Delete( pszDestFileName); + } + + *pui64BytesCopied = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: Rename a file. +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::Rename( + const char * pszFileName, + const char * pszNewFileName) +{ +#if defined( FLM_NLM) + + return( flmNetWareRenameFile( pszFileName, pszNewFileName)); + +#elif defined( FLM_WIN) + + DWORD error; + RCODE rc = NE_XFLM_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( Copy( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied)) + { + rc = RC_SET( NE_XFLM_IO_COPY_ERR); + } + else + { + rc = F_FileSystem::Delete( pszFileName); + } + break; + default: + rc = MapWinErrorToFlaim( error, NE_XFLM_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_XFLM_IO_PATH_CREATE_FAILURE)); + } + else + { + if( Copy( pszFileName, pszNewFileName, TRUE, &ui64BytesCopied)) + { + return( RC_SET( NE_XFLM_IO_COPY_ERR)); + } + + F_FileSystem::Delete( pszFileName); + return( NE_XFLM_OK); + } + } + + default: + { + if( errno == ENOENT) + { + return( RC_SET( NE_XFLM_IO_RENAME_FAILURE)); + } + else + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_RENAMING_FILE)); + } + } + } + } + + return( NE_XFLM_OK); +#endif +} + +/**************************************************************************** +Desc: Get the sector size (not supported on all platforms). +****************************************************************************/ +RCODE XFLMAPI F_FileSystem::GetSectorSize( + const char * pszFileName, + FLMUINT * puiSectorSize) +{ +#ifdef FLM_NLM + + F_UNREFERENCED_PARM( pszFileName); + *puiSectorSize = NETWARE_SECTOR_SIZE; + return( NE_XFLM_OK); + +#elif defined( FLM_WIN) + + RCODE rc = NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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_XFLM_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_XFLM_OK; + +#if defined( FLM_UNIX) + struct stat filestatus; + + if( stat( (char *)pszFileName, &filestatus)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_FAILURE); + goto Exit; + } + +#elif defined( FLM_WIN) + + DWORD dwAttr; + + dwAttr = GetFileAttributes( (LPTSTR)pszFileName); + if( dwAttr == (DWORD)-1) + { + rc = RC_SET( NE_XFLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + if ( bReadOnly) + { + dwAttr |= XF_IO_FA_RDONLY; + } + else + { + dwAttr &= ~XF_IO_FA_RDONLY; + } + + if( !SetFileAttributes( (LPTSTR)pszFileName, dwAttr)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } +#elif defined( FLM_NLM) + + if ( RC_BAD( rc = flmNetWareSetReadOnly( pszFileName, bReadOnly))) + { + goto Exit; + } +#else + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_OK; + + *isdir = 0; + if( stat(tpath, &sbuf) < 0) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_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) + +/************************************************************************* + * There appears to be a bug in newer versions of glibc (it was first + * noticed in RedHat 8) where the behavior of the reaname() function has + * changed. According to the man page, rename should allow you to + * overwrite an existing directory (ie: rename( "OldDir", "NewDir") + * should succeed, even if NewDir already exists). In order to + * avoid having #ifdefs for individual Linux distributions, we + * decided not to rely on this behavior, and simply do the + * existence test ourselves. Thus, the race condition that this + * function was supposed to avoid is theoretically possible, but as long + * as this function only gets called while the database is closed, it won't + * be a problem. RGM 5 June 2003 + ***************************************************************************/ +RCODE F_FileSystem::unix_RenameSafe( + const char * pszSrcFile, + const char * pszDestFile) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_IO_RENAME_FAILURE); + goto Exit; + } + } + + errno = 0; + if( rename( pszSrcFile, pszDestFile) != 0) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_IO_RENAME_FAILURE); + } + +Exit: + + return( rc); +} +#endif diff --git a/version5/src/ffilesys.h b/version5/src/ffilesys.h new file mode 100644 index 0000000..a85a948 --- /dev/null +++ b/version5/src/ffilesys.h @@ -0,0 +1,173 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's +// file system 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.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FFILESYS_H +#define FFILESYS_H + +/*=========================================================================== +Desc: The F_FileSystem class provides a file system abstraction for + interacting with the underlying O/S file system. +===========================================================================*/ +class F_FileSystem : public IF_FileSystem, public XF_Base +{ +public: + + F_FileSystem() + { + } + + virtual ~F_FileSystem() // Destructor + { + } + + RCODE XFLMAPI Create( // Create a new file handle + const char * pszFileName, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + IF_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE XFLMAPI CreateBlockFile( // Create a new block-oriented file handle + const char * pszFileName, // Name of file to be created + FLMUINT uiIoFlags, // Access amd Mode flags + FLMUINT uiBlockSize, // Block size + IF_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE XFLMAPI CreateUnique( // Create a new file (with a unique + // file name). + const char * pszDirName, // Directory where file is to be + // created. + const char * pszFileExtension, // Extension to be used on file. + FLMUINT uiIoFlags, // Access and Mode flags. + IF_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE XFLMAPI Open( // Open an existing file. + const char * pszFileName, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + IF_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE XFLMAPI OpenBlockFile( // Open an existing block-oriented file. + const char * pszFileName, // Name of file to be opened. + FLMUINT uiIoFlags, // Access and Mode flags. + FLMUINT uiBlockSize, // Block size. + IF_FileHdl ** ppFileHdl); // Returns open file handle object. + + RCODE XFLMAPI OpenDir( // Open a directory + const char * pszDirName, // Directory to be opened. + const char * pszPattern, // File name pattern. + IF_DirHdl ** ppDirHdl); // Returns open directory handle + // object. + + RCODE XFLMAPI CreateDir( // Create a directory + const char * pszDirName); // Directory to be created. + + RCODE XFLMAPI RemoveDir( // Remove a directory + const char * pszDirName, // Directory to be removed. + FLMBOOL bClear = FALSE); // OK to delete files if dir is not empty? + + RCODE XFLMAPI Exists( // See if a file or directory exists. + const char * pszFileName); // Name of file or directory to check. + + FLMBOOL XFLMAPI IsDir( // See if a path is a directory. + const char * pszFileName); // Name of path to check. + + RCODE XFLMAPI GetTimeStamp( // Get the date/time when the file + // was last updated. + const char * pszFileName, // Path to file + FLMUINT * puiTimeStamp); // Buffer in which time stamp is + // returned. + + RCODE XFLMAPI Delete( // Delete a file or directory + const char * pszFileName); // Name of file or directory to delete. + + RCODE XFLMAPI Copy( // Copy a file. + const char * pszSrcFileName, // Name of source file to be copied. + const char * pszDestFileName, // Name of destination file. + FLMBOOL bOverwrite, // Overwrite destination file? + FLMUINT64 * pui64BytesCopied);// Number of bytes copied. + + RCODE XFLMAPI Rename( // Rename a file. + const char * pszFileName, // File to be renamed + const char * pszNewFileName); // New file name + + void XFLMAPI pathParse( // ftkpath.cpp + const char * pszPath, + char * pszServer, + char * pszVolume, + char * pszDirPath, + char * pszFileName); + + RCODE XFLMAPI pathReduce( // ftkpath.cpp + const char * pszSourcePath, + char * pszDestPath, + char * pszString); + + RCODE XFLMAPI pathAppend( // ftkpath.cpp + char * pszPath, + const char * pszPathComponent); + + RCODE XFLMAPI pathToStorageString( // ftkpath.cpp + const char * pszPath, + char * pszString); + + void XFLMAPI pathCreateUniqueName( // ftkpath.cpp + FLMUINT * puiTime, + char * pszFileName, + const char * pszFileExt, + FLMBYTE * pHighChars, + FLMBOOL bModext); + + FLMBOOL XFLMAPI doesFileMatch( // ftkpath.cpp + const char * pszFileName, + const char * pszTemplate); + + RCODE XFLMAPI GetSectorSize( // Get the sector size of the volume for + const char * pszFileName, // this file. + 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 +}; + +#ifndef ALLOCATE_SYS_DATA +extern F_FileSystem * gv_pFileSystem; +#else +F_FileSystem * gv_pFileSystem; +#endif + +#endif // #ifndef FFILESYS_H + diff --git a/version5/src/filesys.h b/version5/src/filesys.h new file mode 100644 index 0000000..7e8414a --- /dev/null +++ b/version5/src/filesys.h @@ -0,0 +1,515 @@ +//------------------------------------------------------------------------------ +// Desc: Various macros, prototypes, structures. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: filesys.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FILESYS_H +#define FILESYS_H + +/*************************************************************** +** +** Defined Constants that the File system cares about +** +****************************************************************/ + +#define MAX_DATA_BLOCK_FILE_NUMBER 0x7FF +#define FIRST_LOG_BLOCK_FILE_NUMBER (MAX_DATA_BLOCK_FILE_NUMBER + 1) +#define MAX_LOG_BLOCK_FILE_NUMBER 0xFFF + +#define FSGetFileNumber( uiBlkAddr) ((uiBlkAddr) & MAX_LOG_BLOCK_FILE_NUMBER) +#define FSGetFileOffset( udBlkAddr) ((udBlkAddr) & 0xFFFFF000) +#define FSBlkAddress( iFileNum, udFileOfs) ((udFileOfs) + (iFileNum)) + +// Max file size and log threshold. + +#define LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000) + +// very large threshhold is the size we will allow the physical +// log to grow to before we force a truncation. At the low end, +// it is about 10 megabytes. At the high end it is about +// 1 gigabyte. + +#define LOW_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT)0xA00000) +#define HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE ((FLMUINT) 0x40000000) + +// RFL_TRUNCATE_SIZE is the size we will let an RFL file grow to +// before we truncate it back. RFL files are only truncated if +// we are configured to delete old RFL files. + +#define RFL_TRUNCATE_SIZE ((FLMUINT)1024 * (FLMUINT)1024 * (FLMUINT)10) + +/*============================================================================ + Shared Cache Routines +============================================================================*/ + +RCODE flmPrepareBlockForUse( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr); + +RCODE flmPrepareBlockToWrite( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr); + +void ScaUseCache( + F_CachedBlock * pSCache, + FLMBOOL bMutexAlreadyLocked); + +void ScaReleaseCache( + F_CachedBlock * pSCache, + FLMBOOL bMutexAlreadyLocked); + +#if defined( FLM_NLM) || defined( FLM_WIN) + void FastBlockCheckSum( + void * pBlk, + FLMUINT * puiChecksum, + FLMUINT * puiXORData, + FLMUINT uiNumberOfBytes); +#endif + +/*============================================================================ + File system Btree Cache Routines +============================================================================*/ + +FLMUINT SENNextVal( + FLMBYTE ** senPtrRV ); + +FLMUINT FSGetDomain( + FLMBYTE ** curElmRV, + FLMUINT uiElmOvhd); + +RCODE dbLock( + F_Db * pDb, + FLMUINT uiMaxLockWait ); + +RCODE dbUnlock( + F_Db * pDb); + +FINLINE void FSLFileIn( + FLMBYTE * pucBuf, + LFILE * pLFile, + F_COLLECTION * pCollection, + FLMUINT uiBlkAddress, + FLMUINT uiOffsetInBlk) +{ + F_LF_HDR * pLfHdr = (F_LF_HDR *)pucBuf; + + pLFile->uiBlkAddress = uiBlkAddress; + pLFile->uiOffsetInBlk= uiOffsetInBlk; + + if ((pLFile->eLfType = (eLFileType)pLfHdr->ui32LfType) != XFLM_LF_INVALID) + { + pLFile->uiLfNum = (FLMUINT)pLfHdr->ui32LfNumber; + pLFile->uiRootBlk = (FLMUINT)pLfHdr->ui32RootBlkAddr; + pLFile->uiEncId = (FLMUINT)pLfHdr->ui32EncId; + + if (pCollection) + { + flmAssert( pLFile == &pCollection->lfInfo); + flmAssert( pLFile->eLfType == XFLM_LF_COLLECTION); + pCollection->ui64NextNodeId = pLfHdr->ui64NextNodeId; + pCollection->ui64FirstDocId = pLfHdr->ui64FirstDocId; + pCollection->ui64LastDocId = pLfHdr->ui64LastDocId; + pCollection->bNeedToUpdateNodes = FALSE; + } + else + { + flmAssert( pLFile->eLfType == XFLM_LF_INDEX); + } + } +} + +void lgSetSyncCheckpoint( + F_Database * pDatabase, + FLMUINT uiCheckpoint, + FLMUINT uiBlkAddress); + +FLMUINT32 calcBlkCRC( + F_BLK_HDR * pBlkHdr, + FLMUINT uiBlkEnd); + +FLMUINT32 calcDbHdrCRC( + XFLM_DB_HDR * pDbHdr); + +FLMUINT flmAdjustBlkSize( + FLMUINT uiBlkSize); + +void flmInitDbHdr( + XFLM_CREATE_OPTS * pCreateOpts, + FLMBOOL bCreatingDatabase, + FLMBOOL bTempDb, + XFLM_DB_HDR * pDbHdr); + +RCODE flmCreateLckFile( + const char * pszFilePath, + IF_FileHdl ** ppLockFileHdl); + +RCODE flmAllocHashTbl( + FLMUINT uiHashTblSize, + FBUCKET ** ppHashTblRV); + +RCODE flmWaitNotifyReq( + F_MUTEX hMutex, + F_SEM hSem, + FNOTIFY ** ppNotifyListRV, + void * pvUserData); + +RCODE flmReadAndVerifyHdrInfo( + XFLM_DB_STATS * pDbStats, + IF_FileHdl * pFileHdl, + XFLM_DB_HDR * pDbHdr, + FLMUINT32 * pui32CalcCRC = NULL); + +void flmDoEventCallback( + eEventCategory eCategory, + eEventType eEvent, + IF_Db * pDb, + FLMUINT uiThreadId, + FLMUINT64 ui64TransID, + FLMUINT uiIndexOrCollection, + FLMUINT64 ui64NodeId, + RCODE rc); + +#define FLM_MAX_SEN_LEN 9 + +FLMUINT flmGetSENByteCount( + FLMUINT64 ui64Num); + +FLMUINT flmEncodeSEN( + FLMUINT64 ui64Value, + FLMBYTE ** ppucBuffer, + FLMUINT uiBytesWanted = 0); + +RCODE flmEncodeSEN( + FLMUINT64 ui64Value, + FLMBYTE ** ppucBuffer, + FLMBYTE * pucEnd); + +FLMUINT flmEncodeSENKnownLength( + FLMUINT64 ui64Value, + FLMUINT uiSenLen, + FLMBYTE ** ppucBuffer); + +RCODE flmDecodeSEN64( + const FLMBYTE ** ppucBuffer, + const FLMBYTE * pucEnd, + FLMUINT64 * pui64Value); + +FINLINE RCODE flmDecodeSEN( + const FLMBYTE ** ppucBuffer, + const FLMBYTE * pucEnd, + FLMUINT * puiValue) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Value; + + if( RC_BAD( rc = flmDecodeSEN64( ppucBuffer, pucEnd, &ui64Value))) + { + return( rc); + } + + if( ui64Value > FLM_MAX_UINT) + { + return( RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + if( puiValue) + { + *puiValue = (FLMUINT)ui64Value; + } + + return( rc); +} + +#ifdef ALLOCATE_SYS_DATA + FLMBYTE gv_ucSENLengthArray[] = + { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0 - 15 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 16 - 31 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 48 - 63 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 - 95 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 112 - 127 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 128 - 143 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 144 - 159 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 160 - 175 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 176 - 191 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 192 - 207 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 208 - 223 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // 224 - 239 + 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 8, 9 // 240 - 255 + }; +#else + extern FLMBYTE gv_ucSENLengthArray[]; +#endif + +FINLINE FLMUINT flmGetSENLength( + FLMBYTE ucByte) +{ + return( gv_ucSENLengthArray[ ucByte]); +} + +void flmLogError( + RCODE rc, + const char * pszDoing, + const char * pszFileName = NULL, + FLMINT iLineNumber = 0); + +RCODE flmCollation2Number( + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg, + FLMUINT * puiBytesProcessed); + +RCODE flmStorageNum2CollationNum( + const FLMBYTE * pucStorageBuf, + FLMUINT uiStorageLen, + FLMBYTE * pucCollBuf, + FLMUINT * puiCollLen); + +RCODE flmCollationNum2StorageNum( + const FLMBYTE * pucCollBuf, + FLMUINT uiCollLen, + FLMBYTE * pucStorageBuf, + FLMUINT * puiStorageLen); + +RCODE flmStorageNum2StorageText( + const FLMBYTE * pucNum, + FLMUINT uiNumLen, + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +RCODE flmStorageNumberToNumber( + const FLMBYTE * pucNumBuf, + FLMUINT uiNumBufLen, + FLMUINT64 * pui64Number, + FLMBOOL * pbNeg); + +void kyReleaseCdls( + IXD * pIxd, + CDL_HDR * pCdlTbl); + +RCODE KYCollateValue( + FLMBYTE * pucDest, + FLMUINT * puiDestLen, + IF_PosIStream * pIStream, + FLMUINT uiDataType, + FLMUINT uiFlags, + FLMUINT uiCompareRules, + FLMUINT uiLimit, + FLMUINT * puiCollationLen, + FLMUINT * puiLuLen, + FLMUINT uiLanguage, + FLMBOOL bFirstSubstring, + FLMBOOL bDataTruncated, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbOriginalCharsLost); + +RCODE flmGetNextMetaphone( + IF_IStream * pIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone = NULL); + +RCODE flmVerifyMetaphoneRoutines( void); + +#define UNDF_CHR 0x0000 // Undefined char - ignore for now +#define IGNR_CHR 0x0001 // Ignore this char +#define SDWD_CHR 0x0002 // Space delimited word chr +#define DELI_CHR 0x0040 // Delimiter +#define WDJN_CHR 0x0080 // Word Joining chr ".,/-_" + +// Implement later + +#define KATA_CHR 0x0004 // Katakana word chr +#define HANG_CHR 0x0008 // Hangul word chr +#define CJK_CHR 0x0010 // CJK word chr + +RCODE KYSubstringParse( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUINT uiLimit, + FLMBYTE * pucSubstrBuf, + FLMUINT * puiSubstrBytes, + FLMUINT * puiSubstrChars); + +RCODE KYEachWordParse( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUINT uiLimit, + FLMBYTE * pucWordBuf, + FLMUINT * puiWordLen); + +RCODE fdictGetState( + const char * pszState, + FLMUINT * puiState); + +RCODE fdictGetIndexState( + const char * pszState, + FLMUINT * puiState); + +#define FLM_BACKGROUND_LOCK_PRIORITY -100 + +void flmLogIndexingProgress( + FLMUINT uiIndexNum, + FLMUINT64 ui64LastDocumentId); + +F_BKGND_IX * flmBackgroundIndexGet( + F_Database * pDatabase, + FLMUINT uiValue, + FLMBOOL bMutexLocked, + FLMUINT * puiThreadId = NULL); + +RCODE flmGetHdrInfo( + F_SuperFileHdl_p pSFileHdl, + XFLM_DB_HDR * pDbHdr, + FLMUINT32 * pui32CalcCRC = NULL); + +void convert64( + FLMUINT64 * pui64Num); + +void convert32( + FLMUINT32 * pui32Num); + +void convert16( + FLMUINT16 * pui16Num); + +void convertDbHdr( + XFLM_DB_HDR * pDbHdr); + +void convertBlkHdr( + F_BLK_HDR * pBlkHdr); + +void convertBlk( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr); + +void convertLfHdr( + F_LF_HDR * pLfHdr); + +/*-------------------------------------------------------- +** Inline Functions +**-------------------------------------------------------*/ + +/************************************************************************** +Desc: Returns TRUE if a block address is less than another block address. +**************************************************************************/ +FINLINE FLMBOOL FSAddrIsBelow( + FLMUINT uiAddress1, + FLMUINT uiAddress2) +{ + if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) + { + if( FSGetFileOffset( uiAddress1) >= FSGetFileOffset( uiAddress2)) + { + return FALSE; + } + } + else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) + { + return FALSE; + } + + return TRUE; +} + +/************************************************************************** +Desc: Returns TRUE if a block address is less than or equal another + block address. +**************************************************************************/ +FINLINE FLMBOOL FSAddrIsAtOrBelow( + FLMUINT uiAddress1, + FLMUINT uiAddress2) +{ + if( FSGetFileNumber( uiAddress1) == FSGetFileNumber( uiAddress2)) + { + if( FSGetFileOffset( uiAddress1) > FSGetFileOffset( uiAddress2)) + { + return FALSE; + } + } + else if( FSGetFileNumber( uiAddress1) > FSGetFileNumber( uiAddress2)) + { + return FALSE; + } + return TRUE; +} + +/**************************************************************************** +Desc: Get the total bytes represented by a particular block address. +****************************************************************************/ +FINLINE FLMUINT64 FSGetSizeInBytes( + FLMUINT uiMaxFileSize, + FLMUINT uiBlkAddress) +{ + FLMUINT uiFileNum; + FLMUINT uiFileOffset; + FLMUINT64 ui64Size; + + uiFileNum = FSGetFileNumber( uiBlkAddress); + uiFileOffset = FSGetFileOffset( uiBlkAddress); + if( uiFileNum > 1) + { + ui64Size = (FLMUINT64)(((FLMUINT64)uiFileNum - (FLMUINT64)1) * + (FLMUINT64)uiMaxFileSize + + (FLMUINT64)uiFileOffset); + } + else + { + ui64Size = (FLMUINT64)uiFileOffset; + } + return( ui64Size); +} + +/************************************************************************** +Desc: Calculate the significant bits in a block size. +**************************************************************************/ +FINLINE FLMUINT calcSigBits( + FLMUINT uiBlockSize + ) +{ + FLMUINT uiSigBitsInBlkSize = 0; + while (!(uiBlockSize & 1)) + { + uiSigBitsInBlkSize++; + uiBlockSize >>= 1; + } + return( uiSigBitsInBlkSize); +} + +/************************************************************************** +Desc: Outputs an update event callback. +**************************************************************************/ +FINLINE void flmTransEventCallback( + eEventType eEventType, + F_Db * pDb, + RCODE rc, + FLMUINT64 ui64TransId) +{ + flmDoEventCallback( XFLM_EVENT_UPDATES, eEventType, (IF_Db *)pDb, + f_threadId(), ui64TransId, 0, 0, rc); +} + +#endif // FILESYS_H + diff --git a/version5/src/flaimsys.h b/version5/src/flaimsys.h new file mode 100644 index 0000000..ab5fd81 --- /dev/null +++ b/version5/src/flaimsys.h @@ -0,0 +1,9051 @@ +//------------------------------------------------------------------------------ +// Desc: This is the master header file for FLAIM. It is included by nearly +// all of the source files. +// +// 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: flaimsys.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FLAIMSYS_H +#define FLAIMSYS_H + +#ifdef _WIN32 + #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 + #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) + +#endif + +// Public includes + +#include "xflaim.h" + +#ifdef 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 +#endif + +/**************************************************************************** +Desc: Base class +****************************************************************************/ +class XF_Base +{ +public: + + XF_Base() + { + } + + virtual ~XF_Base() + { + } + + void * operator new( + FLMSIZET uiSize); + + void * operator new[]( + FLMSIZET uiSize); + +#ifdef FLM_DEBUG + void * operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine); +#endif + +#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, + char * file, + int line); + + void operator delete[]( + void * ptr, + char * file, + int line); +#endif + +}; + +// Put all forward references here + +class F_Database; +class F_Dict; +class F_Db; +class F_NameTable; +class F_IOBuffer; +class F_Rfl; +class F_Btree; +class F_DOMNode; +class F_NodeList; +class F_Query; +class F_DbRebuild; +class F_DbCheck; +class F_DbInfo; +class F_KeyCollector; +class F_Thread; +class FSIndexCursor; +class FSCollectionCursor; +class FlmRecord; +class FlmDefaultRec; +class FDynSearchSet; +class F_CachedBlock; +class F_CachedNode; +class F_GlobalCacheMgr; +class F_BlockCacheMgr; +class F_NodeCacheMgr; +class F_NodeBufferIStream; +class F_BTreeIStream; +class F_IniFile; +class F_QueryResultSet; +class F_BTreeInfo; +class F_AttrItem; + +// Internal includes + +#include "ftk.h" +#include "fbuff.h" +#include "fcollate.h" +#include "fdict.h" +#include "fxml.h" +#include "fstructs.h" +#include "fcache.h" +#include "flmstat.h" +#include "ffilehdl.h" +#include "ffilesys.h" +#include "f64bitfh.h" +#include "frset.h" +#include "fxpath.h" +#include "fbtrset.h" +#include "frecread.h" +#include "fsrvlock.h" +#include "fquery.h" +#include "ftkthrd.h" +#include "fcollate.h" +#include "fsrvlock.h" +#include "fdir.h" +#include "f_btree.h" +#include "f_btpool.h" +#include "rfl.h" +#include "fsuperfl.h" +#include "filesys.h" +#include "flog.h" +#include "flfixed.h" +#include "f_nici.h" + +RCODE MapErrnoToFlaimErr( + int err, + RCODE defaultRc); + +// Misc. global constants + +#ifdef DEFINE_NUMBER_MAXIMUMS + #define GV_EXTERN +#else + #define GV_EXTERN extern +#endif + +GV_EXTERN FLMBOOL gv_b32BitPlatform +#ifdef DEFINE_NUMBER_MAXIMUMS + = (FLMBOOL)(sizeof( FLMUINT) == 4 ? TRUE : FALSE) +#endif + ; + +GV_EXTERN FLMUINT gv_uiMaxUInt32Val +#ifdef DEFINE_NUMBER_MAXIMUMS + = (FLMUINT)0xFFFFFFFF +#endif + ; + +GV_EXTERN FLMUINT gv_uiMaxUIntVal +#ifdef DEFINE_NUMBER_MAXIMUMS + = (FLMUINT)(~(FLMUINT)0) +#endif + ; + +GV_EXTERN FLMUINT gv_uiMaxSignedIntVal +#ifdef DEFINE_NUMBER_MAXIMUMS + = (FLMUINT)((((~(FLMUINT)0) << 1) >> 1)) +#endif + ; + +GV_EXTERN FLMUINT64 gv_ui64MaxSignedIntVal +#ifdef DEFINE_NUMBER_MAXIMUMS + = (FLMUINT64)((((~(FLMUINT64)0) << 1) >> 1)) +#endif + ; + +#if defined( FLM_WIN) || defined( FLM_NLM) + #define FLM_CAN_GET_PHYS_MEM +#elif defined( FLM_UNIX) + #if defined( _SC_AVPHYS_PAGES) + #define FLM_CAN_GET_PHYS_MEM + #endif +#endif + +// A global module lock allows us to properly implement DllCanUnloadNow +// This is only used in a COM environment. The functions are actually +// defined in fdllmain.cpp + +extern void LockModule(void); +extern void UnlockModule(void); + +#define MAX_DIRTY_NODES_THRESHOLD 1024 +#define MAX_DOM_HEADER_SIZE 118 +#define FIXED_DOM_HEADER_SIZE 94 +#define XFLM_FIXED_SIZE_HEADER_TOKEN 0xFF + +// NOTE: ENCRYPT_MIN_CHUNK_SIZE should always be a power of 2 +// getEncLen supposes that it is. + +#define ENCRYPT_MIN_CHUNK_SIZE 16 +#define ENCRYPT_BOUNDARY_MASK (~(ENCRYPT_MIN_CHUNK_SIZE - 1)) + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToUINT( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMUINT * puiNum) +{ + if( bNeg) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + + if( gv_b32BitPlatform && ui64Num > 0xFFFFFFFF) + { + return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + *puiNum = (FLMUINT)ui64Num; + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToUINT32( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMUINT32 * pui32Num) +{ + if( bNeg) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + + if( ui64Num > 0xFFFFFFFF) + { + return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + *pui32Num = (FLMUINT32)ui64Num; + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToUINT64( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMUINT64 * pui64Num) +{ + if( bNeg) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + + *pui64Num = ui64Num; + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToINT( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMINT * piNum) +{ + if( bNeg) + { + if (ui64Num == (FLMUINT64)(FLM_MAX_INT) + 1) + { + *piNum = FLM_MIN_INT; + } + else if( ui64Num > (FLMUINT64)(FLM_MAX_INT) + 1) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + else + { + *piNum = -((FLMINT)ui64Num); + } + } + else + { + if( ui64Num > (FLMUINT64)FLM_MAX_INT) + { + return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + *piNum = (FLMINT)ui64Num; + } + + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToINT32( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMINT32 * pi32Num) +{ + if( bNeg) + { + if (ui64Num == (FLMUINT64)(FLM_MAX_INT32) + 1) + { + *pi32Num = FLM_MIN_INT32; + } + else if( ui64Num > (FLMUINT64)(FLM_MAX_INT32) + 1) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + else + { + *pi32Num = -((FLMINT32)ui64Num); + } + } + else + { + if( ui64Num > (FLMUINT64)FLM_MAX_INT32) + { + return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + *pi32Num = (FLMINT32)ui64Num; + } + + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +FINLINE RCODE convertToINT64( + FLMUINT64 ui64Num, + FLMBOOL bNeg, + FLMINT64 * pi64Num) +{ + if( bNeg) + { + if( ui64Num > gv_ui64MaxSignedIntVal + 1) + { + return( RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW)); + } + + *pi64Num = -(FLMINT64)ui64Num; + } + else + { + if( ui64Num > gv_ui64MaxSignedIntVal) + { + return( RC_SET( NE_XFLM_CONV_NUM_OVERFLOW)); + } + + *pi64Num = (FLMINT64)ui64Num; + } + + return( NE_XFLM_OK); +} + +/***************************************************************************** +Desc: Calculate the number of bytes extra there are beyond the closest + encryption boundary. +******************************************************************************/ +FINLINE FLMUINT extraEncBytes( + FLMUINT uiDataLen) +{ + // This works if ENCRYPT_MIN_CHUNK_SIZE is a power of 2 + // Otherwise, it needs to be changed to uiDataLen % ENCRYPT_MIN_CHUNK_SIZE + + return( uiDataLen & (ENCRYPT_MIN_CHUNK_SIZE - 1)); +} + +/***************************************************************************** +Desc: Calculate the encryption length for a piece of data. +******************************************************************************/ +FINLINE FLMUINT getEncLen( + FLMUINT uiDataLen) +{ + return( extraEncBytes( uiDataLen) + ? ((uiDataLen + ENCRYPT_MIN_CHUNK_SIZE) & ENCRYPT_BOUNDARY_MASK) + : uiDataLen); +} + +/*============================================================================ +Some simple inline functions for dealing with tag numbers +============================================================================*/ + +FINLINE FLMBOOL elementIsUserDefined( + FLMUINT uiNum + ) +{ + return( uiNum <= XFLM_MAX_ELEMENT_NUM ? TRUE : FALSE); +} + +FINLINE FLMBOOL attributeIsUserDefined( + FLMUINT uiNum + ) +{ + return( uiNum <= XFLM_MAX_ATTRIBUTE_NUM ? TRUE : FALSE); +} + +FINLINE FLMBOOL elementIsReservedTag( + FLMUINT uiNum + ) +{ + return( uiNum >= XFLM_FIRST_RESERVED_ELEMENT_TAG && + uiNum <= XFLM_LAST_RESERVED_ELEMENT_TAG ? TRUE : FALSE); +} + +FINLINE FLMBOOL attributeIsReservedTag( + FLMUINT uiNum + ) +{ + return( uiNum >= XFLM_FIRST_RESERVED_ATTRIBUTE_TAG && + uiNum <= XFLM_LAST_RESERVED_ATTRIBUTE_TAG ? 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 XF_RefCount, public XF_Base +{ +public: + + F_NameTable(); + + ~F_NameTable(); + + RCODE setupNameTable( void); + + void clearTable( + FLMUINT uiPoolBlkSize); + + RCODE addReservedDictTags( void); + + RCODE 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 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 getFromTagTypeAndName( + F_Db * pDb, + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMBOOL bMatchNamespace, + const FLMUNICODE * puzNamespace = NULL, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiDataType = NULL); + + RCODE getFromTagTypeAndNum( + F_Db * pDb, + 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 addTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType = 0, + FLMUNICODE * puzNamespace = NULL, + FLMBOOL bCheckDuplicates = TRUE, + FLMBOOL bLimitNumToLoad = TRUE); + + void sortTags( void); + + void removeTag( + FLMUINT uiType, + FLMUINT uiTagNum); + + RCODE cloneNameTable( + F_NameTable * pSrcNameTable); + + RCODE importFromNameTable( + F_NameTable * pSrcNameTable); + + FINLINE FLMBOOL haveAllElements( void) + { + return m_bLoadedAllElements; + } + + FINLINE FLMBOOL haveAllAttributes( void) + { + return m_bLoadedAllAttributes; + } + + // Override the AddRef and Release provided in XF_RefCount. + // These need to be thread-safe. + + FLMUINT32 XFLMAPI AddRef( void); + + FLMUINT32 XFLMAPI Release( void); + +private: + + 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; + FLMBOOL m_bLoadedAllElements; + FLMBOOL m_bLoadedAllAttributes; + FLMUINT m_uiNumElementsLoaded; + FLMUINT m_uiNumAttributesLoaded; + FLMUNICODE ** m_ppuzNamespaces; + FLMUINT m_uiNamespaceTblSize; + FLMUINT m_uiNumNamespaces; + F_MUTEX m_hRefMutex; + +friend class F_Db; +}; + +/**************************************************************************** +Storage conversion functions. +****************************************************************************/ + +#define FLM_MAX_NUM_BUF_SIZE 9 + +RCODE flmStorage2Number( + FLMUINT uiType, + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT * puiNum, + FLMINT * piNum); + +RCODE flmStorage2Number64( + FLMUINT uiType, + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT64 * pui64Num, + FLMINT64 * pi64Num); + +RCODE flmNumber64ToStorage( + FLMUINT64 ui64Num, + FLMUINT * puiBufLen, + FLMBYTE * pucBuf, + FLMBOOL bNegative, + FLMBOOL bCollation); + +RCODE FlmUINT2Storage( + FLMUINT uiNum, + FLMUINT * puiBufLen, + FLMBYTE * pucBuf); + +RCODE FlmINT2Storage( + FLMINT iNum, + FLMUINT * puiBufLen, + FLMBYTE * pucBuf); + +RCODE flmUTF8ToStorage( + const FLMBYTE * pucUTF8, + FLMUINT uiBytesInBuffer, + FLMBYTE * pucBuf, + FLMUINT * puiBufLength); + +RCODE flmGetCharCountFromStorageBuf( + const FLMBYTE ** ppucBuf, + FLMUINT uiBufSize, + FLMUINT * puiNumChars, + FLMUINT * puiSenLen = NULL); + +RCODE flmStorage2UTF8( + FLMUINT uiType, + FLMUINT uiBufLength, + const FLMBYTE * pucBuffer, + FLMUINT * puiOutBufLen, + FLMBYTE * pucOutBuf); + +RCODE flmStorage2Unicode( + FLMUINT uiType, + FLMUINT uiBufLength, + const FLMBYTE * pucBuffer, + FLMUINT * puiOutBufLen, + void * pOutBuf); + +RCODE flmStorage2Unicode( + FLMUINT uiType, + FLMUINT uiStorageLength, + const FLMBYTE * pucStorageBuffer, + IF_DynaBuf * pBuffer); + +RCODE flmUnicode2Storage( + const FLMUNICODE * puzStr, + FLMUINT uiStrLen, + FLMBYTE * pucBuf, + FLMUINT * puiBufLength, + FLMUINT * puiCharCount); + +RCODE flmStorageNum2StorageText( + const FLMBYTE * pucNum, + FLMUINT uiNumLen, + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen); + +/**************************************************************************** +Desc: Returns the size of buffer needed to hold the unicode string in + FLAIM's storage format. +****************************************************************************/ +FINLINE RCODE FlmGetUnicodeStorageLength( + FLMUNICODE * puzStr, + FLMUINT * puiByteCount) +{ + FLMUINT uiByteCount; + RCODE rc; + + if( RC_BAD( rc = flmUnicode2Storage( puzStr, 0, NULL, + &uiByteCount, NULL))) + { + return( rc); + } + + *puiByteCount = uiByteCount + sizeof( FLMUNICODE); + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Copies and formats a Unicode string into FLAIM's storage format. + The Unicode string must be in little-endian byte order. + Unicode values that are not represented as WordPerfect 6.x characters + are preserved as non-WP characters. +****************************************************************************/ +FINLINE RCODE FlmUnicode2Storage( + FLMUNICODE * puzStr, + FLMUINT * puiBufLength, // [IN] size of pBuf, + // [OUT] amount of pBuf used to hold puzStr + FLMBYTE * pBuf) +{ + return( flmUnicode2Storage( puzStr, 0, pBuf, puiBufLength, NULL)); +} + +/**************************************************************************** +Desc: Converts from FLAIM's internal storage format to a Unicode string +****************************************************************************/ +FINLINE RCODE FlmStorage2Unicode( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiOutBufLen, + FLMUNICODE * puzOutBuf) +{ + return( flmStorage2Unicode( uiType, uiBufLength, pBuffer, + puiOutBufLen, puzOutBuf)); +} + +/**************************************************************************** +Desc: Convert storage text into a null-terminated UTF-8 string +****************************************************************************/ +FINLINE RCODE FlmStorage2UTF8( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiOutBufLenRV, + // [IN] Specified the number of bytes available in buffer. + // [OUT] Returns the number of bytes of UTF-8 text. This value + // does not include a terminating NULL byte in the buffer. + FLMBYTE * pOutBuffer) + // [OUT] The buffer that will hold the output UTF-8 text. + // If this value is NULL then only bufLenRV will computed so that + // the caller may know how many bytes to allocate for a buffer. +{ + return( flmStorage2UTF8( uiType, uiBufLength, + pBuffer, puiOutBufLenRV, pOutBuffer)); +} + +typedef struct FlmVectorElementTag +{ + FLMUINT64 ui64ID; + FLMUINT uiNameId; + FLMUINT uiFlags; +#define VECT_SLOT_HAS_DATA 0x01 +#define VECT_SLOT_HAS_ID 0x02 +#define VECT_SLOT_RIGHT_TRUNCATED 0x04 +#define VECT_SLOT_LEFT_TRUNCATED 0x08 +#define VECT_SLOT_HAS_NAME_ID 0x10 +#define VECT_SLOT_IS_ATTR 0x20 +#define VECT_SLOT_IS_DATA 0x40 + FLMUINT uiDataType; + FLMUINT uiDataLength; + FLMUINT uiDataOffset; +} F_VECTOR_ELEMENT; + +/***************************************************************************** +Desc: Used to build keys and data components +*****************************************************************************/ +class F_DataVector : public IF_DataVector, public XF_Base +{ +public: + + // Constructor/Destructor + + F_DataVector(); + virtual ~F_DataVector(); + + // Setter methods + + FINLINE void XFLMAPI setDocumentID( + FLMUINT64 ui64DocumentID) + { + m_ui64DocumentID = ui64DocumentID; + } + + RCODE XFLMAPI setID( + FLMUINT uiElementNumber, + FLMUINT64 ui64ID); + + RCODE XFLMAPI setNameId( + FLMUINT uiElementNumber, + FLMUINT uiNameId, + FLMBOOL bIsAttr, + FLMBOOL bIsData); + + RCODE XFLMAPI setINT( + FLMUINT uiElementNumber, + FLMINT iNum); + + RCODE XFLMAPI setINT64( + FLMUINT uiElementNumber, + FLMINT64 i64Num); + + RCODE XFLMAPI setUINT( + FLMUINT uiElementNumber, + FLMUINT uiNum); + + RCODE XFLMAPI setUINT64( + FLMUINT uiElementNumber, + FLMUINT64 ui64Num); + + RCODE XFLMAPI setUnicode( + FLMUINT uiElementNumber, + const FLMUNICODE * puzUnicode); + + RCODE XFLMAPI setUTF8( + FLMUINT uiElementNumber, + const FLMBYTE * pszUtf8, + FLMUINT uiBytesInBuffer = 0); + + FINLINE RCODE XFLMAPI setBinary( + FLMUINT uiElementNumber, + const void * pvBinary, + FLMUINT uiBinaryLen) + { + return( storeValue( uiElementNumber, + XFLM_BINARY_TYPE, (FLMBYTE *)pvBinary, uiBinaryLen)); + } + + FINLINE void XFLMAPI setRightTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + setRightTruncated( pVector); + } + else + { + // Need to set some data value before setting + // truncated. + flmAssert( 0); + } + } + + FINLINE void XFLMAPI setLeftTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + setLeftTruncated( pVector); + } + else + { + // Need to set some data value before setting + // truncated. + flmAssert( 0); + } + } + + FINLINE void XFLMAPI clearRightTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + clearRightTruncated( pVector); + } + else + { + // Need to set some data value before clearing + // truncated. + flmAssert( 0); + } + } + + FINLINE void XFLMAPI clearLeftTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + clearLeftTruncated( pVector); + } + else + { + // Need to set some data value before clearing + // truncated. + flmAssert( 0); + } + } + + FINLINE FLMBOOL XFLMAPI isRightTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + return( isRightTruncated( pVector)); + } + + return( FALSE); + } + + FINLINE FLMBOOL XFLMAPI isLeftTruncated( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + return( isLeftTruncated( pVector)); + } + + return( FALSE); + } + + // Getter methods + + FINLINE FLMUINT64 XFLMAPI getDocumentID( void) + { + return( m_ui64DocumentID); + } + + FINLINE FLMUINT64 XFLMAPI getID( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_ID)) == NULL) + { + return( 0); + } + else + { + return( pVector->ui64ID); + } + } + + FINLINE FLMUINT XFLMAPI getNameId( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) + { + return( 0); + } + else + { + return( pVector->uiNameId); + } + } + + FINLINE FLMBOOL XFLMAPI isAttr( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) + { + return( FALSE); + } + else + { + return( (pVector->uiFlags & VECT_SLOT_IS_ATTR) ? TRUE : FALSE); + } + } + + FINLINE FLMBOOL XFLMAPI isDataComponent( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) + { + return( FALSE); + } + else + { + return( (pVector->uiFlags & VECT_SLOT_IS_DATA) ? TRUE : FALSE); + } + } + + FINLINE FLMBOOL XFLMAPI isKeyComponent( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_NAME_ID)) == NULL) + { + return( FALSE); + } + else + { + return( (pVector->uiFlags & VECT_SLOT_IS_DATA) ? FALSE : TRUE); + } + } + + FLMUINT XFLMAPI getDataLength( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) == NULL) + { + return( 0); + } + else + { + return( pVector->uiDataLength); + } + } + + FLMUINT XFLMAPI getDataType( + FLMUINT uiElementNumber) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) == NULL) + { + return( XFLM_UNKNOWN_TYPE); + } + else + { + return( pVector->uiDataType); + } + } + + RCODE XFLMAPI getUTF8Ptr( + FLMUINT uiElementNumber, + const FLMBYTE ** ppszUTF8, + FLMUINT * puiBufLen); + + FINLINE RCODE XFLMAPI getINT( + FLMUINT uiElementNumber, + FLMINT * piNum) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Number( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + NULL, piNum) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getINT64( + FLMUINT uiElementNumber, + FLMINT64 * pi64Num) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Number64( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + NULL, pi64Num) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getUINT( + FLMUINT uiElementNumber, + FLMUINT * puiNum) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Number( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + puiNum, NULL) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getUINT64( + FLMUINT uiElementNumber, + FLMUINT64 * pui64Num) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Number64( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + pui64Num, NULL) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + FLMUNICODE ** ppuzUnicode); + + FINLINE RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + IF_DynaBuf * pBuffer) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Unicode( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + pBuffer) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + FLMUNICODE * puzUnicode, + FLMUINT * puiBufLen) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2Unicode( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + puiBufLen, puzUnicode) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getUTF8( + FLMUINT uiElementNumber, + FLMBYTE * pszUTF8, + FLMUINT * puiBufLen) + { + F_VECTOR_ELEMENT * pVector; + + return( (RCODE)((pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA)) != NULL + ? flmStorage2UTF8( pVector->uiDataType, + pVector->uiDataLength, + (FLMBYTE *)getDataPtr( pVector), + puiBufLen, pszUTF8) + : RC_SET( NE_XFLM_NOT_FOUND))); + } + + FINLINE RCODE XFLMAPI getBinary( + FLMUINT uiElementNumber, + void * pvBuffer, + FLMUINT * puiLength) + { + F_VECTOR_ELEMENT * pVector; + + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + *puiLength = f_min( (*puiLength), pVector->uiDataLength); + if (pvBuffer && *puiLength) + { + f_memcpy( pvBuffer, getDataPtr( pVector), *puiLength); + } + + return( NE_XFLM_OK); + } + else + { + *puiLength = 0; + } + + return( RC_SET( NE_XFLM_NOT_FOUND)); + } + + FINLINE RCODE XFLMAPI getBinary( + FLMUINT uiElementNumber, + IF_DynaBuf * pBuffer) + { + F_VECTOR_ELEMENT * pVector; + + pBuffer->truncateData( 0); + if ((pVector = getVector( uiElementNumber, VECT_SLOT_HAS_DATA)) != NULL) + { + return( pBuffer->appendData( getDataPtr( pVector), + pVector->uiDataLength)); + } + + return( RC_SET( NE_XFLM_NOT_FOUND)); + } + + RCODE XFLMAPI outputKey( + IF_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT uiMatchFlags, + FLMBYTE * pucKeyBuf, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen); + + RCODE XFLMAPI outputData( + IF_Db * pDb, + FLMUINT uiIndexNum, + FLMBYTE * pucDataBuf, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen); + + RCODE XFLMAPI inputKey( + IF_Db * pDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen); + + RCODE XFLMAPI inputData( + IF_Db * pDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucData, + FLMUINT uiDataLen); + + // Miscellaneous methods + + void XFLMAPI reset( void); + + FINLINE const void * XFLMAPI getDataPtr( + FLMUINT uiElementNumber) + { + return( getDataPtr( getVector( uiElementNumber, VECT_SLOT_HAS_DATA))); + } + +private: + + RCODE allocVectorArray( + FLMUINT uiElementNumber); + + RCODE storeValue( + FLMINT uiElementNumber, + FLMUINT uiDataType, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBYTE ** ppucDataPtr = NULL); + + FINLINE F_VECTOR_ELEMENT * getVector( + FLMUINT uiElementNumber, + FLMUINT uiTestFlags) + { + F_VECTOR_ELEMENT * pVector; + + if (uiElementNumber >= m_uiNumElements) + { + return( NULL); + } + pVector = &m_pVectorElements [uiElementNumber]; + if (!(pVector->uiFlags & uiTestFlags)) + { + return( NULL); + } + else + { + return( pVector); + } + } + + FINLINE FLMBOOL isRightTruncated( + F_VECTOR_ELEMENT * pVector) + { + return( (pVector->uiFlags & VECT_SLOT_RIGHT_TRUNCATED) + ? TRUE + : FALSE); + } + + FINLINE void setRightTruncated( + F_VECTOR_ELEMENT * pVector) + { + pVector->uiFlags |= VECT_SLOT_RIGHT_TRUNCATED; + } + + FINLINE void clearRightTruncated( + F_VECTOR_ELEMENT * pVector) + { + pVector->uiFlags &= (~(VECT_SLOT_RIGHT_TRUNCATED)); + } + + FINLINE FLMBOOL isLeftTruncated( + F_VECTOR_ELEMENT * pVector) + { + return( (pVector->uiFlags & VECT_SLOT_LEFT_TRUNCATED) + ? TRUE + : FALSE); + } + + FINLINE void setLeftTruncated( + F_VECTOR_ELEMENT * pVector) + { + pVector->uiFlags |= VECT_SLOT_LEFT_TRUNCATED; + } + + FINLINE void clearLeftTruncated( + F_VECTOR_ELEMENT * pVector) + { + pVector->uiFlags &= (~(VECT_SLOT_LEFT_TRUNCATED)); + } + + FINLINE void * getDataPtr( + F_VECTOR_ELEMENT * pVector) + { + if (!pVector || !pVector->uiDataLength) + { + return( NULL); + } + else if (pVector->uiDataLength <= sizeof( FLMUINT)) + { + return( (void *)&pVector->uiDataOffset); + } + else + { + return( (void *)(m_pucDataBuf + pVector->uiDataOffset)); + } + } + + RCODE outputKey( + IXD * pIxd, + FLMUINT uiMatchFlags, + FLMBYTE * pucKeyBuf, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT uiSearchKeyFlag); + + RCODE outputData( + IXD * pIxd, + FLMBYTE * pucDataBuf, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen); + + RCODE inputKey( + IXD * pIxd, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen); + + RCODE inputData( + IXD * pIxd, + const FLMBYTE * pucData, + FLMUINT uiDataLen); + +#define MIN_VECTOR_ELEMENTS 6 + F_VECTOR_ELEMENT m_VectorArray [MIN_VECTOR_ELEMENTS]; + F_VECTOR_ELEMENT * m_pVectorElements; // Pointer to vector elements + FLMUINT m_uiVectorArraySize; // Size of vector array + FLMUINT m_uiNumElements; // Number of elements actually + // populated in the array. + + FLMBYTE m_ucIntDataBuf[ 32]; // Internal data buffer + FLMBYTE * m_pucDataBuf; // Values stored here + FLMUINT m_uiDataBufLength; // Bytes of data allocated + FLMUINT m_uiDataBufOffset; // Current offset into allocated + // data buffer. + FLMUINT64 m_ui64DocumentID; // Document ID; + +friend class F_Db; +friend class FSIndexCursor; +friend class FSCollectionCursor; +friend class F_QueryResultSet; +}; + +// Flags for the m_uiFlags member of the F_Db object + +#define FDB_UPDATED_DICTIONARY 0x0001 + // Flag indicating whether the file's + // local dictionary was updated + // during the transaction. +#define FDB_DO_TRUNCATE 0x0002 + // Truncate log extents at the end + // of a transaction. +#define FDB_HAS_FILE_LOCK 0x0004 + // FDB has a file lock. +#define FDB_FILE_LOCK_SHARED 0x0008 + // File lock is shared. Update + // transactions are not allowed when + // the lock is shared. +#define FDB_FILE_LOCK_IMPLICIT 0x0010 + // File lock is implicit - means file + // lock was obtained when the update + // transaction began and cannot be + // released by a call to FlmDbUnlock. +#define FDB_DONT_KILL_TRANS 0x0020 + // Do not attempt to kill an active + // read transaction on this database + // handle. This is used by FlmDbBackup. +#define FDB_INTERNAL_OPEN 0x0040 + // FDB is an internal one used by a + // background thread. +#define FDB_DONT_POISON_CACHE 0x0080 + // If blocks are read from disk during + // a transaction, release them at the LRU + // end of the cache chain. +#define FDB_UPGRADING 0x0100 + // Database is being upgraded. +#define FDB_REPLAYING_RFL 0x0200 + // Database is being recovered +#define FDB_REPLAYING_COMMIT 0x0400 + // During replay of the RFL, this + // is an actual call to FlmDbTransCommit. +#define FDB_BACKGROUND_INDEXING 0x0800 + // FDB is being used by a background indexing + // thread +#define FDB_HAS_WRITE_LOCK 0x1000 + // FDB has the write lock +#define FDB_REBUILDING_DATABASE 0x2000 + // Database is being rebuilt +#define FDB_SWEEP_SCHEDULED 0x4000 + // Sweep operation scheduled due to a + // dictionary change during the transaction + +class F_ThreadInfo : public IF_ThreadInfo, public XF_Base +{ +public: + + F_ThreadInfo() + { + m_Pool.poolInit( 1024); + m_uiNumThreads = 0; + m_pThreadInfoArray = NULL; + } + + virtual ~F_ThreadInfo() + { + m_Pool.poolFree(); + } + + FLMUINT XFLMAPI getNumThreads( void) + { + return( m_uiNumThreads); + } + + FINLINE void XFLMAPI getThreadInfo( + FLMUINT uiThreadNum, + FLMUINT * puiThreadId, + FLMUINT * puiThreadGroup, + FLMUINT * puiAppId, + FLMUINT * puiStartTime, + const char ** ppszThreadName, + const char ** ppszThreadStatus) + { + if (uiThreadNum < m_uiNumThreads) + { + F_THREAD_INFO * pThrdInfo = &m_pThreadInfoArray [uiThreadNum]; + + *puiThreadId = pThrdInfo->uiThreadId; + *puiThreadGroup = pThrdInfo->uiThreadGroup; + *puiAppId = pThrdInfo->uiAppId; + *puiStartTime = pThrdInfo->uiStartTime; + *ppszThreadName = pThrdInfo->pszThreadName; + *ppszThreadStatus = pThrdInfo->pszThreadStatus; + } + else + { + *puiThreadId = 0; + *puiThreadGroup = 0; + *puiAppId = 0; + *puiStartTime = 0; + *ppszThreadName = NULL; + *ppszThreadStatus = NULL; + } + } + +private: + + F_Pool m_Pool; + F_THREAD_INFO * m_pThreadInfoArray; + FLMUINT m_uiNumThreads; + +friend class F_DbSystem; + +}; + +/***************************************************************************** +Desc: Class for performing database backup. +*****************************************************************************/ +class F_Backup : public IF_Backup, public XF_Base +{ +public: + + F_Backup(); + virtual ~F_Backup(); + + FINLINE FLMUINT64 XFLMAPI getBackupTransId( void) + { + return( m_ui64TransId); + } + + FINLINE FLMUINT64 XFLMAPI getLastBackupTransId( void) + { + return( m_ui64LastBackupTransId); + } + + RCODE XFLMAPI backup( + const char * pszBackupPath, + const char * pszPassword, + IF_BackupClient * pClient, + IF_BackupStatus * pStatus, + FLMUINT * puiIncSeqNum); + + RCODE XFLMAPI endBackup( void); + +private: + + void reset( void); + + F_Db * m_pDb; + eDbTransType m_eTransType; + FLMUINT64 m_ui64TransId; + FLMUINT64 m_ui64LastBackupTransId; + FLMUINT m_uiDbVersion; + FLMUINT m_uiBlkChgSinceLastBackup; + FLMBOOL m_bTransStarted; + FLMUINT m_uiBlockSize; + FLMUINT m_uiLogicalEOF; + FLMUINT m_uiFirstReqRfl; + FLMUINT m_uiIncSeqNum; + FLMBOOL m_bCompletedBackup; + eDbBackupType m_eBackupType; + RCODE m_backupRc; + FLMBYTE m_ucNextIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; + char m_szDbPath[ F_PATH_MAX_SIZE]; + XFLM_DB_HDR m_dbHdr; + +friend class F_Db; +}; + + +/***************************************************************************** +Desc: An implementation of IF_Backup_Client that backs up to the + local hard disk. +*****************************************************************************/ +class F_DefaultBackupClient : public IF_BackupClient, public XF_Base +{ +public: + + F_DefaultBackupClient( + const char * pszBackupPath); + + virtual ~F_DefaultBackupClient(); + + RCODE XFLMAPI WriteData( + const void * pvBuffer, + FLMUINT uiBytesToWrite); + + FINLINE FLMUINT getRefCount( void) + { + return( IF_BackupClient::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_BackupClient::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_BackupClient::Release()); + } + +private: + + char m_szPath[ F_PATH_MAX_SIZE]; + F_64BitFileHandle * m_pFileHdl64; + FLMUINT64 m_ui64Offset; + RCODE m_rc; +}; + +/***************************************************************************** +Desc: The F_FSRestore class is used to read backup and RFL files from + a disk file system. +*****************************************************************************/ +class F_FSRestore : public IF_RestoreClient, public XF_Base +{ +public: + + virtual ~F_FSRestore(); + F_FSRestore(); + + RCODE setup( + const char * pszDbPath, + const char * pszBackupSetPath, + const char * pszRflDir); + + RCODE XFLMAPI openBackupSet( void); + + RCODE XFLMAPI openIncFile( + FLMUINT uiFileNum); + + RCODE XFLMAPI openRflFile( + FLMUINT uiFileNum); + + RCODE XFLMAPI read( + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE XFLMAPI close( void); + + RCODE XFLMAPI abortFile( void); + + FINLINE FLMUINT getRefCount( void) + { + return( IF_RestoreClient::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_RestoreClient::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_RestoreClient::Release()); + } + +protected: + + IF_FileHdl * m_pFileHdl; + F_64BitFileHandle * m_pFileHdl64; + FLMUINT64 m_ui64Offset; + FLMUINT m_uiDbVersion; + char m_szDbPath[ F_PATH_MAX_SIZE]; + char m_szBackupSetPath[ F_PATH_MAX_SIZE]; + char m_szRflDir[ F_PATH_MAX_SIZE]; + FLMBOOL m_bSetupCalled; + FLMBOOL m_bOpen; +}; + +/***************************************************************************** +Desc: Default implementation of a restore status object than can + be inherited by a user implementation. +*****************************************************************************/ +class F_DefaultRestoreStatus : public IF_RestoreStatus, public XF_Base +{ +public: + + F_DefaultRestoreStatus() + { + } + + RCODE XFLMAPI reportProgress( + eRestoreAction * peAction, + FLMUINT64, // ui64BytesToDo, + FLMUINT64) // ui64BytesDone + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportError( + eRestoreAction * peAction, + RCODE) // rcErr + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportBeginTrans( + eRestoreAction * peAction, + FLMUINT64) // ui64TransId + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportCommitTrans( + eRestoreAction * peAction, + FLMUINT64) // ui64TransId + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportAbortTrans( + eRestoreAction * peAction, + FLMUINT64) // ui64TransId + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportRemoveData( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiLfNum, + FLMUINT, // uiKeyLen, + FLMBYTE *) // pucKey + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportInsertData( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiLfNum, + FLMUINT, // uiKeyLen, + FLMBYTE *) // pucKey + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportReplaceData( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiLfNum, + FLMUINT, // uiKeyLen, + FLMBYTE *) // pucKey + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportLFileCreate( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT) // uiLfNum + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportLFileUpdate( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiLfNum, + FLMUINT, // uiRootBlk, + FLMUINT64, // ui64NextNodeId, + FLMUINT64, // ui64FirstDocId, + FLMUINT64 // ui64LastDocId + ) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportUpdateDict( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiDictType, + FLMUINT, // uiDictNum, + FLMBOOL) // bDeleting + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportIndexSuspend( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT) // uiIndexNum + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportIndexResume( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT) // uiIndexNum + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportReduce( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT) // uiCount + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportUpgrade( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiOldDbVersion, + FLMUINT) // uiNewDbVersion + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportEnableEncryption( + eRestoreAction * peAction, + FLMUINT64 // ui64TransId + ) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportWrapKey( + eRestoreAction * peAction, + FLMUINT64) // ui64TransId + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportOpenRflFile( + eRestoreAction * peAction, + FLMUINT) // uiFileNum + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportRflRead( + eRestoreAction * peAction, + FLMUINT, // uiFileNum, + FLMUINT) // uiBytesRead + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + FINLINE FLMUINT getRefCount( void) + { + return( IF_RestoreStatus::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_RestoreStatus::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_RestoreStatus::Release()); + } +}; + +// Indexing actions + +typedef enum IxActionTag +{ + IX_UNLINK_NODE = 0, + IX_LINK_NODE, + IX_DEL_NODE_VALUE, + IX_ADD_NODE_VALUE, + IX_LINK_AND_ADD_NODE +} IxAction; + +typedef struct ElmAttrStateInfo +{ + FLMUINT uiDictType; + FLMUINT uiDictNum; + FLMUINT uiState; + FLMUINT64 ui64StateChangeCount; +} ELM_ATTR_STATE_INFO; + +typedef struct KEY_GEN_INFO +{ + FLMUINT64 ui64DocumentID; + IXD * pIxd; + FLMBOOL bIsAsia; + FLMBOOL bIsCompound; + CDL_HDR * pCdlTbl; + FLMBOOL bUseSubtreeNodes; + FLMBOOL bAddKeys; + FLMBYTE * pucKeyBuf; + FLMBYTE * pucData; + FLMUINT uiDataBufSize; + FLMBOOL bDataBufAllocated; +} KEY_GEN_INFO; + +typedef struct OLD_NODE_DATA +{ + eDomNodeType eNodeType; + FLMUINT uiCollection; + FLMUINT64 ui64NodeId; + FLMUINT uiNameId; + FLMBYTE * pucData; + FLMUINT uiDataLen; +} OLD_NODE_DATA; + +/***************************************************************************** +Desc: Thread's database object - returned by dbOpen, dbCreate in F_DbSystem class +*****************************************************************************/ +class F_OldNodeList : public XF_RefCount, public XF_Base +{ +public: + + F_OldNodeList() + { + m_pNodeList = NULL; + m_uiListSize = 0; + m_uiNodeCount = 0; + m_pool.poolInit( 512); + } + + ~F_OldNodeList(); + + FLMBOOL findNodeInList( + eDomNodeType eNodeType, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiNameId, + FLMBYTE ** ppucData, + FLMUINT * puiDataLen, + FLMUINT * puiInsertPos); + + RCODE addNodeToList( + F_Db * pDb, + F_DOMNode * pNode); + + void resetList( void); + + FINLINE FLMUINT getNodeCount( void) + { + return( m_uiNodeCount); + } + +private: + + OLD_NODE_DATA * m_pNodeList; + F_Pool m_pool; + FLMUINT m_uiListSize; + FLMUINT m_uiNodeCount; +}; + +/***************************************************************************** +Desc: Thread's database object - returned by dbOpen, dbCreate in F_DbSystem class +*****************************************************************************/ +class F_Db : public IF_Db, public XF_Base +{ +public: + + F_Db( + FLMBOOL bInternalOpen); + + virtual ~F_Db(); + + RCODE XFLMAPI transBegin( + eDbTransType eTransType, + FLMUINT uiMaxLockWait = XFLM_NO_TIMEOUT, + FLMUINT uiFlags = 0, + XFLM_DB_HDR * pDbHeader = NULL); + + RCODE XFLMAPI transBegin( + IF_Db * pDb); + + RCODE XFLMAPI transCommit( + FLMBOOL * pbEmpty = NULL); + + RCODE XFLMAPI transAbort( void); + + FINLINE eDbTransType XFLMAPI getTransType( void) + { + return( m_eTransType); + } + + RCODE XFLMAPI doCheckpoint( + FLMUINT uiTimeout); + + RCODE XFLMAPI dbLock( + eDbLockType eLockType, + FLMINT iPriority, + FLMUINT uiTimeout); + + RCODE XFLMAPI dbUnlock( void); + + RCODE XFLMAPI getLockType( + eDbLockType * peLockType, + FLMBOOL * pbImplicit); + + RCODE XFLMAPI getLockInfo( + FLMINT iPriority, + eDbLockType * peCurrLockType, + FLMUINT * puiThreadId, + FLMUINT * puiNumExclQueued, + FLMUINT * puiNumSharedQueued, + FLMUINT * puiPriorityCount); + + RCODE dupTrans( + FLMUINT64 ui64TransId); + + RCODE demoteTrans( void); + + RCODE cancelTrans( + FLMUINT64 ui64TransId); + + RCODE getCommitCnt( + FLMUINT64 * pui64CommitCount); + + // Index methods + + RCODE XFLMAPI indexStatus( + FLMUINT uiIndexNum, + XFLM_INDEX_STATUS * pIndexStatus); + + RCODE XFLMAPI indexGetNext( + FLMUINT * puiIndexNum); + + RCODE XFLMAPI indexSuspend( + FLMUINT uiIndexNum); + + RCODE XFLMAPI indexResume( + FLMUINT uiIndexNum); + + // Retrieval Functions + + RCODE XFLMAPI keyRetrieve( + FLMUINT uiIndex, + IF_DataVector * ifpSearchKey, + FLMUINT uiFlags, + IF_DataVector * ifpFoundKey); + + RCODE XFLMAPI enableEncryption( void); + + RCODE XFLMAPI wrapKey( + const char * pszPassword = NULL); + + RCODE XFLMAPI rollOverDbKey( void); + + RCODE XFLMAPI changeItemState( + FLMUINT uiDictType, + FLMUINT uiDictNum, + const char * pszState); + + RCODE XFLMAPI reduceSize( + FLMUINT uiCount, + FLMUINT * puiCountRV); + + RCODE XFLMAPI upgrade( + IF_UpgradeClient * pUpgradeClient); + + RCODE XFLMAPI createRootElement( + FLMUINT uiCollection, + FLMUINT uiNameId, + IF_DOMNode ** ppElementNode, + FLMUINT64 * pui64NodeId = NULL); + + RCODE XFLMAPI createDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode, + FLMUINT64 * pui64NodeId = NULL); + + RCODE XFLMAPI getFirstDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode); + + RCODE XFLMAPI getLastDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode); + + RCODE XFLMAPI getDocument( + FLMUINT uiCollection, + FLMUINT uiFlags, + FLMUINT64 ui64DocumentId, + IF_DOMNode ** ppDocumentNode); + + RCODE XFLMAPI documentDone( + FLMUINT uiCollection, + FLMUINT64 ui64RootId); + + RCODE XFLMAPI documentDone( + IF_DOMNode * pDocNode); + + FINLINE RCODE XFLMAPI createElementDef( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT uiDataType, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( TRUE, FALSE, pszNamespaceURI, + pszElementName, uiDataType, FALSE, + puiElementNameId, (F_DOMNode **)ppDocumentNode)); + } + + FINLINE RCODE XFLMAPI createElementDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT uiDataType, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( TRUE, TRUE, puzNamespaceURI, + puzElementName, uiDataType, FALSE, + puiElementNameId, (F_DOMNode **)ppDocumentNode)); + } + + FINLINE RCODE XFLMAPI createUniqueElmDef( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( TRUE, FALSE, pszNamespaceURI, + pszElementName, XFLM_NODATA_TYPE, TRUE, + puiElementNameId, (F_DOMNode **)ppDocumentNode)); + } + + FINLINE RCODE XFLMAPI createUniqueElmDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( TRUE, TRUE, puzNamespaceURI, + puzElementName, XFLM_NODATA_TYPE, TRUE, + puiElementNameId, (F_DOMNode **)ppDocumentNode)); + } + + RCODE XFLMAPI getElementNameId( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT * puiElementNameId); + + RCODE XFLMAPI getElementNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT * puiElementNameId); + + FINLINE RCODE XFLMAPI createAttributeDef( + const char * pszNamespaceURI, + const char * pszAttributeName, + FLMUINT uiDataType, + FLMUINT * puiAttributeNameId, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( FALSE, FALSE, pszNamespaceURI, + pszAttributeName, uiDataType, FALSE, puiAttributeNameId, + (F_DOMNode **)ppDocumentNode)); + } + + FINLINE RCODE XFLMAPI createAttributeDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzAttributeName, + FLMUINT uiDataType, + FLMUINT * puiAttributeNameId, + IF_DOMNode ** ppDocumentNode = NULL) + { + return( createElemOrAttrDef( FALSE, TRUE, puzNamespaceURI, + puzAttributeName, uiDataType, FALSE, puiAttributeNameId, + (F_DOMNode **)ppDocumentNode)); + } + + RCODE XFLMAPI getAttributeNameId( + const char * pszNamespaceURI, + const char * pszAttributeName, + FLMUINT * puiAttributeNameId); + + RCODE XFLMAPI getAttributeNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzAttributeName, + FLMUINT * puiAttributeNameId); + + FINLINE RCODE XFLMAPI createPrefixDef( + const char * pszPrefixName, + FLMUINT * puiPrefixNumber) + { + return( createPrefixDef( FALSE, pszPrefixName, puiPrefixNumber)); + } + + FINLINE RCODE XFLMAPI createPrefixDef( + const FLMUNICODE * puzPrefixName, + FLMUINT * puiPrefixNumber) + { + return( createPrefixDef( TRUE, puzPrefixName, puiPrefixNumber)); + } + + RCODE XFLMAPI getPrefixId( + const char * pszPrefixName, + FLMUINT * puiPrefixNumber); + + RCODE XFLMAPI getPrefixId( + const FLMUNICODE * puzPrefixName, + FLMUINT * puiPrefixNumber); + + FINLINE RCODE XFLMAPI createEncDef( + const char * pszEncType, + const char * pszEncName, + FLMUINT uiKeySize = 0, + FLMUINT * puiEncDefNumber = NULL) + { + return( createEncDef( FALSE, pszEncType, pszEncName, + uiKeySize, puiEncDefNumber)); + } + + FINLINE RCODE XFLMAPI createEncDef( + const FLMUNICODE * puzEncType, + const FLMUNICODE * puzEncName, + FLMUINT uiKeySize = 0, + FLMUINT * puiEncDefNumber = NULL) + { + return( createEncDef( TRUE, puzEncType, puzEncName, + uiKeySize, puiEncDefNumber)); + } + + RCODE XFLMAPI getEncDefId( + const char * pszEncDefName, + FLMUINT * puiEncDefNumber); + + RCODE XFLMAPI getEncDefId( + const FLMUNICODE * puzEncDefName, + FLMUINT * puiEncDefNumber); + + FINLINE RCODE XFLMAPI createCollectionDef( + const char * pszCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncNumber = 0) + { + return( createCollectionDef( FALSE, pszCollectionName, + puiCollectionNumber, uiEncNumber)); + } + + FINLINE RCODE XFLMAPI createCollectionDef( + const FLMUNICODE * puzCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncNumber = 0) + { + return( createCollectionDef( TRUE, puzCollectionName, + puiCollectionNumber, uiEncNumber)); + } + + RCODE XFLMAPI getCollectionNumber( + const char * pszCollectionName, + FLMUINT * puiCollectionNumber); + + RCODE XFLMAPI getCollectionNumber( + const FLMUNICODE * puzCollectionName, + FLMUINT * puiCollectionNumber); + + RCODE XFLMAPI getIndexNumber( + const char * pszIndexName, + FLMUINT * puiIndexNumber); + + RCODE XFLMAPI getIndexNumber( + const FLMUNICODE * puzIndexName, + FLMUINT * puiIndexNumber); + + RCODE XFLMAPI getDictionaryDef( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + IF_DOMNode ** ppDocumentNode); + + RCODE XFLMAPI getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + char * pszName, + FLMUINT * puiNameBufSize, + char * pszNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL); + + RCODE XFLMAPI getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + FLMUNICODE * puzName, + FLMUINT * puiNameBufSize, + FLMUNICODE * puzNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL); + + RCODE XFLMAPI getNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_DOMNode ** ifppNode) + { + return( getNode( uiCollection, + ui64NodeId, XFLM_EXACT, (F_DOMNode **)ifppNode)); + } + + FINLINE RCODE getNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppNode) + { + return( getNode( uiCollection, ui64NodeId, XFLM_EXACT, ppNode)); + } + + FINLINE RCODE getNextNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppNode) + { + return( getNode( uiCollection, ui64NodeId, XFLM_EXCL, ppNode)); + } + + RCODE XFLMAPI getAttribute( + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiAttrName, + IF_DOMNode ** ppNode); + + RCODE XFLMAPI getDataType( + FLMUINT uiDictType, + FLMUINT uiNameId, + FLMUINT * puiDataType); + + RCODE XFLMAPI backupBegin( + eDbBackupType eBackupType, + eDbTransType eTransType, + FLMUINT uiMaxLockWait, + IF_Backup ** ppBackup); + + void XFLMAPI getRflFileName( + FLMUINT uiFileNum, + FLMBOOL bBaseOnly, + char * pszFileName, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated = NULL); + + RCODE XFLMAPI import( + IF_IStream * pIStream, + FLMUINT uiCollection, + IF_DOMNode * pNodeToLinkTo = NULL, + eNodeInsertLoc eInsertLoc = XFLM_LAST_CHILD, + XFLM_IMPORT_STATS * pImportStats = NULL); + + RCODE XFLMAPI importDocument( + IF_IStream * ifpStream, + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode = NULL, + XFLM_IMPORT_STATS * pImportStats = NULL); + + RCODE XFLMAPI exportXML( + IF_DOMNode * pStartNode, + IF_OStream * pOStream, + eExportFormatType eFormat = XFLM_EXPORT_INDENT); + + RCODE XFLMAPI setNextNodeId( + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId); + + RCODE XFLMAPI setNextDictNum( + FLMUINT uiDictType, + FLMUINT uiDictNumber); + + // Configuration methods + + RCODE XFLMAPI setRflKeepFilesFlag( + FLMBOOL bKeep); + + RCODE XFLMAPI getRflKeepFlag( + FLMBOOL * pbKeep); + + RCODE XFLMAPI setRflDir( + const char * pszNewRflDir); + + void XFLMAPI getRflDir( + char * pszRflDir); + + RCODE XFLMAPI getRflFileNum( + FLMUINT * puiRflFileNum); + + RCODE XFLMAPI getHighestNotUsedRflFileNum( + FLMUINT * puiHighestNotUsedRflFileNum); + + RCODE XFLMAPI setRflFileSizeLimits( + FLMUINT uiMinRflSize, + FLMUINT uiMaxRflSize); + + RCODE XFLMAPI getRflFileSizeLimits( + FLMUINT * puiRflMinFileSize, + FLMUINT * puiRflMaxFileSize); + + RCODE XFLMAPI rflRollToNextFile( void); + + RCODE XFLMAPI setKeepAbortedTransInRflFlag( + FLMBOOL bKeep); + + RCODE XFLMAPI getKeepAbortedTransInRflFlag( + FLMBOOL * pbKeep); + + RCODE XFLMAPI setAutoTurnOffKeepRflFlag( + FLMBOOL bAutoTurnOff); + + RCODE XFLMAPI getAutoTurnOffKeepRflFlag( + FLMBOOL * pbAutoTurnOff); + + FINLINE void XFLMAPI setFileExtendSize( + FLMUINT uiFileExtendSize) + { + m_pDatabase->m_uiFileExtendSize = uiFileExtendSize; + } + + FINLINE FLMUINT XFLMAPI getFileExtendSize( void) + { + return( m_pDatabase->m_uiFileExtendSize); + } + + FINLINE void XFLMAPI setAppData( + void * pvAppData) + { + m_pvAppData = pvAppData; + } + + FINLINE void * XFLMAPI getAppData( void) + { + return( m_pvAppData); + } + + FINLINE void XFLMAPI setDeleteStatusObject( + IF_DeleteStatus * pDeleteStatus) + { + if (m_pDeleteStatus) + { + m_pDeleteStatus->Release(); + } + if ((m_pDeleteStatus = pDeleteStatus) != NULL) + { + m_pDeleteStatus->AddRef(); + } + } + + FINLINE void XFLMAPI setCommitClientObject( + IF_CommitClient * pCommitClient) + { + if (m_pCommitClient) + { + m_pCommitClient->Release(); + } + + m_pCommitClient = pCommitClient; + + if (m_pCommitClient) + { + m_pCommitClient->AddRef(); + } + } + + FINLINE void XFLMAPI setIndexingClientObject( + IF_IxClient * pIxClient) + { + if (m_pIxClient) + { + m_pIxClient->Release(); + } + m_pIxClient = pIxClient; + if (m_pIxClient) + { + m_pIxClient->AddRef(); + } + } + + FINLINE void XFLMAPI setIndexingStatusObject( + IF_IxStatus * ifpIxStatus) + { + if (m_pIxStatus) + { + m_pIxStatus->Release(); + } + m_pIxStatus = ifpIxStatus; + if (m_pIxStatus) + { + m_pIxStatus->AddRef(); + } + } + + // Configuration information getting methods + + FINLINE FLMUINT XFLMAPI getDbVersion( void) + { + return( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion); + } + + FINLINE FLMUINT XFLMAPI getBlockSize( void) + { + return( m_pDatabase->m_uiBlockSize); + } + + FINLINE FLMUINT XFLMAPI getDefaultLanguage( void) + { + return( m_pDatabase->m_uiDefaultLanguage); + } + + FINLINE FLMUINT64 XFLMAPI getTransID( void) + { + if (m_eTransType != XFLM_NO_TRANS) + { + return( m_ui64CurrTransID); + } + else if (m_uiFlags & FDB_HAS_FILE_LOCK) + { + return( m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID); + } + + return( 0); + } + + void XFLMAPI getCheckpointInfo( + XFLM_CHECKPOINT_INFO * pCheckpointInfo); + + RCODE XFLMAPI getDbControlFileName( + char * pszControlFileName, + FLMUINT uiControlFileBufSize) + { + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen = f_strlen( m_pDatabase->m_pszDbPath); + + if (uiLen + 1 > uiControlFileBufSize) + { + uiLen = uiControlFileBufSize - 1; + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + } + f_memcpy( pszControlFileName, m_pDatabase->m_pszDbPath, uiLen); + pszControlFileName [uiLen] = 0; + return( rc); + } + + FINLINE FLMBOOL threadWaitingLock( void) + { + return( m_pDatabase->m_pDatabaseLockObj->ThreadWaitingLock()); + } + + RCODE XFLMAPI getLockWaiters( + IF_LockInfoClient * pLockInfo); + + RCODE XFLMAPI getLastBackupTransID( + FLMUINT64 * pui64LastBackupTransID); + + RCODE XFLMAPI getBlocksChangedSinceBackup( + FLMUINT * puiBlocksChangedSinceBackup); + + RCODE XFLMAPI getNextIncBackupSequenceNum( + FLMUINT * puiNextIncBackupSequenceNum); + + void XFLMAPI getSerialNumber( + char * pucSerialNumber); + + RCODE XFLMAPI getDiskSpaceUsage( + FLMUINT64 * pui64DataSize, + FLMUINT64 * pui64RollbackSize, + FLMUINT64 * pui64RflSize); + + FINLINE RCODE XFLMAPI getMustCloseRC( void) + { + return( m_pDatabase->m_rcMustClose); + } + + FINLINE RCODE XFLMAPI getAbortRC( void) + { + return( m_AbortRc); + } + + FINLINE RCODE startTransaction( + eDbTransType eReqTransType, + FLMBOOL * pbStartedTrans) + { + RCODE rc; + + if( m_eTransType != XFLM_NO_TRANS) + { + return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP)); + } + + if( !pbStartedTrans) + { + return( RC_SET( NE_XFLM_NO_TRANS_ACTIVE)); + } + + if( RC_BAD( rc = transBegin( eReqTransType))) + { + return( rc); + } + + *pbStartedTrans = TRUE; + return( NE_XFLM_OK); + } + + FINLINE RCODE checkTransaction( + eDbTransType eReqTransType, + FLMBOOL * pbStartedTrans) + { + if( m_AbortRc) + { + return( m_AbortRc); + } + else if( m_eTransType >= eReqTransType) + { + return( NE_XFLM_OK); + } + + return( startTransaction( eReqTransType, pbStartedTrans)); + } + + FINLINE void XFLMAPI setMustAbortTrans( + RCODE rc) + { + if( RC_BAD( rc) && RC_OK( m_AbortRc)) + { + m_AbortRc = rc; + } + } + + RCODE getDictionary( + F_Dict ** ppDict); + + FINLINE RCODE checkState( + const char * pszFileName, + FLMINT iLineNumber) + { + RCODE rc = NE_XFLM_OK; + + if (m_bMustClose) + { + m_pDatabase->logMustCloseReason( pszFileName, iLineNumber); + rc = RC_SET( NE_XFLM_MUST_CLOSE_DATABASE); + } + return( rc); + } + + RCODE getElmAttrInfo( + FLMUINT uiType, + FLMUINT64 ui64DocumentID, + F_AttrElmInfo * pDefInfo, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE getCollectionDef( + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT * puiEncId); + + RCODE getPrefixDef( + F_Dict * pDict, + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzPrefixName, + FLMUINT * puiPrefixNumber); + + RCODE getEncDefDef( + F_Dict * pDict, + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzEncDefName, + FLMUINT * puiEncDefNumber, + FLMUINT * puiEncDefKeySize, + F_CCS ** ppCcs); + + RCODE getIndexDef( + FLMUINT64 ui64DocumentID, + FLMUNICODE ** ppuzIndexName, + FLMUINT * puiIndexNumber, + FLMUINT * puiCollectionNumber, + FLMUINT * puiLanguage, + FLMUINT * puiFlags, + FLMUINT64 * pui64LastDocIndexed, + FLMUINT * puiEncId, + F_DOMNode ** ppNode, + FLMBOOL bOpeningDict, + FLMBOOL bDeleting); + + RCODE getIndexComponentDef( + F_Dict * pDict, + F_DOMNode * pElementNode, + FLMUINT uiElementId, + IXD * pIxd, + ICD * pIcd); + + RCODE getNameTable( + F_NameTable ** ppNameTable); + + FINLINE F_Database * getDatabase( void) + { + return m_pDatabase; + } + + RCODE backgroundIndexBuild( + F_Thread * pThread, + FLMBOOL * pbShutdown, + FLMINT * piErrorLine); + + FINLINE FLMUINT getLogicalEOF( void) + { + return( m_uiLogicalEOF); + } + + // Key Collector object, used when checking indexes + + FINLINE void setKeyCollector( + F_KeyCollector * pKeyColl) + { + m_pKeyColl = pKeyColl; + } + + FINLINE F_KeyCollector * getKeyCollector( void) + { + return m_pKeyColl; + } + + RCODE waitForMaintenanceToComplete( void); + + FINLINE F_Dict * getDict( void) + { + return( m_pDict); + } + + FINLINE F_OldNodeList * getOldNodeList( void) + { + return( m_pOldNodeList); + } + + void removeCollectionNodes( + FLMUINT uiCollection, + FLMUINT64 ui64TransId); + +private: + + RCODE createElemOrAttrDef( + FLMBOOL bElement, + FLMBOOL bUnicode, + const void * pvNamespaceURI, + const void * pvLocalName, + FLMUINT uiDataType, + FLMBOOL bUniqueChildElms, + FLMUINT * puiNameId, + F_DOMNode ** ppRootNode); + + RCODE createPrefixDef( + FLMBOOL bUnicode, + const void * pvName, + FLMUINT * puiPrefixNumber); + + RCODE createCollectionDef( + FLMBOOL bUnicode, + const void * pvName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncDefId); + + RCODE createEncDef( + FLMBOOL bUnicode, + const void * pvEncType, + const void * pvEncName, + FLMUINT uiKeySize, + FLMUINT * puiEncDefId); + + // This routine assumes that the database mutex is locked + FINLINE void linkToDict( + F_Dict * pDict) + { + if (pDict != m_pDict) + { + if (m_pDict) + { + unlinkFromDict(); + } + if ((m_pDict = pDict) != NULL) + { + pDict->incrUseCount(); + } + } + } + + // This routine assumes the database mutex is locked. + FINLINE void unlinkFromDict( void) + { + if (m_pDict) + { + + // If the use count goes to zero and the F_Dict is not the first one + // in the file's list or it is not linked to a file, unlink the F_Dict + // object from its database and delete it. + + if (!m_pDict->decrUseCount() && + (m_pDict->getPrev() || !m_pDict->getDatabase())) + { + m_pDict->unlinkFromDatabase(); + } + m_pDict = NULL; + } + } + + RCODE linkToDatabase( + F_Database * pDatabase); + + void unlinkFromDatabase(); + + RCODE initDbFiles( + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + XFLM_CREATE_OPTS * pCreateOpts); + + RCODE beginBackgroundTrans( + F_Thread * pThread); + + RCODE beginTrans( + eDbTransType eTransType, + FLMUINT uiMaxLockWait = XFLM_NO_TIMEOUT, + FLMUINT uiFlags = 0, + XFLM_DB_HDR * pDbHdr = NULL); + + RCODE beginTrans( + F_Db * pDb); + + RCODE commitTrans( + FLMUINT uiNewLogicalEOF, + FLMBOOL bForceCheckpoint, + FLMBOOL * pbEmpty = NULL); + + RCODE abortTrans( + FLMBOOL bOkToLogAbort = TRUE); + + RCODE readRollbackLog( + FLMUINT uiLogEOF, + FLMUINT * puiCurrAddr, + F_BLK_HDR * pBlkHdr, + FLMBOOL * pbIsBeforeImageBlk); + + RCODE processBeforeImage( + FLMUINT uiLogEOF, + FLMUINT * puiCurrAddrRV, + F_BLK_HDR * pBlkHdr, + FLMBOOL bDoingRecovery, + FLMUINT64 ui64MaxTransID); + + RCODE physRollback( + FLMUINT uiLogEOF, + FLMUINT uiFirstLogBlkAddr, + FLMBOOL bDoingRecovery, + FLMUINT64 ui64MaxTransID); + + void completeOpenOrCreate( + RCODE rc, + FLMBOOL bNewDatabase); + + RCODE startBackgroundIndexing( void); + + void unlinkFromTransList( + FLMBOOL bCommitting); + + RCODE lockExclusive( + FLMUINT uiMaxLockWait); + + void unlockExclusive( void); + + RCODE readDictionary( void); + + RCODE dictCreate( + const char * pszDictPath, + const char * pszDictBuf); + + RCODE dictOpen( void); + + RCODE dictReadLFH( void); + + RCODE dictReadDefs( + FLMUINT uiDictType); + + RCODE dictClone( void); + + RCODE createNewDict( void); + + FINLINE void getDbHdrInfo( + XFLM_DB_HDR * pDbHdr) + { + // IMPORTANT NOTE: Any changes to this method should also be + // mirrored with changes to the other getDbHdrInfo call - see below. + + m_ui64CurrTransID = pDbHdr->ui64CurrTransID; + m_uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; + + // If we are doing a read transaction, this is only needed + // if we are checking the database. + + m_uiFirstAvailBlkAddr = (FLMUINT)pDbHdr->ui32FirstAvailBlkAddr; + } + + FINLINE void getDbHdrInfo( + F_Db * pDb) + { + m_ui64CurrTransID = pDb->m_ui64CurrTransID; + m_uiLogicalEOF = pDb->m_uiLogicalEOF; + + // If we are doing a read transaction, this is only needed + // if we are checking the database. + + m_uiFirstAvailBlkAddr = pDb->m_uiFirstAvailBlkAddr; + } + + FINLINE FLMBOOL okToCommitTrans( void) + { + return( m_eTransType == XFLM_READ_TRANS || + m_AbortRc == NE_XFLM_OK + ? TRUE + : FALSE); + } + + RCODE processDupKeys( + IXD * pIxd); + + RCODE keysCommit( + FLMBOOL bCommittingTrans, + FLMBOOL bSortKeys = TRUE); + + RCODE refUpdate( + LFILE * pLFile, + IXD * pIxd, + KREF_ENTRY * pKrefEntry, + FLMBOOL bNormalUpdate); + + FINLINE RCODE flushKeys( void) + { + RCODE rc = NE_XFLM_OK; + + if( m_bKrefSetup) + { + if( m_uiKrefCount) + { + if (RC_BAD( rc = keysCommit( FALSE))) + { + goto Exit; + } + } + + m_pKrefReset = m_pKrefPool->poolMark(); + } + + Exit: + + return( rc); + } + + RCODE krefCntrlCheck( void); + + void krefCntrlFree( void); + + FINLINE FLMBOOL isKrefOverThreshold( void) + { + if( (((m_pKrefPool->getBlockSize() * 3) - 250) <= m_uiTotalKrefBytes) || + m_uiKrefCount > (m_uiKrefTblSize - 128)) + { + return( TRUE); + } + + return( FALSE); + } + + RCODE addToKrefTbl( + FLMUINT uiKeyLen, + FLMUINT uiDataLen); + + RCODE verifyKeyContext( + FLMBOOL * pbVerified); + + RCODE buildContext( + ICD * pIcd, + FLMUINT uiKeyLen, + FLMUINT uiDataLen); + + RCODE buildData( + ICD * pIcd, + FLMUINT uiKeyLen, + FLMUINT uiDataLen); + + RCODE finishKeyComponent( + ICD * pIcd, + FLMUINT uiKeyLen); + + RCODE genTextKeyComponents( + F_DOMNode * pNode, + ICD * pIcd, + FLMUINT uiKeyLen, + FLMBYTE ** ppucTmpBuf, + FLMUINT * puiTmpBufSize, + void ** ppvMark); + + RCODE genOtherKeyComponent( + F_DOMNode * pNode, + ICD * pIcd, + FLMUINT uiKeyLen); + + RCODE buildKeys( + ICD * pIcd, + FLMUINT uiKeyLen); + + RCODE buildKeys( + FLMUINT64 ui64DocumentID, + IXD * pIxd, + CDL_HDR * pCdlTbl, + FLMBOOL bUseSubtreeNodes, + FLMBOOL bAddKeys); + + RCODE genIndexKeys( + FLMUINT64 ui64DocumentID, + F_DOMNode * pNode, + IXD * pIxd, + ICD * pIcd, + IxAction eAction); + + RCODE updateIndexKeys( + FLMUINT uiCollectionNum, + F_DOMNode * pNode, + IxAction eAction, + FLMBOOL bStartOfUpdate, + FLMBOOL * pbIsIndexed = NULL); + + RCODE attrIsInIndexDef( + FLMUINT uiAttrNameId, + FLMBOOL * pbIsInIndexDef); + + void indexingAfterCommit( void); + + void indexingAfterAbort( void); + + RCODE addToStopList( + FLMUINT uiIndexNum); + + RCODE addToStartList( + FLMUINT uiIndexNum); + + void stopBackgroundIndexThread( + FLMUINT uiIndexNum, + FLMBOOL bWait, + FLMBOOL * pbStopped); + + RCODE startIndexBuild( + FLMUINT uiIndexNum); + + RCODE checkDictDefInfo( + FLMUINT64 ui64DocumentID, + FLMBOOL bDeleting, + FLMUINT * puiDictType, + FLMUINT * puiDictNumber); + + RCODE dictDocumentDone( + FLMUINT64 ui64DocumentID, + FLMBOOL bDeleting, + FLMUINT * puiDictDefType); + + RCODE outputContextKeys( + FLMUINT64 ui64DocumentId, + IXD * pIxd, + IX_CONTEXT * pIxContext, + IX_CONTEXT ** ppIxContextList); + + RCODE removeCdls( + FLMUINT64 ui64DocumentId, + IXD * pIxd, + IX_CONTEXT * pIxContext, + ICD * pRefIcd); + + RCODE indexDocument( + IXD * pIxd, + F_DOMNode * pDocNode); + + RCODE indexSetOfDocuments( + FLMUINT uiIndexNum, + FLMUINT64 ui64StartDocumentId, + FLMUINT64 ui64EndDocumentId, + IF_IxStatus * ifpIxStatus, + IF_IxClient * ifpIxClient, + XFLM_INDEX_STATUS * pIndexStatus, + FLMBOOL * pbHitEnd, + F_Thread * pThread = NULL); + + RCODE setIxStateInfo( + FLMUINT uiIndexNum, + FLMUINT64 ui64LastDocumentIndexed, + FLMUINT uiState); + + RCODE buildIndex( + FLMUINT uiIndexNum, + FLMUINT uiState); + + RCODE readBlkHdr( + FLMUINT uiBlkAddress, + F_BLK_HDR * pBlkHdr, + FLMINT * piType); + + XFLM_LFILE_STATS * getLFileStatPtr( + LFILE * pLFile); + + RCODE checkAndUpdateState( + eDomNodeType eNodeType, + FLMUINT uiNameId); + +#define FLM_UPD_ADD 0x00001 +#define FLM_UPD_INTERNAL_CHANGE 0x00004 + + RCODE findNode( + FLMUINT uiCollection, + FLMUINT64 * pui64NodeId, + FLMUINT uiFlags); + + RCODE getNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiFlags, + F_DOMNode ** ppNode); + + RCODE _updateNode( + F_CachedNode * pNode, + FLMUINT uiFlags); + + FINLINE RCODE updateNode( + F_CachedNode * pCachedNode, + FLMUINT uiFlags) + { + flmAssert( !m_pDatabase->m_pRfl->isLoggingEnabled()); + + if( uiFlags || pCachedNode->getCollection() == XFLM_DICT_COLLECTION) + { + return( _updateNode( pCachedNode, uiFlags)); + } + + if( !pCachedNode->nodeIsDirty()) + { + pCachedNode->setNodeDirty( this, FALSE); + } + + return( NE_XFLM_OK); + } + + RCODE getCachedBTree( + FLMUINT uiCollection, + F_Btree ** ppBTree); + + RCODE flushNode( + F_Btree * pBTree, + F_CachedNode * pNode); + + RCODE purgeNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId); + + RCODE allocNode( + eDomNodeType eNodeType, + F_DOMNode ** ppNode); + + RCODE createRootNode( + FLMUINT uiCollection, + FLMUINT uiElementNameId, + eDomNodeType eNodeType, + F_DOMNode ** ppNewNode, + FLMUINT64 * pui64NodeId = NULL); + + RCODE sweep( + F_Thread * pThread); + + RCODE sweepGatherList( + ELM_ATTR_STATE_INFO ** ppStateTbl, + FLMUINT * puiNumItems); + + RCODE sweepCheckElementState( + F_DOMNode * pElementNode, + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT * puiNumItems, + FLMBOOL * pbStartedTrans); + + RCODE sweepCheckAttributeStates( + F_DOMNode * pElementNode, + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT * puiNumItems, + FLMBOOL * pbStartedTrans); + + RCODE sweepFinalizeStates( + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT uiNumItems, + FLMBOOL * pbStartedTrans); + + RCODE flushDirtyNodes( void); + + RCODE flushDirtyNode( + F_CachedNode * pNode); + + RCODE maintBlockChainFree( + FLMUINT64 ui64MaintDocID, + FLMUINT uiBlocksToDelete, + FLMUINT uiExpectedEndAddr, + FLMUINT * puiBlocksFreed); + + RCODE encryptData( + FLMUINT uiEncDefId, + FLMBYTE * pucIV, + FLMBYTE * pucBuffer, + FLMUINT uiBufferSize, + FLMUINT uiDataLen, + FLMUINT * puiEncryptedLength); + + RCODE decryptData( + FLMUINT uiEncDefId, + FLMBYTE * pucIV, + void * pvInBuf, + FLMUINT uiInLen, + void * pvOutBuf, + FLMUINT uiOutBufLen); + + RCODE createDbKey(); + + // Private data members. + + F_Database * m_pDatabase; // Pointer to F_Database object + F_Dict * m_pDict; // Pointer to dictionary object + F_Db * m_pNextForDatabase; // Next F_Db associated with F_Database + // NOTE: gv_XFlmSysData.hShareMutex + // must be locked to set this + F_Db * m_pPrevForDatabase; // Prev F_Db associated with F_Database + // NOTE: gv_XFlmSysData.hShareMutex + // must be locked to set this + void * m_pvAppData; // Application data that is used + // to associate this F_Db with + // an object in the application + // space. + FLMUINT m_uiThreadId; // Thread that started the current + // transaction, if any. NOTE: + // Only set on transaction begin. + // Hence, if operations are performed + // by multiple threads, within the + // transaction, it will not necessarily + // reflect the thread that is currently + // using the F_Db. + FLMBOOL m_bMustClose; // An error has occurred that requires + // the application to stop using (close) + // this FDB + F_SuperFileHdl * m_pSFileHdl; // Pointer to the super file handle + FLMUINT m_uiFlags; // Flags for this F_Db. + + // TRANSACTION STATE STUFF + + FLMUINT m_uiTransCount; // Transaction counter for the F_Db. + // Incremented whenever a transaction + // is started on this F_Db. Used so + // that FLAIM can tell if an implicit + // transaction it started is still in + // effect. This should NOT be + // confused with update transaction + // IDs. + eDbTransType m_eTransType; // Type of transaction + RCODE m_AbortRc; // If not NE_XFLM_OK, transaction must be + // aborted. + FLMUINT64 m_ui64CurrTransID;// Current transaction ID. + FLMUINT m_uiFirstAvailBlkAddr; // Address of first block in avail list + FLMUINT m_uiLogicalEOF; // Current logical end of file. New + // blocks are allocated at this address. + FLMUINT m_uiUpgradeCPFileNum; + FLMUINT m_uiUpgradeCPOffset; + // RFL file number and offset to set + // RFL to during an upgrade operation + // that happens during a restore or + // recovery. + FLMUINT m_uiTransEOF; // Address of logical end of file + // when the last transaction + // committed. A block beyond this + // point in the file is going to be + // a new block and will not need to + // be logged. + KEY_GEN_INFO m_keyGenInfo; // Information for generating index + // keys. + F_TMSTAMP m_TransStartTime; // Transaction start time, for stats + + // KREF STUFF + + KREF_ENTRY ** m_pKrefTbl; // Pointer to KREF table, which is an array + // of KREF_ENTRY * pointers. + FLMUINT m_uiKrefTblSize; // KREF table size. + FLMUINT m_uiKrefCount; // Number of entries in KREF table that + // are currently used. + FLMUINT m_uiTotalKrefBytes; // Total number of entries allocated + // in the pool. + FLMBYTE * m_pucKrefKeyBuf; // Pointer to temporary key buffer. + FLMBOOL m_bKrefSetup; // True if the KRef table has been initialized. + F_Pool * m_pKrefPool; // Memory pool to use + FLMBOOL m_bReuseKrefPool; // Reuse pool instead of free it? + FLMBOOL m_bKrefCompoundKey; // True if a compound key has been processed. + void * m_pKrefReset; // Used to reset the Kref pool on + // indexing failures + F_Pool m_tmpKrefPool; // KREF pool to be used during + // read transactions - only used when + // checking indexes. + + // UPDATE TRANSACTION STUFF + + FLMBOOL m_bHadUpdOper; // Did this transaction have any + // updates? + FLMUINT m_uiBlkChangeCnt; // Number of times ScaLogPhysBlk has + // been called during this transaction. + // This is used by the cursor code to + // know when it is necessary to + // re-position in the B-Tree.0 + IXD_FIXUP * m_pIxdFixups; // List of indexes whose IXD needs + // to be restored to its prior + // state if the transaction aborts + // READ TRANSACTION STUFF + + F_Db * m_pNextReadTrans; // Next active read transaction for + // this database. + // NOTE: If uiKilledTime (see below) + // is non-zero, then transaction is + // in killed list. + F_Db * m_pPrevReadTrans; // Previous active read transaction + // for this database. + // NOTE: If m_uiKilledTime (see below) + // is non-zero, then transaction is + // in killed list. + FLMUINT m_uiInactiveTime; // If non-zero, this is the last time + // the checkpoint thread marked this + // transaction as inactive. If zero, + // it means that the transaction is + // active, or it has not been marked + // by the checkpoint thread as + // inactive. If it stays non-zero for + // five or more minutes, it will be + // killed. + FLMUINT m_uiKilledTime; // Time transaction was killed, if + // non-zero. + // Misc. DB Info. + + FLMBOOL m_bItemStateUpdOk;// This variable is used to ensure + // that FlmDbSweep / recovery are the + // only ways that: + // 1) an element or attribute's state + // can be changed to 'unused' + // 2) a 'purge' element or attribute + // can be deleted + + F_Pool m_TempPool; // Temporary memory pool. It + // is only used for the duration of + // a FLAIM operation and then reset. + // The first block in the pool is + // retained between operations to + // help performance. + + // Callback functions. + IF_DeleteStatus * m_pDeleteStatus; // Handles status info coming back + // from deleting a BTree + IF_IxClient * m_pIxClient; // Indexing callback + IF_IxStatus * m_pIxStatus; // Indexing status callback + IF_CommitClient * m_pCommitClient; // Commit callback + + XFLM_STATS * m_pStats; + XFLM_DB_STATS * m_pDbStats; // DB statistics pointer. + XFLM_LFILE_STATS * m_pLFileStats; // LFILE statistics pointer. + FLMUINT m_uiLFileAllocSeq;// Allocation sequence number for + // LFILE statistics array so we + // can tell if the array has been + // reallocated and we need to reset + // our pLFileStats pointer. + XFLM_STATS m_Stats; // Statistics kept here until end + // of transaction. + FLMBOOL m_bStatsInitialized;// Has statistics structure been + // initialized? + F_BKGND_IX * m_pIxStartList; // Indexing threads to start at + // the conclusion of the transaction. + F_BKGND_IX * m_pIxStopList; // Indexing threads to stop at + // the conclusion of the transaction. + F_Btree * m_pCachedBTree; // BTree object used for node operations + F_KeyCollector * m_pKeyColl; // Special purpose object used when checking + // indexes in the F_DbCheck class. + F_OldNodeList * m_pOldNodeList; // List of old truncated nodes to use + // updating indexes. + FLMUINT m_uiDirtyNodeCount; + F_SEM m_hWaitSem; // Semaphore that is used when + // waiting for reads to complete + +friend class F_Database; +friend class F_Dict; +friend class F_DbSystem; +friend class F_Rfl; +friend class F_Btree; +friend class F_Backup; +friend class F_DOMNode; +friend class F_BTreeIStream; +friend class F_DataVector; +friend class F_NodeList; +friend class F_XMLImport; +friend class F_DbRebuild; +friend class F_DbCheck; +friend class F_Query; +friend class FSIndexCursor; +friend class FSCollectionCursor; +friend class F_BtRSFactory; +friend class F_BtResultSet; +friend class F_CachedBlock; +friend class F_CachedNode; +friend class F_BlockCacheMgr; +friend class F_NodeCacheMgr; +friend class F_GlobalCacheMgr; +friend class F_QueryResultSet; +friend class ServerLockObject; +friend class F_BTreeInfo; +friend class F_AttrItem; +}; + +/**************************************************************************** +Stuff for F_Query class +****************************************************************************/ + +#define FLM_FALSE 1 +#define FLM_TRUE 2 +#define FLM_UNK 4 + +FINLINE FLMBOOL isLegalOperator( + eQueryOperators eOperator) +{ + return( (eOperator >= XFLM_AND_OP && eOperator <= XFLM_RBRACKET_OP) + ? TRUE + : FALSE); +} + +FINLINE FLMBOOL isLogicalOp( + eQueryOperators eOperator) +{ + return( (eOperator >= XFLM_AND_OP && eOperator <= XFLM_NOT_OP) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isCompareOp( + eQueryOperators eOperator) +{ + return( (eOperator >= XFLM_EQ_OP && eOperator <= XFLM_GE_OP) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isArithOp( + eQueryOperators eOperator) +{ + return( (eOperator >= XFLM_FIRST_ARITH_OP && + eOperator <= XFLM_LAST_ARITH_OP) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isUnsigned( + eValTypes eValType) +{ + return( eValType == XFLM_UINT_VAL || eValType == XFLM_UINT64_VAL + ? TRUE + : FALSE); +} + +FINLINE FLMBOOL isSigned( + eValTypes eValType) +{ + return( eValType == XFLM_INT_VAL || eValType == XFLM_INT64_VAL + ? TRUE + : FALSE); +} + +FINLINE FLMBOOL is64BitVal( + eValTypes eValType) +{ + return( eValType == XFLM_UINT64_VAL || eValType == XFLM_INT64_VAL + ? TRUE + : FALSE); +} + +FINLINE FLMBOOL isNativeNum( + eValTypes eValType) +{ + return( eValType == XFLM_UINT_VAL || eValType == XFLM_INT_VAL + ? TRUE + : FALSE); +} + +FINLINE FLMBOOL isUniversal( + FQNODE * pQNode) +{ + return( pQNode->bNotted); +} + +FINLINE FLMBOOL isExistential( + FQNODE * pQNode) +{ + return( !pQNode->bNotted); +} + +FINLINE FLMBOOL isBoolNode( + FQNODE * pQNode + ) +{ + return( (pQNode->eNodeType == FLM_VALUE_NODE && + pQNode->currVal.eValType == XFLM_BOOL_VAL) ? TRUE : FALSE); +} + +FINLINE FLMBOOL isSigned( + FQVALUE * pValue) +{ + if( pValue->eValType == XFLM_INT_VAL || pValue->eValType == XFLM_INT64_VAL) + { + return( TRUE); + } + + return( FALSE); +} + +FINLINE FLMBOOL isUnsigned( + FQVALUE * pValue) +{ + if( pValue->eValType == XFLM_UINT_VAL || pValue->eValType == XFLM_UINT64_VAL) + { + return( TRUE); + } + + return( FALSE); +} + +typedef struct ExprState * EXPR_STATE_p; + +typedef struct ExprState +{ + FQNODE * pExpr; + FQNODE * pCurOperatorNode; + FQNODE * pLastNode; + FLMUINT uiNestLevel; + FLMBOOL bExpectingOperator; + FLMBOOL bExpectingLParen; + FQFUNCTION * pQFunction; + XPATH_COMPONENT * pXPathComponent; + FLMUINT uiNumExprNeeded; + FLMUINT uiNumExpressions; + EXPR_STATE_p pPrev; + EXPR_STATE_p pNext; +} EXPR_STATE; + +/***************************************************************************** +Desc: Object for using a buffer on the stack until we outgrow it. +*****************************************************************************/ +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 truncateData( + FLMUINT uiSize) + { + if( uiSize < m_uiOffset) + { + m_uiOffset = uiSize; + } + } + + FINLINE RCODE allocSpace( + FLMUINT uiSize, + void ** ppvPtr) + { + RCODE rc = NE_XFLM_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 appendData( + const void * pvData, + FLMUINT uiSize) + { + RCODE rc = NE_XFLM_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 appendByte( + FLMBYTE ucChar) + { + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucTmp; + + if( RC_BAD( rc = allocSpace( 1, (void **)&pucTmp))) + { + goto Exit; + } + + *pucTmp = ucChar; + + Exit: + + return( rc); + } + + FINLINE RCODE appendUniChar( + FLMUNICODE uChar) + { + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puTmp; + + if( RC_BAD( rc = allocSpace( sizeof( FLMUNICODE), (void **)&puTmp))) + { + goto Exit; + } + + *puTmp = uChar; + + Exit: + + return( rc); + } + + FINLINE FLMBYTE * getBufferPtr( void) + { + return( m_pucBuffer); + } + + FINLINE FLMUNICODE * getUnicodePtr( void) + { + if( m_uiOffset >= sizeof( FLMUNICODE)) + { + return( (FLMUNICODE *)m_pucBuffer); + } + + return( NULL); + } + + FINLINE FLMUINT getUnicodeLength( void) + { + if( m_uiOffset <= sizeof( FLMUNICODE)) + { + return( 0); + } + + return( (m_uiOffset >> 1) - 1); + } + + FINLINE FLMUINT getDataLength( void) + { + return( m_uiOffset); + } + + FINLINE RCODE copyFromBuffer( + F_DynaBuf * pSource) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = resizeBuffer( pSource->m_uiBufferSize))) + { + goto Exit; + } + + if( (m_uiOffset = pSource->m_uiOffset) != 0) + { + f_memcpy( m_pucBuffer, pSource->m_pucBuffer, pSource->m_uiOffset); + } + + Exit: + + return( rc); + } + +private: + + RCODE resizeBuffer( + FLMUINT uiNewSize) + { + RCODE rc = NE_XFLM_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; +}; + +/***************************************************************************** +Desc: Object for gathering node information. +*****************************************************************************/ +class F_NodeInfo : public IF_NodeInfo, public XF_Base +{ +public: + + F_NodeInfo() + { + clearNodeInfo(); + } + + virtual ~F_NodeInfo() + { + } + + FINLINE void XFLMAPI clearNodeInfo( void) + { + f_memset( &m_nodeInfo, 0, sizeof( m_nodeInfo)); + m_ui64TotalNodes = 0; + } + + RCODE XFLMAPI addNodeInfo( + IF_Db * pDb, + IF_DOMNode * pNode, + FLMBOOL bDoSubTree, + FLMBOOL bDoSelf = TRUE); + + FINLINE FLMUINT64 XFLMAPI getTotalNodeCount( void) + { + return( m_ui64TotalNodes); + } + + FINLINE void XFLMAPI getNodeInfo( + XFLM_NODE_INFO * pNodeInfo) + { + f_memcpy( pNodeInfo, &m_nodeInfo, sizeof( m_nodeInfo)); + } + +private: + + XFLM_NODE_INFO m_nodeInfo; + FLMUINT64 m_ui64TotalNodes; +}; + +typedef struct BTREE_INFO +{ + FLMUINT uiLfNum; + char * pszLfName; + FLMUINT uiNumLevels; + XFLM_BTREE_LEVEL_INFO levelInfo [MAX_LEVELS]; +} BTREE_INFO; + +/***************************************************************************** +Desc: Object for gathering B-Tree information. +*****************************************************************************/ +class F_BTreeInfo : public IF_BTreeInfo, public XF_Base +{ +public: + F_BTreeInfo() + { + m_pIndexArray = NULL; + m_uiIndexArraySize = 0; + m_uiNumIndexes = 0; + m_pCollectionArray = NULL; + m_uiCollectionArraySize = 0; + m_uiNumCollections = 0; + m_pool.poolInit( 512); + } + + virtual ~F_BTreeInfo() + { + if (m_pIndexArray) + { + f_free( &m_pIndexArray); + } + if (m_pCollectionArray) + { + f_free( &m_pCollectionArray); + } + m_pool.poolFree(); + } + + FINLINE void XFLMAPI clearBTreeInfo( void) + { + m_uiNumIndexes = 0; + m_uiNumCollections = 0; + } + + RCODE XFLMAPI collectIndexInfo( + IF_Db * pDb, + FLMUINT uiIndexNum, + IF_BTreeInfoStatus * pInfoStatus); + + RCODE XFLMAPI collectCollectionInfo( + IF_Db * pDb, + FLMUINT uiCollectionNum, + IF_BTreeInfoStatus * pInfoStatus); + + FINLINE FLMUINT XFLMAPI getNumIndexes( void) + { + return( m_uiNumIndexes); + } + + FINLINE FLMUINT XFLMAPI getNumCollections( void) + { + return( m_uiNumCollections); + } + + FINLINE FLMBOOL XFLMAPI getIndexInfo( + FLMUINT uiNthIndex, + FLMUINT * puiIndexNum, + char ** ppszIndexName, + FLMUINT * puiNumLevels) + { + if (uiNthIndex < m_uiNumIndexes) + { + *puiIndexNum = m_pIndexArray [uiNthIndex].uiLfNum; + *puiNumLevels = m_pIndexArray [uiNthIndex].uiNumLevels; + *ppszIndexName = m_pIndexArray [uiNthIndex].pszLfName; + return( TRUE); + } + else + { + *puiIndexNum = 0; + *ppszIndexName = NULL; + *puiNumLevels = 0; + return( FALSE); + } + } + + FINLINE FLMBOOL XFLMAPI getCollectionInfo( + FLMUINT uiNthCollection, + FLMUINT * puiCollectionNum, + char ** ppszCollectionName, + FLMUINT * puiNumLevels) + { + if (uiNthCollection < m_uiNumCollections) + { + *puiCollectionNum = m_pCollectionArray [uiNthCollection].uiLfNum; + *puiNumLevels = m_pCollectionArray [uiNthCollection].uiNumLevels; + *ppszCollectionName = m_pCollectionArray [uiNthCollection].pszLfName; + return( TRUE); + } + else + { + *puiCollectionNum = 0; + *puiNumLevels = 0; + *ppszCollectionName = NULL; + return( FALSE); + } + } + + FINLINE FLMBOOL XFLMAPI getIndexLevelInfo( + FLMUINT uiNthIndex, + FLMUINT uiBTreeLevel, + XFLM_BTREE_LEVEL_INFO * pLevelInfo) + { + if (uiNthIndex < m_uiNumIndexes && + uiBTreeLevel < m_pIndexArray [uiNthIndex].uiNumLevels) + { + f_memcpy( pLevelInfo, + &(m_pIndexArray [uiNthIndex].levelInfo [uiBTreeLevel]), + sizeof( XFLM_BTREE_LEVEL_INFO)); + return( TRUE); + } + else + { + return( FALSE); + } + } + + FINLINE FLMBOOL XFLMAPI getCollectionLevelInfo( + FLMUINT uiNthCollection, + FLMUINT uiBTreeLevel, + XFLM_BTREE_LEVEL_INFO * pLevelInfo) + { + if (uiNthCollection < m_uiNumCollections && + uiBTreeLevel < m_pCollectionArray [uiNthCollection].uiNumLevels) + { + f_memcpy( pLevelInfo, + &(m_pCollectionArray [uiNthCollection].levelInfo [uiBTreeLevel]), + sizeof( XFLM_BTREE_LEVEL_INFO)); + return( TRUE); + } + else + { + return( FALSE); + } + } + +private: + + RCODE collectBlockInfo( + F_Db * pDb, + LFILE * pLFile, + BTREE_INFO * pBTreeInfo, + F_BTREE_BLK_HDR * pBlkHdr, + IXD * pIxd); + + RCODE collectBTreeInfo( + F_Db * pDb, + LFILE * pLFile, + BTREE_INFO * pBTreeInfo, + IXD * pIxd); + + FINLINE RCODE doCallback( void) + { + if (m_pInfoStatus) + { + return( m_pInfoStatus->infoStatus( m_uiCurrLfNum, m_bIsCollection, + m_pszCurrLfName, m_uiCurrLevel, + m_ui64CurrLfBlockCount, m_ui64CurrLevelBlockCount, + m_ui64TotalBlockCount)); + } + else + { + return( NE_XFLM_OK); + } + } + + BTREE_INFO * m_pIndexArray; + FLMUINT m_uiIndexArraySize; + FLMUINT m_uiNumIndexes; + BTREE_INFO * m_pCollectionArray; + FLMUINT m_uiCollectionArraySize; + FLMUINT m_uiNumCollections; + F_Pool m_pool; + + // Items for the callback function. + + IF_BTreeInfoStatus * m_pInfoStatus; + FLMUINT m_uiBlockSize; + FLMUINT m_uiCurrLfNum; + FLMBOOL m_bIsCollection; + char * m_pszCurrLfName; + FLMUINT m_uiCurrLevel; + FLMUINT64 m_ui64CurrLfBlockCount; + FLMUINT64 m_ui64CurrLevelBlockCount; + FLMUINT64 m_ui64TotalBlockCount; +}; + +RCODE ixKeyCompare( + F_Db * pDb, + IXD * pIxd, + F_DataVector * pSearchKey, + F_OldNodeList * pOldNodeList1, + F_OldNodeList * pOldNodeList2, + FLMBOOL bCompareDocId, + FLMBOOL bCompareNodeIds, + const void * pvKey1, + FLMUINT uiKeyLen1, + const void * pvKey2, + FLMUINT uiKeyLen2, + FLMINT * piCompare); + +/******************************************************************** +Desc: Class for comparing two keys in an index. +********************************************************************/ +class IXKeyCompare : public IF_ResultSetCompare, public XF_Base +{ +public: + + IXKeyCompare() + { + + // m_pDb is used to sort truncated keys if necessary. + // m_pIxd is used for comparison + + m_pDb = NULL; + m_pIxd = NULL; + m_pSearchKey = NULL; + m_pOldNodeList = NULL; + m_bCompareDocId = TRUE; + m_bCompareNodeIds = TRUE; + } + + virtual ~IXKeyCompare() + { + if (m_pOldNodeList) + { + m_pOldNodeList->Release(); + } + } + + FINLINE RCODE XFLMAPI compare( + const void * pvKey1, + FLMUINT uiKeyLen1, + const void * pvKey2, + FLMUINT uiKeyLen2, + FLMINT * piCompare) + { + return( ixKeyCompare( m_pDb, m_pIxd, m_pSearchKey, m_pOldNodeList, + m_pOldNodeList, + m_bCompareDocId, m_bCompareNodeIds, + pvKey1, uiKeyLen1, pvKey2, uiKeyLen2, piCompare)); + } + + FINLINE void setOldNodeList( + F_OldNodeList * pOldNodeList) + { + flmAssert( !m_pOldNodeList); + if ((m_pOldNodeList = pOldNodeList) != NULL) + { + m_pOldNodeList->AddRef(); + } + } + + FINLINE void setIxInfo( + F_Db * pDb, + IXD * pIxd) + { + m_pDb = pDb; + m_pIxd = pIxd; + } + + FINLINE void setSearchKey( + F_DataVector * pSearchKey) + { + m_pSearchKey = pSearchKey; + } + + FINLINE void setCompareNodeIds( + FLMBOOL bCompareNodeIds) + { + m_bCompareNodeIds = bCompareNodeIds; + } + + FINLINE void setCompareDocId( + FLMBOOL bCompareDocId) + { + m_bCompareDocId = bCompareDocId; + } + + FINLINE FLMUINT getRefCount( void) + { + return( IF_ResultSetCompare::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_ResultSetCompare::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_ResultSetCompare::Release()); + } + +private: + + F_Db * m_pDb; + IXD * m_pIxd; + F_DataVector * m_pSearchKey; + F_OldNodeList * m_pOldNodeList; + FLMBOOL m_bCompareDocId; + FLMBOOL m_bCompareNodeIds; +}; + +/*============================================================================= +Desc: Result set class for queries that do sorting. +=============================================================================*/ +class F_QueryResultSet : public XF_RefCount, public XF_Base +{ +public: + + F_QueryResultSet() + { + m_pBTree = NULL; + m_pResultSetDb = NULL; + m_pSrcDb = NULL; + m_pIxd = NULL; + m_uiCurrPos = FLM_MAX_UINT; + m_uiCount = 0; + m_bPositioned = FALSE; + m_hMutex = F_MUTEX_NULL; + } + + ~F_QueryResultSet(); + + // Initialize the result set + + RCODE initResultSet( + FLMBOOL bUseIxCompareObj, + FLMBOOL bEnableEncryption); + + FINLINE void setIxInfo( + F_Db * pSrcDb, + IXD * pIxd) + { + m_pSrcDb = pSrcDb; + m_pIxd = pIxd; + m_compareObj.setIxInfo( pSrcDb, pIxd); + } + + // Entry Add and Sort Methods + + RCODE addEntry( // Variable or fixed length entry coming in + FLMBYTE * pucKey, // key for sorting. + FLMUINT uiKeyLength, + FLMBOOL bLockMutex); + + // Methods to read entries. + + RCODE getFirst( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + RCODE getLast( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + RCODE getNext( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + RCODE getPrev( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + RCODE getCurrent( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + RCODE positionToEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + F_DataVector * pSearchKey, + FLMUINT uiFlags, + FLMBOOL bLockMutex); + + RCODE positionToEntry( + FLMUINT uiPosition, + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex); + + FINLINE FLMUINT getCount( void) + { + return( m_uiCount); + } + + FINLINE FLMUINT getCurrPos( void) + { + return( m_uiCurrPos); + } + + FINLINE void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + FINLINE void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + +private: + + char m_szResultSetDibName [F_PATH_MAX_SIZE]; + F_Db * m_pResultSetDb; + F_Btree * m_pBTree; + LFILE m_LFile; + F_Db * m_pSrcDb; + IXD * m_pIxd; + IXKeyCompare m_compareObj; + FLMUINT m_uiCurrPos; + FLMUINT m_uiCount; + FLMBOOL m_bPositioned; + F_MUTEX m_hMutex; +}; + +typedef struct RS_WAITER +{ + FLMUINT uiThreadId; // Thread of waiter + F_SEM hESem; // Semaphore to signal to wake up thread. + RCODE * pRc; // Pointer to return code that is to + // be set. + FLMUINT uiWaitStartTime; + // Time we started waiting. + FLMUINT uiTimeLimit; // Maximum time (milliseconds) to wait + // before timing out. + FLMUINT uiNumToWaitFor;// Wait until we get at least this many + // in the result set - or until the + // result set is complete. + RS_WAITER * pNext; // Next lock waiter in list. + RS_WAITER * pPrev; // Previous lock waiter in list. +} RS_WAITER; + +/**************************************************************************** +Desc: Class for setting up query criteria +****************************************************************************/ +class F_Query : public IF_Query, public XF_Base +{ +public: + + // Constructor and Destructor + + F_Query(); + virtual ~F_Query(); + + // Methods for constructing a query + + FINLINE RCODE XFLMAPI setLanguage( + FLMUINT uiLanguage) + { + + // Cannot change language after optimization + + if (m_bOptimized) + { + return( RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED)); + } + m_uiLanguage = uiLanguage; + return( NE_XFLM_OK); + } + + FINLINE RCODE XFLMAPI setCollection( + FLMUINT uiCollection + ) + { + + // Cannot change collection after optimization + + if (m_bOptimized) + { + return( RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED)); + } + m_uiCollection = uiCollection; + return( NE_XFLM_OK); + } + + FINLINE RCODE XFLMAPI setupQueryExpr( + IF_Db * pDb, + const FLMUNICODE * puzQuery) + { + return( setupQueryExpr( TRUE, pDb, (void *)puzQuery)); + } + + FINLINE RCODE XFLMAPI setupQueryExpr( + IF_Db * pDb, + const char * pszQuery) + { + return( setupQueryExpr( FALSE, pDb, (void *)pszQuery)); + } + + RCODE XFLMAPI copyCriteria( + IF_Query * pSrcQuery); + + RCODE XFLMAPI addXPathComponent( + eXPathAxisTypes eXPathAxis, + eDomNodeType eNodeType, + FLMUINT uiNameId, + IF_QueryNodeSource * pNodeSource); + + RCODE XFLMAPI addOperator( + eQueryOperators eOperator, + FLMUINT uiCompareRules = 0, + IF_OperandComparer * pOpComparer = NULL); + + RCODE XFLMAPI addUnicodeValue( + const FLMUNICODE * puzVal); + + RCODE XFLMAPI addUTF8Value( + const char * pszVal, + FLMUINT uiUTF8Len = 0); + + RCODE XFLMAPI addBinaryValue( + const void * pvVal, + FLMUINT uiValLen); + + RCODE XFLMAPI addUINTValue( + FLMUINT uiVal); + + RCODE XFLMAPI addINTValue( + FLMINT iVal); + + RCODE XFLMAPI addUINT64Value( + FLMUINT64 ui64Val); + + RCODE XFLMAPI addINT64Value( + FLMINT64 i64Val); + + RCODE XFLMAPI addBoolean( + FLMBOOL bVal, + FLMBOOL bUnknown = FALSE); + + FINLINE RCODE XFLMAPI addFunction( + eQueryFunctions eFunction) + { + return( addFunction( eFunction, NULL, FALSE)); + } + + FINLINE RCODE XFLMAPI addFunction( + IF_QueryValFunc * pFuncObj, + FLMBOOL bHasXPathExpr) + { + // Pass XFLM_FUNC_xxx to private addFunction method - it really + // doesn't matter, because it will be ignored. + + flmAssert( pFuncObj); + return( addFunction( XFLM_FUNC_xxx, pFuncObj, bHasXPathExpr)); + } + + RCODE XFLMAPI getFirst( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0); + + RCODE XFLMAPI getLast( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0); + + RCODE XFLMAPI getNext( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0, + FLMUINT uiNumToSkip = 0, + FLMUINT * puiNumSkipped = NULL); + + RCODE XFLMAPI getPrev( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0, + FLMUINT uiNumToSkip = 0, + FLMUINT * puiNumSkipped = NULL); + + RCODE XFLMAPI getCurrent( + IF_Db * pDb, + IF_DOMNode ** ppNode); + + void XFLMAPI resetQuery( void); + + RCODE XFLMAPI getStatsAndOptInfo( + FLMUINT * puiNumOptInfos, + XFLM_OPT_INFO ** ppOptInfo); + + void XFLMAPI freeStatsAndOptInfo( + XFLM_OPT_INFO ** ppOptInfo); + + void XFLMAPI setDupHandling( + FLMBOOL bRemoveDups); + + RCODE XFLMAPI setIndex( + FLMUINT uiIndex); + + RCODE XFLMAPI getIndex( + IF_Db * pDb, + FLMUINT * puiIndex, + FLMBOOL * pbHaveMultiple); + + RCODE XFLMAPI addSortKey( + void * pvSortKeyContext, + FLMBOOL bChildToContext, + FLMBOOL bElement, + FLMUINT uiNameId, + FLMUINT uiCompareRules, + FLMUINT uiLimit, + FLMUINT uiKeyComponent, + FLMBOOL bSortDescending, + FLMBOOL bSortMissingHigh, + void ** ppvContext); + + FINLINE RCODE XFLMAPI enablePositioning( void) + { + if (m_bOptimized) + { + return( RC_SET( NE_XFLM_ILLEGAL_OP)); + } + else + { + m_bPositioningEnabled = TRUE; + } + return( NE_XFLM_OK); + } + + RCODE XFLMAPI positionTo( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiPosition); + + RCODE XFLMAPI positionTo( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + IF_DataVector * pSearchKey, + FLMUINT uiFlags); + + RCODE XFLMAPI getPosition( + IF_Db * pDb, + FLMUINT * puiPosition); + + RCODE XFLMAPI buildResultSet( + IF_Db * pDb, + FLMUINT uiTimeLimit); + + void XFLMAPI stopBuildingResultSet( void); + + RCODE XFLMAPI getCounts( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMBOOL bPartialCountOk, + FLMUINT * puiReadCount, + FLMUINT * puiPassedCount, + FLMUINT * puiPositionableToCount, + FLMBOOL * pbDoneBuildingResultSet = NULL); + + FINLINE void XFLMAPI enableResultSetEncryption( void) + { + m_bEncryptResultSet = TRUE; + } + + FINLINE void XFLMAPI setQueryStatusObject( + IF_QueryStatus * pQueryStatus) + { + if (m_pQueryStatus) + { + m_pQueryStatus->Release(); + } + if ((m_pQueryStatus = pQueryStatus) != NULL) + { + m_pQueryStatus->AddRef(); + } + } + + FINLINE void XFLMAPI setQueryValidatorObject( + IF_QueryValidator * pQueryValidator) + { + if (m_pQueryValidator) + { + m_pQueryValidator->Release(); + } + if ((m_pQueryValidator = pQueryValidator) != NULL) + { + m_pQueryValidator->AddRef(); + } + } + +private: + + RCODE XFLMAPI addFunction( + eQueryFunctions eFunction, + IF_QueryValFunc * pFuncObj, + FLMBOOL bHasXPathExpr); + + FINLINE FLMBOOL timedOut( void) + { + if (m_uiTimeLimit) + { + FLMUINT uiCurrTime; + + uiCurrTime = FLM_GET_TIMER(); + if (FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime) > m_uiTimeLimit) + { + return( TRUE); + } + } + return( FALSE); + } + + FINLINE RCODE queryStatus( void) + { + if (timedOut()) + { + return( RC_SET( NE_XFLM_TIMEOUT)); + } + if (m_uiBuildThreadId && m_bStopBuildingResultSet) + { + return( RC_SET( NE_XFLM_USER_ABORT)); + } + return( (RCODE)(m_pQueryStatus + ? m_pQueryStatus->queryStatus( m_pCurrOpt) + : (RCODE)NE_XFLM_OK)); + } + + FINLINE RCODE newSource( void) + { + if (timedOut()) + { + return( RC_SET( NE_XFLM_TIMEOUT)); + } + if (m_uiBuildThreadId && m_bStopBuildingResultSet) + { + return( RC_SET( NE_XFLM_USER_ABORT)); + } + return( (RCODE)(m_pQueryStatus + ? m_pQueryStatus->newSource( m_pCurrOpt) + : (RCODE)NE_XFLM_OK)); + } + + FINLINE RCODE incrNodesRead( void) + { + m_pCurrOpt->ui64NodesRead++; + return( queryStatus()); + } + + FINLINE FLMBOOL expectingOperand( void) + { + return( !m_pCurExprState->bExpectingOperator); + } + + FINLINE FLMBOOL expectingOperator( void) + { + return( m_pCurExprState->bExpectingOperator); + } + + FINLINE FLMBOOL parsingFunction( void) + { + return( (FLMBOOL)(m_pCurExprState->pPrev && + m_pCurExprState->pQFunction + ? TRUE + : FALSE)); + } + + FINLINE FLMBOOL parsingXPathExpr( void) + { + return( (FLMBOOL)(m_pCurExprState->pPrev && + m_pCurExprState->pXPathComponent && + !m_pCurExprState->pQFunction + ? TRUE + : FALSE)); + } + + RCODE allocExprState( void); + + RCODE allocValueNode( + FLMUINT uiValLen, + eValTypes eValType, + FQNODE ** ppQNode); + + RCODE intersectPredicates( + CONTEXT_PATH * pContextPath, + FQNODE * pXPathNode, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue, + FLMBOOL * pbClipContext); + + RCODE unionPredicates( + CONTEXT_PATH * pContextPath, + FQNODE * pXPathNode, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue); + + RCODE addPredicateToContext( + OP_CONTEXT * pContext, + XPATH_COMPONENT * pXPathComponent, + XPATH_COMPONENT * pXPathComp, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue, + FLMBOOL * pbClipContext, + FQNODE ** ppQNode); + + RCODE createOpContext( + OP_CONTEXT * pParentContext, + FLMBOOL bIntersect, + FQNODE * pQRootNode); + + RCODE getPathPredicates( + FQNODE * pParentNode, + FQNODE ** ppQNode, + XPATH_COMPONENT * pXPathContext); + + RCODE getPredicates( + FQNODE ** ppExpr, + FQNODE * pStartNode, + XPATH_COMPONENT * pXPathComponent); + + RCODE optimizePredicate( + XPATH_COMPONENT * pXPathComponent, + PATH_PRED * pPred); + + RCODE optimizePath( + CONTEXT_PATH * pContextPath, + PATH_PRED * pSingleNodeIdPred, + FLMBOOL bIntersect); + + RCODE optimizeContext( + OP_CONTEXT * pContext, + CONTEXT_PATH * pSingleNodeIdPath, + PATH_PRED * pSingleNodeIdPred); + + RCODE setupIndexScan( void); + + RCODE checkSortIndex( + FLMUINT uiOptIndex); + + RCODE optimize( void); + + RCODE setupCurrPredicate( + FLMBOOL bForward); + + RCODE testPassed( + IF_DOMNode ** ppNode, + FLMBOOL * pbPassed, + FLMBOOL * pbEliminatedDup); + + RCODE nextFromIndex( + FLMBOOL bEvalCurrDoc, + FLMUINT uiMaxToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode); + + RCODE prevFromIndex( + FLMBOOL bEvalCurrDoc, + FLMUINT uiMaxToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode); + + RCODE getDocFromIndexScan( + FLMBOOL bFirstLast, + FLMBOOL bForward); + + RCODE nextFromScan( + FLMBOOL bFirstDoc, + FLMUINT uiMaxToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode); + + RCODE prevFromScan( + FLMBOOL bLastDoc, + FLMUINT uiMaxToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode); + + void useLeafContext( + FLMBOOL bGetFirst); + + FLMBOOL useNextPredicate( void); + + FLMBOOL usePrevPredicate( void); + + RCODE getNodeSourceNode( + FLMBOOL bForward, + IF_QueryNodeSource * pNodeSource, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getRootAxisNode( + IF_DOMNode ** ppCurrNode); + + RCODE walkDocument( + FLMBOOL bForward, + FLMBOOL bWalkAttributes, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppCurrNode); + + RCODE getChildAxisNode( + FLMBOOL bForward, + IF_DOMNode * pContextNode, + FLMUINT uiChildNameId, + IF_DOMNode ** ppCurrNode); + + RCODE getParentAxisNode( + FLMBOOL bForward, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getAncestorAxisNode( + FLMBOOL bForward, + FLMBOOL bIncludeSelf, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getDescendantAxisNode( + FLMBOOL bForward, + FLMBOOL bIncludeSelf, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getSibAxisNode( + FLMBOOL bForward, + FLMBOOL bPrevSibAxis, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getPrevOrAfterAxisNode( + FLMBOOL bForward, + FLMBOOL bPrevAxis, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE getAttrAxisNode( + FLMBOOL bForward, + FLMBOOL bAttrAxis, + FLMUINT uiAttrNameId, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode); + + RCODE verifyOccurrence( + FLMBOOL bUseKeyNodes, + XPATH_COMPONENT * pXPathComponent, + IF_DOMNode * pCurrNode, + FLMBOOL * pbPassed); + + RCODE getXPathComponentFromAxis( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + XPATH_COMPONENT * pXPathComponent, + IF_DOMNode ** ppCurrNode, + eXPathAxisTypes eAxis, + FLMBOOL bAxisInverted, + FLMBOOL bCountNodes); + + RCODE getNextXPathValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + FLMBOOL bXPathIsEntireExpr, + FQNODE * pQNode); + + RCODE getNextFunctionValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE * pCurrNode, + F_DynaBuf * pDynaBuf); + + RCODE getFuncValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE ** ppCurrNode, + FLMBOOL * pbGetNodeValue, + F_DynaBuf * pDynaBuf); + + RCODE getXPathValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE ** ppCurrNode, + FLMBOOL * pbGetNodeValue, + FLMBOOL bUseKeyNodes, + FLMBOOL bXPathIsEntireExpr); + + RCODE setExprReturnValue( + FLMBOOL bUseKeyNodes, + FQNODE * pQueryExpr, + FLMBOOL * pbPassed, + IF_DOMNode ** ppNode); + + RCODE evalExpr( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + FQNODE * pQueryExpr, + FLMBOOL * pbPassed, + IF_DOMNode ** ppNode); + + RCODE getAppNode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComp); + + RCODE testKey( + F_DataVector * pKey, + PATH_PRED * pPred, + FLMBOOL * pbPasses, + IF_DOMNode ** ppPassedNode); + + RCODE getKey( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent); + + RCODE testMetaData( + IF_DOMNode * pNode, + FLMUINT uiMetaDataType, + PATH_PRED * pPred, + FLMBOOL * pbPasses); + + RCODE getANode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent); + + RCODE getContextNode( + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent); + + RCODE getNextIndexNode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + FQNODE * pExprXPathSource, + FLMBOOL bSkipCurrKey); + + RCODE objectAddRef( + XF_RefCount * pObject); + + RCODE setupQueryExpr( + FLMBOOL bUnicode, + IF_Db * pDb, + const void * pvQuery); + + RCODE allocDupCheckSet( void); + + RCODE checkIfDup( + IF_DOMNode ** ppNode, + FLMBOOL * pbPassed); + + RCODE copyValue( + FQVALUE * pDestValue, + FQVALUE * pSrcValue); + + RCODE copyXPath( + XPATH_COMPONENT * pXPathContext, + FQNODE * pDestNode, + FXPATH ** ppDestXPath, + FXPATH * pSrcXPath); + + RCODE copyFunction( + XPATH_COMPONENT * pXPathContext, + FQFUNCTION ** ppDestFunc, + FQFUNCTION * pSrcFunc); + + RCODE copyNode( + XPATH_COMPONENT * pXPathContext, + FQNODE ** ppDestNode, + FQNODE * pSrcNode); + + RCODE copyExpr( + XPATH_COMPONENT * pXPathContext, + FQNODE ** ppDestExpr, + FQNODE * pSrcExpr); + + void clearQuery( void); + + void initVars( void); + + FINLINE RCODE validateNode( + IF_DOMNode * pNode, + FLMBOOL * pbPassed) + { + RCODE rc = NE_XFLM_OK; + + if (*pbPassed && m_pQueryValidator) + { + if (RC_BAD( rc = m_pQueryValidator->validateNode( + (IF_Db *)m_pDb, pNode, pbPassed))) + { + goto Exit; + } + if (!(*pbPassed)) + { + if (!m_pQuery || m_pQuery->eNodeType != FLM_XPATH_NODE || m_bRemoveDups) + { + m_pCurrOpt->ui64DocsFailedValidation++; + } + m_pCurrOpt->ui64NodesFailedValidation++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + } + Exit: + return( rc); + } + + RCODE createResultSet( void); + + RCODE buildResultSet( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMUINT uiNumToWaitFor); + + void checkResultSetWaiters( + RCODE rc); + + RCODE waitResultSetBuild( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMUINT uiNumToWaitFor); + + RCODE getFirstFromResultSet( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit); + + RCODE getLastFromResultSet( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit); + + RCODE getNextFromResultSet( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped); + + RCODE getPrevFromResultSet( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped); + + RCODE getCurrentFromResultSet( + IF_Db * pDb, + IF_DOMNode ** ppNode); + + RCODE verifySortKeys( void); + + RCODE addToResultSet( void); + + RCODE m_rc; + FQNODE * m_pQuery; + FLMBOOL m_bScan; + FLMBOOL m_bScanIndex; + FLMBOOL m_bResetAllXPaths; + FSIndexCursor * m_pFSIndexCursor; + XFLM_OPT_INFO m_scanOptInfo; + XFLM_OPT_INFO * m_pCurrOpt; + FLMBOOL m_bEmpty; + IXD * m_pSortIxd; + F_QueryResultSet * m_pSortResultSet; + RS_WAITER * m_pFirstWaiter; + FLMBOOL m_bStopBuildingResultSet; + FLMUINT m_uiBuildThreadId; + FLMBOOL m_bPositioningEnabled; + FLMBOOL m_bResultSetPopulated; + FLMBOOL m_bEntriesAlreadyInOrder; + FLMBOOL m_bEncryptResultSet; + FLMUINT64 m_ui64RSDocsRead; + FLMUINT64 m_ui64RSDocsPassed; + EXPR_STATE * m_pCurExprState; + F_Pool m_Pool; + FLMBOOL m_bOptimized; + FLMUINT m_uiLanguage; + FLMUINT m_uiCollection; + IF_DOMNode * m_pCurrDoc; + IF_DOMNode * m_pCurrNode; + OP_CONTEXT * m_pCurrContext; + CONTEXT_PATH * m_pCurrContextPath; + PATH_PRED * m_pCurrPred; + FQNODE * m_pExprXPathSource; + eQueryStates m_eState; + IF_QueryStatus * m_pQueryStatus; + IF_QueryValidator * m_pQueryValidator; + F_Database * m_pDatabase; + F_Db * m_pDb; + F_Query * m_pNext; // Next query off of database + F_Query * m_pPrev; // Prev query off of database + XF_RefCount ** m_ppObjectList; + FLMUINT m_uiObjectListSize; + FLMUINT m_uiObjectCount; + FLMBOOL m_bRemoveDups; + FDynSearchSet * m_pDocIdSet; + FLMUINT m_uiIndex; + FLMBOOL m_bIndexSet; + FLMUINT m_uiTimeLimit; + FLMUINT m_uiStartTime; + +friend class F_Db; +friend class F_Database; +friend class F_Dict; +friend class F_IStream; +}; + +/***************************************************************************** +Desc: FLAIM database system object +******************************************************************************/ +class F_DbSystem : public IF_DbSystem, public F_OSBase +{ +public: + + F_DbSystem() + { + m_ui32RefCnt = 1; + LockModule(); + } + + virtual ~F_DbSystem() + { + UnlockModule(); + } + + FLMUINT32 XFLMAPI AddRef( void); + + FLMUINT32 XFLMAPI Release( void); + + RCODE XFLMAPI QueryInterface( + RXFLMIID riid, + void ** ppv); + + RCODE XFLMAPI init( void); + + RCODE XFLMAPI updateIniFile( + const char * pszParamName, + const char * pszValue); + + void XFLMAPI exit(); + + FINLINE void XFLMAPI getFileSystem( + IF_FileSystem ** ppFileSystem) + { + *ppFileSystem = (IF_FileSystem *)gv_pFileSystem; + (*ppFileSystem)->AddRef(); + } + + RCODE XFLMAPI dbCreate( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + XFLM_CREATE_OPTS * pCreateOpts, + FLMBOOL bTempDb, + IF_Db ** ppDb); + + FINLINE RCODE XFLMAPI dbCreate( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszDictFileName, + const char * pszDictBuf, + XFLM_CREATE_OPTS * pCreateOpts, + IF_Db ** ppDb) + { + return( dbCreate( pszDbFileName, pszDataDir, pszRflDir, pszDictFileName, + pszDictBuf, pCreateOpts, FALSE, ppDb)); + } + + FINLINE RCODE XFLMAPI dbOpen( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMBOOL bAllowLimited, + IF_Db ** ppDb) + { + FLMUINT uiOpenFlags = bAllowLimited ? XFLM_ALLOW_LIMITED_MODE : 0; + + return( openDb( pszDbFileName, pszDataDir, pszRflDir, + pszPassword, uiOpenFlags, ppDb)); + } + + RCODE XFLMAPI dbRebuild( + const char * pszSourceDbPath, + const char * pszSourceDataDir, + const char * pszDestDbPath, + const char * pszDestDataDir, + const char * pszDestRflDir, + const char * pszDictPath, + const char * pszPassword, + XFLM_CREATE_OPTS * pCreateOpts, + FLMUINT64 * pui64TotNodes, + FLMUINT64 * pui64NodesRecov, + FLMUINT64 * pui64QuarantinedNodes, + IF_DbRebuildStatus * pRebuildStatus); + + RCODE XFLMAPI dbCheck( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiFlags, + IF_DbInfo ** ppDbInfo, + IF_DbCheckStatus * pDbCheck); + + FINLINE RCODE XFLMAPI dbDup( + IF_Db * ifpDb, + IF_Db ** ppDb) + { + F_Db * pDb = (F_Db *)ifpDb; + + return( openDatabase( pDb->m_pDatabase, NULL, NULL, NULL, NULL, 0, + FALSE, NULL, NULL, NULL, ppDb)); + } + + FINLINE RCODE XFLMAPI setDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, + FLMUINT uiCacheAdjustMin, + FLMUINT uiCacheAdjustMax, + FLMUINT uiCacheAdjustMinToLeave) + { + return( gv_XFlmSysData.pGlobalCacheMgr->setDynamicMemoryLimit( + uiCacheAdjustPercent, uiCacheAdjustMin, + uiCacheAdjustMax, uiCacheAdjustMinToLeave)); + } + + FINLINE RCODE XFLMAPI setHardMemoryLimit( + FLMUINT uiPercent, + FLMBOOL bPercentOfAvail, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bPreallocate) + { + return( gv_XFlmSysData.pGlobalCacheMgr->setHardMemoryLimit( uiPercent, + bPercentOfAvail, uiMin, uiMax, uiMinToLeave, bPreallocate)); + } + + // Determine if dyamic cache adjusting is supported. + + FINLINE FLMBOOL XFLMAPI getDynamicCacheSupported( void) + { +#ifdef FLM_CAN_GET_PHYS_MEM + return( TRUE); +#else + return( FALSE); +#endif + } + + FINLINE void XFLMAPI getCacheInfo( + XFLM_CACHE_INFO * pCacheInfo) + { + gv_XFlmSysData.pGlobalCacheMgr->getCacheInfo( pCacheInfo); + } + + // Enable/disable cache debugging mode + + void XFLMAPI enableCacheDebug( + FLMBOOL bDebug); + + FLMBOOL XFLMAPI cacheDebugEnabled( void); + + // Clear cache + + FINLINE RCODE XFLMAPI clearCache( + IF_Db * pDb) + { + return( gv_XFlmSysData.pGlobalCacheMgr->clearCache( pDb)); + } + + // Close all files that have not been used for the specified number of + // seconds. + + RCODE XFLMAPI closeUnusedFiles( + FLMUINT uiSeconds); + + // Maximum number of file handles available. + + void XFLMAPI setOpenThreshold( + FLMUINT uiThreshold); + + FLMUINT XFLMAPI getOpenThreshold( void); + + // Get the number of open files + + FLMUINT XFLMAPI getOpenFileCount( void); + + // Start gathering statistics. + + void XFLMAPI startStats( void); + + // Stop gathering statistics. + + void XFLMAPI stopStats( void); + + // Reset statistics. + + void XFLMAPI resetStats( void); + + RCODE XFLMAPI getStats( + XFLM_STATS * pFlmStats); + + void XFLMAPI freeStats( + XFLM_STATS * pFlmStats); + + // Set the maximum number of queries to save. + + void XFLMAPI setQuerySaveMax( + FLMUINT uiMaxToSave); + + FLMUINT XFLMAPI getQuerySaveMax( void); + + // Set temporary directory. + + RCODE XFLMAPI setTempDir( + const char * pszPath); + + RCODE XFLMAPI getTempDir( + char * pszPath); + + // Maximum seconds between checkpoints. + + void XFLMAPI setCheckpointInterval( + FLMUINT uiSeconds); + + FLMUINT XFLMAPI getCheckpointInterval( void); + + // Set interval for dynamically adjusting cache limit. + + void XFLMAPI setCacheAdjustInterval( + FLMUINT uiSeconds); + + FLMUINT XFLMAPI getCacheAdjustInterval( void); + + // Set interval for dynamically cleaning out old cache blocks and records. + + void XFLMAPI setCacheCleanupInterval( + FLMUINT uiSeconds); + + FLMUINT XFLMAPI getCacheCleanupInterval( void); + + // Set interval for cleaning up unused structures. + + void XFLMAPI setUnusedCleanupInterval( + FLMUINT uiSeconds); + + FLMUINT XFLMAPI getUnusedCleanupInterval( void); + + // Set maximum time for an item to be unused. + + void XFLMAPI setMaxUnusedTime( + FLMUINT uiSeconds); + + FLMUINT XFLMAPI getMaxUnusedTime( void); + + // Specify the logger object + + void XFLMAPI setLogger( + IF_LoggerClient * pLogger); + + // Enable or disable use of ESM + + void XFLMAPI enableExtendedServerMemory( + FLMBOOL bEnable); + + FLMBOOL XFLMAPI extendedServerMemoryEnabled( void); + + void XFLMAPI deactivateOpenDb( + const char * pszDbFileName, + const char * pszDataDir); + + // Maximum dirty cache. + + void XFLMAPI setDirtyCacheLimits( + FLMUINT uiMaxDirty, + FLMUINT uiLowDirty); + + void XFLMAPI getDirtyCacheLimits( + FLMUINT * puiMaxDirty, + FLMUINT * puiLowDirty); + + RCODE XFLMAPI getThreadInfo( + IF_ThreadInfo ** ppThreadInfo); + + RCODE XFLMAPI registerForEvent( + eEventCategory eCategory, + IF_EventClient * pEventClient); + + void XFLMAPI deregisterForEvent( + eEventCategory eCategory, + IF_EventClient * pEventClient); + + RCODE XFLMAPI getNextMetaphone( + IF_IStream * pIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone = NULL); + + RCODE XFLMAPI dbCopy( + const char * pszSrcDbName, + const char * pszSrcDataDir, + const char * pszSrcRflDir, + const char * pszDestDbName, + const char * pszDestDataDir, + const char * pszDestRflDir, + IF_DbCopyStatus * ifpStatus); + + RCODE XFLMAPI dbRemove( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + FLMBOOL bRemoveRflFiles); + + RCODE XFLMAPI dbRename( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszNewDbName, + FLMBOOL bOverwriteDestOk, + IF_DbRenameStatus * ifpStatus); + + RCODE XFLMAPI dbRestore( + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszBackupPath, + const char * pszPassword, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus); + + RCODE XFLMAPI strCmp( + FLMUINT uiCompFlags, + FLMUINT uiLanguage, + FLMUNICODE * uzStr1, + FLMUNICODE * uzStr2, + FLMINT * piCmp); + + FLMBOOL XFLMAPI errorIsFileCorrupt( + RCODE rc); + + static FLMBOOL _errorIsFileCorrupt( + RCODE rc) + { + F_DbSystem dbSystem; + + return( dbSystem.errorIsFileCorrupt( rc)); + } + + const char * XFLMAPI errorString( + RCODE rc); + + const char * XFLMAPI checkErrorToStr( + FLMINT iCheckErrorCode); + + static const char * _errorString( + RCODE rc) + { + F_DbSystem dbSystem; + + return( dbSystem.errorString( rc)); + } + + RCODE XFLMAPI openBufferIStream( + const char * pucBuffer, + FLMUINT uiLength, + IF_PosIStream ** ppIStream); + + RCODE XFLMAPI openFileIStream( + const char * pszPath, + IF_PosIStream ** ppIStream); + + RCODE XFLMAPI openMultiFileIStream( + const char * pszDirectory, + const char * pszBaseName, + IF_IStream ** ppIStream); + + RCODE XFLMAPI openBufferedIStream( + IF_IStream * pIStream, + FLMUINT uiBufferSize, + IF_IStream ** ppIStream); + + RCODE XFLMAPI openUncompressingIStream( + IF_IStream * pIStream, + IF_IStream ** ppIStream); + + RCODE XFLMAPI openFileOStream( + const char * pszFileName, + FLMBOOL bTruncateIfExists, + IF_OStream ** ppOStream); + + RCODE XFLMAPI openMultiFileOStream( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOverwrite, + IF_OStream ** ppStream); + + RCODE XFLMAPI removeMultiFileStream( + const char * pszDirectory, + const char * pszBaseName); + + RCODE XFLMAPI openBufferedOStream( + IF_OStream * pOStream, + FLMUINT uiBufferSize, + IF_OStream ** ppOStream); + + RCODE XFLMAPI openCompressingOStream( + IF_OStream * pOStream, + IF_OStream ** ppOStream); + + RCODE XFLMAPI writeToOStream( + IF_IStream * pIStream, + IF_OStream * pOStream); + + RCODE XFLMAPI openBase64Encoder( + IF_IStream * pInputStream, + FLMBOOL bInsertLineBreaks, + IF_IStream ** ppEncodedStream); + + RCODE XFLMAPI openBase64Decoder( + IF_IStream * pInputStream, + IF_IStream ** ppDecodedStream); + + FINLINE RCODE XFLMAPI createMemoryPool( + IF_Pool ** ppPool) + { + if( (*ppPool = f_new F_Pool) == NULL) + { + return( RC_SET( NE_XFLM_MEM)); + } + + return( NE_XFLM_OK); + } + + RCODE XFLMAPI createIFDataVector( + IF_DataVector ** ifppDV); + + RCODE XFLMAPI createIFResultSet( + IF_ResultSet ** ppResultSet); + + RCODE XFLMAPI createIFQuery( + IF_Query ** ppQuery); + + FINLINE void XFLMAPI freeMem( + void ** ppMem) + { + f_free( ppMem); + } + + FINLINE RCODE internalDbOpen( + F_Database * pDatabase, + F_Db ** ppDb) + { + RCODE rc = NE_XFLM_OK; + IF_Db * pDb; + + if (RC_OK( rc = openDatabase( pDatabase, NULL, NULL, NULL, + NULL, 0, TRUE, NULL, NULL, NULL, &pDb))) + { + *ppDb = (F_Db *)pDb; + } + return( rc); + } + + RCODE openDb( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + IF_Db ** ppDb); + + void enableOutOfMemorySimulation( + FLMBOOL bEnable); + + FLMBOOL outOfMemorySimulationEnabled( void); + + static FINLINE FLMBOOL validBlockSize( + FLMUINT uiBlockSize) + { + if( uiBlockSize == 4096 || uiBlockSize == 8192) + { + return( TRUE); + } + + return( FALSE); + } + + static FLMUINT languageToNum( + const char * pszLanguage); + + static void languageToStr( + FLMINT iLangNum, + char * pszLanguage); + + static void getDbBasePath( + char * pszBaseDbName, + const char * pszDbName, + FLMUINT * puiBaseDbNameLen); + + RCODE XFLMAPI compareUTF8Strings( + const FLMBYTE * pucLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMBYTE * pucRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult); + + RCODE XFLMAPI compareUnicodeStrings( + const FLMUNICODE * puzLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMUNICODE * puzRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult); + + RCODE XFLMAPI utf8IsSubStr( + const FLMBYTE * pszString, + const FLMBYTE * pszSubString, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMBOOL * pbExists); + + FLMBOOL XFLMAPI uniIsUpper( + FLMUNICODE uzChar); + + FLMBOOL XFLMAPI uniIsLower( + FLMUNICODE uzChar); + + FLMBOOL XFLMAPI uniIsAlpha( + FLMUNICODE uzChar); + + FLMBOOL XFLMAPI uniIsDecimalDigit( + FLMUNICODE uzChar); + + FLMUNICODE XFLMAPI uniToLower( + FLMUNICODE uzChar); + + RCODE XFLMAPI nextUCS2Char( + const FLMBYTE ** ppszUTF8, + const FLMBYTE * pszEndOfUTF8String, + FLMUNICODE * puzChar); + + RCODE XFLMAPI numUCS2Chars( + const FLMBYTE * pszUTF8, + FLMUINT * puiNumChars); + + RCODE XFLMAPI waitToClose( + const char * pszDbPath); + + RCODE XFLMAPI createIFNodeInfo( + IF_NodeInfo ** ifppNodeInfo); + + RCODE XFLMAPI createIFBTreeInfo( + IF_BTreeInfo ** ifppBTreeInfo); + +private: + + // Methods + + RCODE readIniFile( void); + + RCODE setCacheParams( + F_IniFile * pIniFile); + + void cleanup( void); + + FINLINE RCODE internalDbDup( + F_Db * pDb, + F_Db ** ppDb + ) + { + RCODE rc = NE_XFLM_OK; + IF_Db * ifpDb; + + if (RC_OK( rc = openDatabase( pDb->m_pDatabase, NULL, NULL, + NULL, NULL, 0, TRUE, NULL, NULL, NULL, &ifpDb))) + { + *ppDb = (F_Db *)ifpDb; + } + + return( rc); + } + + void lockSysData( void); + + void unlockSysData( void); + + RCODE initCharMappingTables( void); + + void initFastBlockCheckSum( void); + + void freeCharMappingTables( void); + + RCODE checkErrorCodeTables( void); + + RCODE allocDb( + F_Db ** ppDb, + FLMBOOL bInternalOpen); + + RCODE findDatabase( + const char * pszDbPath, + const char * pszDataDir, + F_Database ** ppDatabase); + + RCODE checkDatabaseClosed( + const char * pszDbName, + const char * pszDataDir); + + RCODE allocDatabase( + const char * pszDbPath, + const char * pszDataDir, + FLMBOOL bTempDb, + F_Database ** ppDatabase); + + RCODE openDatabase( + F_Database * pDatabase, + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + FLMBOOL bInternalOpen, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus, + IF_FileHdl * pLockFileHdl, + IF_Db ** ppDb); + + RCODE copyDb( + const char * pszSrcDbName, + const char * pszSrcDataDir, + const char * pszSrcRflDir, + const char * pszDestDbName, + const char * pszDestDataDir, + const char * pszDestRflDir, + IF_DbCopyStatus * ifpStatus); + + static RCODE monitorThrd( + F_Thread * pThread); + + static RCODE cacheCleanupThrd( + F_Thread * pThread); + + static void checkNotUsedObjects( void); + + static FLMUINT32 m_ui32FlmSysSpinLock; + static FLMUINT m_uiFlmSysStartupCount; + +friend class F_Db; +friend class F_Database; +friend class F_DbRebuild; +friend class F_DbCheck; +}; + +// Supported text types + +typedef enum +{ + XFLM_UNICODE_TEXT = 1, + XFLM_UTF8_TEXT +} eXFlmTextType; + +/*------------------------------------------------------ + FLAIM Processing Hooks (call-backs) +-------------------------------------------------------*/ + +#define FLM_DATA_LEFT_TRUNCATED 0x10 // Data is left truncated +#define FLM_DATA_RIGHT_TRUNCATED 0x20 // Data is right truncated + +RCODE flmReadSEN( + IF_IStream * pIStream, + FLMUINT * puiValue, + FLMUINT * puiLength = NULL); + +RCODE flmReadSEN64( + IF_IStream * pIStream, + FLMUINT64 * pui64Value, + FLMUINT * puiLength = NULL); + +RCODE flmReadUTF8CharAsUnicode( + IF_IStream * pStream, + FLMUNICODE * puChar); + +RCODE flmReadUTF8CharAsUTF8( + IF_IStream * pIStream, + FLMBYTE * pucBuf, + FLMUINT * puiLen); + +RCODE flmReadStorageAsText( + IF_IStream * pIStream, + FLMBYTE * pucStorageData, + FLMUINT uiDataLen, + FLMUINT uiDataType, + void * pvBuffer, + FLMUINT uiBufLen, + eXFlmTextType eTextType, + FLMUINT uiMaxCharsToRead, + FLMUINT uiCharOffset, + FLMUINT * puiCharsRead, + FLMUINT * puiBufferBytesUsed); + +RCODE flmReadStorageAsBinary( + IF_IStream * pIStream, + void * pvBuffer, + FLMUINT uiBufLen, + FLMUINT uiByteOffset, + FLMUINT * puiBytesRead); + +RCODE flmReadStorageAsNumber( + IF_IStream * pIStream, + FLMUINT uiDataType, + FLMUINT64 * pui64Number, + FLMBOOL * pbNeg); + +RCODE flmReadLine( + IF_IStream * pIStream, + FLMBYTE * pucBuffer, + FLMUINT * puiSize); + +typedef struct F_CollStreamPos +{ + FLMUINT64 ui64Position; + FLMUNICODE uNextChar; +} F_CollStreamPos; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_CollIStream : public F_PosIStream +{ +public: + + F_CollIStream() + { + m_pIStream = NULL; + m_uiLanguage = 0; + m_bMayHaveWildCards = FALSE; + m_bUnicodeStream = FALSE; + m_uNextChar = 0; + } + + virtual ~F_CollIStream() + { + if( m_pIStream) + { + m_pIStream->Release(); + } + } + + RCODE open( + IF_PosIStream * pIStream, + FLMBOOL bUnicodeStream, + FLMUINT uiLanguage, + FLMUINT uiCompareRules, + FLMBOOL bMayHaveWildCards) + { + if( m_pIStream) + { + m_pIStream->Release(); + } + + m_pIStream = pIStream; + m_pIStream->AddRef(); + m_uiLanguage = uiLanguage; + m_uiCompareRules = uiCompareRules; + m_bCaseSensitive = (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) + ? FALSE + : TRUE; + m_bMayHaveWildCards = bMayHaveWildCards; + m_bUnicodeStream = bUnicodeStream; + m_ui64EndOfLeadingSpacesPos = 0; + return( NE_XFLM_OK); + } + + void XFLMAPI close( void) + { + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + } + } + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_pIStream->read( pvBuffer, + uiBytesToRead, puiBytesRead))) + { + goto Exit; + } + + Exit: + + return( rc); + } + + RCODE read( + FLMBOOL bAllowTwoIntoOne, + FLMUNICODE * puChar, + FLMBOOL * pbCharIsWild, + FLMUINT16 * pui16Col, + FLMUINT16 * pui16SubCol, + FLMBYTE * pucCase); + + FINLINE FLMUINT64 XFLMAPI totalSize( void) + { + if( m_pIStream) + { + return( m_pIStream->totalSize()); + } + + return( 0); + } + + FINLINE FLMUINT64 XFLMAPI remainingSize( void) + { + if( m_pIStream) + { + return( m_pIStream->remainingSize()); + } + + return( 0); + } + + FINLINE RCODE XFLMAPI positionTo( + FLMUINT64) + { + return( RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED)); + } + + FINLINE RCODE XFLMAPI positionTo( + F_CollStreamPos * pPos) + { + + // Should never be able to position back to before the + // leading spaces. + + m_uNextChar = pPos->uNextChar; + flmAssert( pPos->ui64Position >= m_ui64EndOfLeadingSpacesPos); + return( m_pIStream->positionTo( pPos->ui64Position)); + } + + FINLINE FLMUINT64 XFLMAPI getCurrPosition( void) + { + flmAssert( 0); + return( 0); + } + + FINLINE void XFLMAPI getCurrPosition( + F_CollStreamPos * pPos) + { + pPos->uNextChar = m_uNextChar; + pPos->ui64Position = m_pIStream->getCurrPosition(); + } + +private: + + FINLINE RCODE readCharFromStream( + FLMUNICODE * puChar) + { + RCODE rc = NE_XFLM_OK; + + if( m_bUnicodeStream) + { + if( RC_BAD( rc = m_pIStream->read( puChar, sizeof( FLMUNICODE), NULL))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( + m_pIStream, puChar))) + { + goto Exit; + } + } + + Exit: + + return( rc); + } + + IF_PosIStream * m_pIStream; + FLMUINT m_uiLanguage; + FLMBOOL m_bCaseSensitive; + FLMUINT m_uiCompareRules; + FLMUINT64 m_ui64EndOfLeadingSpacesPos; + FLMBOOL m_bMayHaveWildCards; + FLMBOOL m_bUnicodeStream; + FLMUNICODE m_uNextChar; +}; + +#define FLM_ENCRYPT_CHUNK_SIZE 512 + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_BTreeIStream : public F_PosIStream +{ +public: + + F_BTreeIStream() + { + m_pucBuffer = NULL; + m_pBTree = NULL; + m_bReleaseBTree = FALSE; + reset(); + } + + virtual ~F_BTreeIStream() + { + reset(); + } + + FINLINE void reset( void) + { + m_pNextInPool = NULL; + if( m_pBTree && m_bReleaseBTree) + { + m_pBTree->btClose(); + gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pBTree); + m_pBTree = NULL; + } + + if( m_pucBuffer != &m_ucBuffer [0]) + { + f_free( &m_pucBuffer); + } + + m_pDb = NULL; + m_uiCollection = 0; + m_ui64NodeId = 0; + m_pBTree = NULL; + m_bReleaseBTree = FALSE; + m_uiKeyLength = 0; + m_uiStreamSize = 0; + m_uiBufferBytes = 0; + m_uiBufferOffset = 0; + m_uiBufferStartOffset = 0; + m_uiBufferSize = sizeof( m_ucBuffer); + m_pucBuffer = &m_ucBuffer [0]; + m_ui32BlkAddr = 0; + m_uiOffsetIndex = 0; + m_bDataEncrypted = FALSE; + m_bBufferDecrypted = FALSE; + m_uiDataLength = 0; + m_uiEncDefId = 0; + } + + RCODE open( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT32 ui32BlkAddr = 0, + FLMUINT uiOffsetIndex = 0); + + RCODE open( + F_Db * pDb, + F_Btree * pBTree, + FLMUINT uiFlags, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT32 ui32BlkAddr = 0, + FLMUINT uiOffsetIndex = 0); + + FINLINE FLMUINT64 XFLMAPI totalSize( void) + { + return( m_uiStreamSize); + } + + FINLINE FLMUINT64 XFLMAPI remainingSize( void) + { + return( m_uiStreamSize - (m_uiBufferStartOffset + m_uiBufferOffset)); + } + + FINLINE void XFLMAPI close( void) + { + reset(); + } + + RCODE XFLMAPI positionTo( + FLMUINT64 ui64Position); + + FINLINE FLMUINT64 XFLMAPI getCurrPosition( void) + { + return( m_uiBufferStartOffset + m_uiBufferOffset); + } + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FLMUINT32 XFLMAPI Release( void); + + FINLINE FLMUINT32 getBlkAddr( void) + { + return( m_ui32BlkAddr); + } + + FINLINE FLMUINT getOffsetIndex( void) + { + return( m_uiOffsetIndex); + } + +private: + + F_BTreeIStream * m_pNextInPool; + F_Db * m_pDb; + F_Btree * m_pBTree; + FLMUINT m_uiCollection; + FLMUINT64 m_ui64NodeId; + FLMUINT m_uiStreamSize; + FLMUINT m_uiKeyLength; + FLMUINT m_uiBufferBytes; + FLMUINT m_uiBufferSize; + FLMUINT m_uiBufferOffset; + FLMUINT m_uiBufferStartOffset; + FLMUINT m_uiDataLength; + FLMUINT m_uiEncDefId; + FLMBYTE m_ucBuffer[ FLM_ENCRYPT_CHUNK_SIZE]; + FLMBYTE * m_pucBuffer; + FLMUINT m_uiOffsetIndex; + FLMUINT32 m_ui32BlkAddr; + FLMBOOL m_bReleaseBTree; + FLMBOOL m_bDataEncrypted; + FLMBOOL m_bBufferDecrypted; + FLMBYTE m_ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE m_ucIV [16]; +friend class F_DOMNode; +friend class F_CachedNode; +friend class F_NodePool; +friend class F_Db; +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_NodeBufferIStream : public F_BufferIStream +{ +public: + + F_NodeBufferIStream() + { + m_pCachedNode = NULL; + reset(); + } + + virtual ~F_NodeBufferIStream() + { + reset(); + } + + FINLINE void reset( void) + { + if (m_pCachedNode) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + m_pCachedNode->decrNodeUseCount(); + m_pCachedNode->decrStreamUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + m_pCachedNode = NULL; + } + } + + F_CachedNode * m_pCachedNode; +friend class F_CachedNode; +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_DOMNode : public IF_DOMNode, public XF_Base +{ +public: + + F_DOMNode() + { + m_pCachedNode = NULL; + resetDOMNode( FALSE); + } + + virtual ~F_DOMNode() + { + resetDOMNode( FALSE); + } + + void resetDOMNode( + FLMBOOL bMutexAlreadyLocked) + { + m_pNextInPool = NULL; + m_uiAttrNameId = 0; + + if (m_pCachedNode) + { + if( !bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + m_pCachedNode->decrNodeUseCount(); + if( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + m_pCachedNode = NULL; + } + } + + FLMUINT32 XFLMAPI Release( void); + + RCODE XFLMAPI createNode( + IF_Db * pDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewNode, + FLMUINT64 * pui64NodeId = NULL); + + RCODE XFLMAPI createChildElement( + IF_Db * pDb, + FLMUINT uiChildElementNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewChildElementNode, + FLMUINT64 * pui64NodeId = NULL); + + RCODE XFLMAPI deleteNode( + IF_Db * pDb); + + RCODE XFLMAPI deleteChildren( + IF_Db * pDb, + FLMUINT uiNameId = 0); + + RCODE XFLMAPI createAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode); + + RCODE XFLMAPI getFirstAttribute( + IF_Db * pDb, + IF_DOMNode ** ppAttrNode); + + RCODE XFLMAPI getLastAttribute( + IF_Db * pDb, + IF_DOMNode ** ppAttrNode); + + FINLINE RCODE XFLMAPI getAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode) + { + return( hasAttribute( pDb, uiAttrNameId, ppAttrNode)); + } + + RCODE XFLMAPI deleteAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId); + + RCODE XFLMAPI hasAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode = NULL); + + RCODE XFLMAPI hasAttributes( + IF_Db * pDb, + FLMBOOL * pbHasAttrs); + + RCODE XFLMAPI hasNextSibling( + IF_Db * pDb, + FLMBOOL * pbHasNextSibling); + + RCODE XFLMAPI hasPreviousSibling( + IF_Db * pDb, + FLMBOOL * pbHasPreviousSibling); + + RCODE XFLMAPI hasChildren( + IF_Db * pDb, + FLMBOOL * pbHasChildren); + + RCODE XFLMAPI isNamespaceDecl( + IF_Db * pDb, + FLMBOOL * pbIsNamespaceDecl); + + FINLINE eDomNodeType XFLMAPI getNodeType( void) + { + if( m_uiAttrNameId) + { + return( ATTRIBUTE_NODE); + } + else if( m_pCachedNode) + { + return( m_pCachedNode->getNodeType()); + } + + flmAssert( 0); + return( INVALID_NODE); + } + + RCODE XFLMAPI getNodeId( + IF_Db * pDb, + FLMUINT64 * pui64NodeId); + + RCODE XFLMAPI getParentId( + IF_Db * pDb, + FLMUINT64 * pui64ParentId); + + RCODE XFLMAPI getDocumentId( + IF_Db * pDb, + FLMUINT64 * pui64DocumentId); + + RCODE XFLMAPI getPrevSibId( + IF_Db * pDb, + FLMUINT64 * pui64PrevSibId); + + RCODE XFLMAPI getNextSibId( + IF_Db * pDb, + FLMUINT64 * pui64NextSibId); + + RCODE XFLMAPI getFirstChildId( + IF_Db * pDb, + FLMUINT64 * pui64FirstChildId); + + RCODE XFLMAPI getLastChildId( + IF_Db * pDb, + FLMUINT64 * pui64LastChildId); + + RCODE XFLMAPI getNameId( + IF_Db * pDb, + FLMUINT * puiNameId); + + virtual RCODE XFLMAPI getEncDefId( + IF_Db * pDb, + FLMUINT * puiEncDefId); + + RCODE XFLMAPI getDataType( + IF_Db * pDb, + FLMUINT * puiDataType); + + RCODE XFLMAPI getDataLength( + IF_Db * pDb, + FLMUINT * puiLength); + + FINLINE RCODE XFLMAPI getUINT32( + IF_Db * pDb, + FLMUINT32 * pui32Value) + { + RCODE rc; + FLMUINT64 ui64Value; + + if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, NULL))) + { + return( rc); + } + + return( convertToUINT32( ui64Value, FALSE, pui32Value)); + } + + FINLINE RCODE XFLMAPI getUINT( + IF_Db * pDb, + FLMUINT * puiValue) + { + RCODE rc; + FLMUINT64 ui64Value; + + if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, NULL))) + { + return( rc); + } + + return( convertToUINT( ui64Value, FALSE, puiValue)); + } + + FINLINE RCODE XFLMAPI getUINT64( + IF_Db * pDb, + FLMUINT64 * pui64Value) + { + return( getNumber64( (F_Db *)pDb, pui64Value, NULL)); + } + + FINLINE RCODE XFLMAPI getINT32( + IF_Db * pDb, + FLMINT32 * pi32Value) + { + RCODE rc; + FLMUINT64 ui64Value; + FLMBOOL bNeg; + + if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) + { + return( rc); + } + + return( convertToINT32( ui64Value, bNeg, pi32Value)); + } + + FINLINE RCODE XFLMAPI getINT( + IF_Db * pDb, + FLMINT * piValue) + { + RCODE rc; + FLMUINT64 ui64Value; + FLMBOOL bNeg; + + if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) + { + return( rc); + } + + return( convertToINT( ui64Value, bNeg, piValue)); + } + + FINLINE RCODE XFLMAPI getINT64( + IF_Db * pDb, + FLMINT64 * pi64Value) + { + RCODE rc; + FLMUINT64 ui64Value; + FLMBOOL bNeg; + + if( RC_BAD( rc = getNumber64( (F_Db *)pDb, &ui64Value, &bNeg))) + { + return( rc); + } + + return( convertToINT64( ui64Value, bNeg, pi64Value)); + } + + RCODE XFLMAPI getMetaValue( + IF_Db * pDb, + FLMUINT64 * pui64Value); + + FINLINE RCODE XFLMAPI getUnicodeChars( + IF_Db * pDb, + FLMUINT * puiNumChars) + { + return( getUnicode( pDb, NULL, 0, 0, FLM_MAX_UINT, puiNumChars)); + } + + RCODE XFLMAPI getUnicode( + IF_Db * pDb, + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL); + + RCODE XFLMAPI getUnicode( + IF_Db * pDb, + FLMUNICODE ** ppuzUnicodeValue); + + RCODE XFLMAPI getUnicode( + IF_Db * pDb, + IF_DynaBuf * pDynaBuf); + + RCODE XFLMAPI getUTF8( + IF_Db * pDb, + FLMBYTE * pszValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL); + + RCODE XFLMAPI getUTF8( + IF_Db * pDb, + FLMBYTE ** ppszUTF8Value); + + RCODE XFLMAPI getUTF8( + IF_Db * pDb, + IF_DynaBuf * pDynaBuf); + + RCODE XFLMAPI getBinary( + IF_Db * pDb, + void * pvValue, + FLMUINT uiByteOffset, + FLMUINT uiBytesRequested, + FLMUINT * puiBytesReturned); + + RCODE XFLMAPI getBinary( + IF_Db * pDb, + IF_DynaBuf * pBuffer); + + FINLINE RCODE XFLMAPI getAttributeValueUINT32( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT32 * pui32Num) + { + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, + uiAttrName, &ui64Num, &bNeg))) + { + return( rc); + } + + return( convertToUINT32( ui64Num, bNeg, pui32Num)); + } + + FINLINE RCODE XFLMAPI getAttributeValueUINT32( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT32 * pui32Num, + FLMUINT32 ui32NotFoundDefault) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = getAttributeValueUINT32( + pDb, uiAttrName, pui32Num))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + return( rc); + } + + *pui32Num = ui32NotFoundDefault; + rc = NE_XFLM_OK; + } + + return( rc); + } + + FINLINE RCODE XFLMAPI getAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT * puiNum) + { + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, + uiAttrName, &ui64Num, &bNeg))) + { + return( rc); + } + + return( convertToUINT( ui64Num, bNeg, puiNum)); + } + + FINLINE RCODE XFLMAPI getAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT * puiNum, + FLMUINT uiNotFoundDefault) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = getAttributeValueUINT( + pDb, uiAttrName, puiNum))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + return( rc); + } + + *puiNum = uiNotFoundDefault; + rc = NE_XFLM_OK; + } + + return( rc); + } + + FINLINE RCODE XFLMAPI getAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT64 * pui64Num) + { + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, + uiAttrName, &ui64Num, &bNeg))) + { + return( rc); + } + + return( convertToUINT64( ui64Num, bNeg, pui64Num)); + } + + FINLINE RCODE XFLMAPI getAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT64 * pui64Num, + FLMUINT64 ui64NotFoundDefault) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = getAttributeValueUINT64( + pDb, uiAttrName, pui64Num))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + return( rc); + } + + *pui64Num = ui64NotFoundDefault; + rc = NE_XFLM_OK; + } + + return( rc); + } + + FINLINE RCODE XFLMAPI getAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT * piNum) + { + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, + uiAttrName, &ui64Num, &bNeg))) + { + return( rc); + } + + return( convertToINT( ui64Num, bNeg, piNum)); + } + + FINLINE RCODE XFLMAPI getAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT * piNum, + FLMINT iNotFoundDefault) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = getAttributeValueINT( + pDb, uiAttrName, piNum))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + return( rc); + } + + *piNum = iNotFoundDefault; + rc = NE_XFLM_OK; + } + + return( rc); + } + + FINLINE RCODE XFLMAPI getAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT64 * pi64Num) + { + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = getAttributeValueNumber( (F_Db *)pDb, + uiAttrName, &ui64Num, &bNeg))) + { + return( rc); + } + + return( convertToINT64( ui64Num, bNeg, pi64Num)); + } + + FINLINE RCODE XFLMAPI getAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT64 * pi64Num, + FLMINT64 i64NotFoundDefault) + { + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = getAttributeValueINT64( + pDb, uiAttrName, pi64Num))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + return( rc); + } + + *pi64Num = i64NotFoundDefault; + rc = NE_XFLM_OK; + } + + return( rc); + } + + FINLINE RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) + { + return( getAttributeValueText( pDb, uiAttrName, XFLM_UNICODE_TEXT, + puzValueBuffer, uiBufferSize, puiCharsReturned, puiBufferBytesUsed)); + } + + RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUNICODE ** ppuzValueBuffer); + + RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrName, + IF_DynaBuf * pDynaBuf); + + RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMBYTE * pucValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) + { + return( getAttributeValueText( pDb, uiAttrName, XFLM_UTF8_TEXT, + pucValueBuffer, uiBufferSize, puiCharsReturned, puiBufferBytesUsed)); + } + + RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMBYTE ** ppszValueBuffer); + + RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrName, + IF_DynaBuf * pDynaBuf); + + RCODE XFLMAPI getAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrName, + void * pvValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLength); + + RCODE XFLMAPI getAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrName, + IF_DynaBuf * pDynaBuf); + + FINLINE RCODE XFLMAPI setUINT( + IF_Db * pDb, + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) + { + return( setNumber64( pDb, 0, uiValue, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setUINT64( + IF_Db * pDb, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) + { + return( setNumber64( pDb, 0, ui64Value, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setINT( + IF_Db * pDb, + FLMINT iValue, + FLMUINT uiEncDefId = 0) + { + return( setNumber64( pDb, iValue, 0, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setINT64( + IF_Db * pDb, + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) + { + return( setNumber64( pDb, i64Value, 0, uiEncDefId)); + } + + RCODE XFLMAPI setMetaValue( + IF_Db * pDb, + FLMUINT64 ui64Value); + + FINLINE RCODE XFLMAPI setUnicode( + IF_Db * pDb, + const FLMUNICODE * puzValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) + { + F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; + + if( bLast && !pDatabase->m_pPendingInput) + { + return( setTextFastPath( (F_Db *)pDb, puzValue, uiValueLength, + XFLM_UNICODE_TEXT, uiEncDefId)); + } + else + { + return( setTextStreaming( (F_Db *)pDb, puzValue, + uiValueLength, XFLM_UNICODE_TEXT, bLast, uiEncDefId)); + } + } + + FINLINE RCODE XFLMAPI setUTF8( + IF_Db * pDb, + const FLMBYTE * pszValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) + { + F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; + + if( bLast && !pDatabase->m_pPendingInput) + { + return( setTextFastPath( (F_Db *)pDb, pszValue, uiValueLength, + XFLM_UTF8_TEXT, uiEncDefId)); + } + else + { + return( setTextStreaming( (F_Db *)pDb, pszValue, + uiValueLength, XFLM_UTF8_TEXT, bLast, uiEncDefId)); + } + } + + FINLINE RCODE XFLMAPI setBinary( + IF_Db * pDb, + const void * pvValue, + FLMUINT uiValueLength, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) + { + F_Database * pDatabase = ((F_Db *)pDb)->m_pDatabase; + + if( bLast && !pDatabase->m_pPendingInput) + { + return( setBinaryFastPath( (F_Db *)pDb, pvValue, + uiValueLength, uiEncDefId)); + } + else + { + return( setBinaryStreaming( (F_Db *)pDb, pvValue, uiValueLength, bLast, + uiEncDefId)); + } + } + + FINLINE RCODE XFLMAPI setAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) + { + return( setAttributeValueNumber( pDb, uiAttrName, + 0, uiValue, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) + { + return( setAttributeValueNumber( pDb, uiAttrName, + 0, ui64Value, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT iValue, + FLMUINT uiEncDefId = 0) + { + return( setAttributeValueNumber( pDb, uiAttrName, + iValue, 0, uiEncDefId)); + } + + FINLINE RCODE XFLMAPI setAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) + { + return( setAttributeValueNumber( pDb, uiAttrName, + i64Value, 0, uiEncDefId)); + } + + RCODE XFLMAPI setAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrName, + const FLMUNICODE * puzValue, + FLMUINT uiEncDefId = 0); + + RCODE XFLMAPI setAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrName, + const FLMBYTE * pszValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0); + + RCODE XFLMAPI setAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrName, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0); + + RCODE XFLMAPI getDocumentNode( + IF_Db * pDb, + IF_DOMNode ** ppDocument); + + RCODE XFLMAPI getNextDocument( + IF_Db * pDb, + IF_DOMNode ** ppNextDocument); + + RCODE XFLMAPI getPreviousDocument( + IF_Db * pDb, + IF_DOMNode ** ppPrevDocument); + + RCODE XFLMAPI getParentNode( + IF_Db * pDb, + IF_DOMNode ** ppParent); + + RCODE XFLMAPI getFirstChild( + IF_Db * pDb, + IF_DOMNode ** ppFirstChild); + + RCODE XFLMAPI getLastChild( + IF_Db * pDb, + IF_DOMNode ** ppLastChild); + + RCODE XFLMAPI getNextSibling( + IF_Db * pDb, + IF_DOMNode ** ppNextSibling); + + RCODE XFLMAPI getPreviousSibling( + IF_Db * pDb, + IF_DOMNode ** ppPrevSibling); + + RCODE XFLMAPI getChild( + IF_Db * pDb, + eDomNodeType eNodeType, + IF_DOMNode ** ppChild); + + RCODE XFLMAPI getChildElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppChild, + FLMUINT uiFlags = 0); + + RCODE XFLMAPI getSiblingElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + FLMBOOL bNext, + IF_DOMNode ** ppSibling); + + RCODE XFLMAPI getAncestorElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppAncestor); + + RCODE XFLMAPI getDescendantElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppDescendant); + + RCODE XFLMAPI insertBefore( + IF_Db * pDb, + IF_DOMNode * pNewChild, + IF_DOMNode * pRefChild); + + FINLINE RCODE XFLMAPI getPrefix( + IF_Db * pDb, + FLMUNICODE * puzPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return( getPrefix( TRUE, pDb, (void *)puzPrefixBuffer, uiBufferSize, + puiCharsReturned)); + } + + FINLINE RCODE XFLMAPI getPrefix( + IF_Db * pDb, + char * pszPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return( getPrefix( FALSE, pDb, (void *)pszPrefixBuffer, uiBufferSize, + puiCharsReturned)); + } + + RCODE XFLMAPI getPrefixId( + IF_Db * pDb, + FLMUINT * puiPrefixId); + + FINLINE RCODE XFLMAPI setPrefix( + IF_Db * pDb, + const FLMUNICODE * puzPrefix) + { + return setPrefix( TRUE, pDb, (void *)puzPrefix); + } + + FINLINE RCODE XFLMAPI setPrefix( + IF_Db * pDb, + const char * pszPrefix) + { + return setPrefix( FALSE, pDb, (void *)pszPrefix); + } + + RCODE XFLMAPI setPrefixId( + IF_Db * pDb, + FLMUINT uiPrefixId); + + FINLINE RCODE XFLMAPI getNamespaceURI( + IF_Db * pDb, + FLMUNICODE * puzNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return getNamespaceURI( TRUE, pDb, + (void *)puzNamespaceURIBuffer, uiBufferSize, puiCharsReturned); + } + + FINLINE RCODE XFLMAPI getNamespaceURI( + IF_Db * pDb, + char * pszNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return getNamespaceURI( FALSE, pDb, + (void *)pszNamespaceURIBuffer, uiBufferSize, puiCharsReturned); + } + + FINLINE RCODE XFLMAPI getLocalName( + IF_Db * pDb, + FLMUNICODE * puzLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return( getLocalName( TRUE, pDb, (void *)puzLocalNameBuffer, + uiBufferSize, puiCharsReturned)); + } + + FINLINE RCODE XFLMAPI getLocalName( + IF_Db * pDb, + char * pszLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned) + { + return( getLocalName( FALSE, pDb, (void *)pszLocalNameBuffer, uiBufferSize, + puiCharsReturned)); + } + + RCODE XFLMAPI getQualifiedName( + IF_Db * pDb, + FLMUNICODE * puzQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned); + + RCODE XFLMAPI getQualifiedName( + IF_Db * pDb, + char * pszQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned); + + FINLINE RCODE XFLMAPI getCollection( + IF_Db *, // pDb, + FLMUINT * puiCollection) + { + *puiCollection = m_pCachedNode->getCollection(); + return( NE_XFLM_OK); + } + + RCODE XFLMAPI createAnnotation( + IF_Db * pDb, + IF_DOMNode ** ppAnnotation, + FLMUINT64 * pui64NodeId = NULL); + + RCODE XFLMAPI getAnnotation( + IF_Db * pDb, + IF_DOMNode ** ppAnnotation); + + RCODE XFLMAPI getAnnotationId( + IF_Db * pDb, + FLMUINT64 * pui64AnnotationId); + + RCODE XFLMAPI hasAnnotation( + IF_Db * pDb, + FLMBOOL * pbHasAnnotation); + + FINLINE RCODE XFLMAPI getIStream( + IF_Db * pDb, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL) + { + return( getIStream( (F_Db *)pDb, NULL, ppIStream, + puiDataType, puiDataLength)); + } + + FINLINE RCODE XFLMAPI getTextIStream( + IF_Db * pDb, + IF_PosIStream ** ppIStream, + FLMUINT * puiNumChars = NULL) + { + return( getTextIStream( (F_Db *)pDb, NULL, ppIStream, puiNumChars)); + } + + FLMUINT XFLMAPI compareNode( + IF_DOMNode * pNode, + IF_Db * pDb1, + IF_Db * pDb2, + char * pszErrBuff, + FLMUINT uiErrBuffLen); + + RCODE XFLMAPI isDataLocalToNode( + IF_Db * pDb, + FLMBOOL * pbDataIsLocal); + + // Public methods that are not part of the exposed public API in the IF_DOMNode interface + + FINLINE FLMBOOL isNamespaceDecl( void) + { + if( getModeFlags() & FDOM_NAMESPACE_DECL) + { + return( TRUE); + } + + return( FALSE); + } + + RCODE XFLMAPI setTextFastPath( + F_Db * pDb, + const void * pvValue, + FLMUINT uiNumBytesInBuffer, + eXFlmTextType eTextType, + FLMUINT uiEncDefId); + + RCODE getNodeId( + F_Db * pDb, + FLMUINT64 * pui64NodeId, + FLMUINT * puiAttrNameId); + + FINLINE FLMUINT64 getNodeId( void) + { + if( m_uiAttrNameId) + { + flmAssert( 0); + return( 0); + } + + if( m_pCachedNode) + { + return( m_pCachedNode->getNodeId()); + } + + return( 0); + } + + FINLINE FLMUINT64 getIxNodeId( void) + { + if( m_pCachedNode) + { + return( m_pCachedNode->getNodeId()); + } + + return( 0); + } + + FINLINE FLMUINT getCollection( void) + { + if( m_pCachedNode) + { + return( m_pCachedNode->getCollection()); + } + + return( 0); + } + + RCODE getIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL); + + RCODE getTextIStream( + F_Db * pDb, + F_NodeBufferIStream * pStackStream, + IF_PosIStream ** ppIStream, + FLMUINT * puiNumChars = NULL); + + FINLINE FLMBOOL isQuarantined( void) + { + return( (getModeFlags() & FDOM_QUARANTINED) + ? TRUE + : FALSE); + } + + RCODE getAttributeValueNumber( + F_Db * pDb, + FLMUINT uiAttrName, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg); + + RCODE getAttributeValueText( + IF_Db * pDb, + FLMUINT uiAttrName, + eXFlmTextType eTextType, + void * pvBuffer, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned, + FLMUINT * puiBufferBytesUsed); + + RCODE setAttributeValueNumber( + IF_Db * pDb, + FLMUINT uiAttrName, + FLMINT64 i64Value, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId); + + RCODE deleteAttributes( + F_Db * pDb, + FLMUINT uiAttrToDelete, + FLMUINT uiFlags); + +private: + + // Methods + + RCODE setTextStreaming( + F_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + eXFlmTextType eTextType, + FLMBOOL bLast, + FLMUINT uiEncDefId = 0); + + RCODE setBinaryStreaming( + IF_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + FLMBOOL bLast, + FLMUINT uiEncDefId); + + RCODE setBinaryFastPath( + IF_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId); + + RCODE clearNodeValue( + F_Db * pDb); + + FINLINE RCODE makeWriteCopy( + F_Db * pDb) + { + return( gv_XFlmSysData.pNodeCacheMgr->makeWriteCopy( + pDb, &m_pCachedNode)); + } + + RCODE canSetValue( + F_Db * pDb, + FLMUINT uiDataType); + + RCODE isChildTypeValid( + eDomNodeType eChildNodeType); + + RCODE isDescendantOf( + F_Db * pDb, + F_DOMNode * pAncestor, + FLMBOOL * pbDescendant); + + RCODE getNumber64( + F_Db * pDb, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg); + + RCODE storeTextAsNumber( + F_Db * pDb, + void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0); + + RCODE storeTextAsBinary( + F_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0); + + RCODE storeBinaryAsText( + F_Db * pDb, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0); + + FINLINE RCODE syncFromDb( + F_Db * pDb) + { + F_CachedNode * pCachedNode = m_pCachedNode; + + if( !pCachedNode) + { + return( RC_SET( NE_XFLM_DOM_NODE_DELETED)); + } + else if( !pCachedNode->nodeLinkedToDatabase()) + { + return( _syncFromDb( pDb)); + } + else if( pDb->m_pDatabase != pCachedNode->getDatabase()) + { + return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); + } + else if( pCachedNode->isRightVersion( pDb->m_ui64CurrTransID) && + !pCachedNode->nodePurged()) + { + if( m_uiAttrNameId) + { + if( !pCachedNode->m_uiAttrCount || + !pCachedNode->getAttribute( m_uiAttrNameId, NULL)) + { + return( RC_SET( NE_XFLM_DOM_NODE_DELETED)); + } + } + + return( NE_XFLM_OK); + } + + return( _syncFromDb( pDb)); + } + + RCODE _syncFromDb( + F_Db * pDb); + + RCODE unlinkNode( + F_Db * pDb, + FLMUINT uiFlags); + + RCODE addModeFlags( + F_Db * pDb, + FLMUINT uiFlags); + + RCODE removeModeFlags( + F_Db * pDb, + FLMUINT uiFlags); + + FINLINE FLMBOOL canHaveChildren( void) + { + + if( m_pCachedNode) + { + eDomNodeType eNodeType = getNodeType(); + + return( (eNodeType == DOCUMENT_NODE || + eNodeType == ELEMENT_NODE) + ? TRUE + : FALSE); + } + + return( FALSE); + } + + RCODE getData( + F_Db * pDb, + FLMBYTE * pucBuffer, + FLMUINT * puiLength); + + void addNodeToMRUList( + F_DOMNode * pNode); + + void removeNodeFromMRUList( + F_DOMNode * pNode); + + RCODE getNodeFromMRUList( + F_Db * pDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + F_DOMNode ** ppNode); + + RCODE getNodeFromMRUListById( + F_Db * pDb, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppNode); + + RCODE getLocalName( + FLMBOOL bUnicode, + IF_Db * pDb, + void * pvLocalName, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned); + + RCODE getPrefix( + FLMBOOL bUnicode, + IF_Db * pDb, + void * pvPrefix, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned); + + RCODE setPrefix( + FLMBOOL bUnicode, + IF_Db * pDb, + void * pvPrefix); + + FINLINE FLMUINT64 getDocumentId( void) + { + if( m_pCachedNode) + { + return( m_pCachedNode->getDocumentId()); + } + + flmAssert( 0); + return( 0); + } + + FINLINE FLMBOOL isRootNode( void) + { + eDomNodeType eNodeType = getNodeType(); + + if( eNodeType == DOCUMENT_NODE || + eNodeType == ELEMENT_NODE) + { + return( m_pCachedNode->isRootNode()); + } + + return( FALSE); + } + + FINLINE FLMUINT64 getParentId( void) + { + if( !m_pCachedNode) + { + return( 0); + } + + if( m_uiAttrNameId) + { + return( m_pCachedNode->getNodeId()); + } + + return( m_pCachedNode->getParentId()); + } + + FINLINE void setParentId( + FLMUINT64 ui64ParentId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setParentId( ui64ParentId); + } + + FINLINE FLMUINT64 getFirstChildId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + if( m_pCachedNode) + { + return( m_pCachedNode->getFirstChildId()); + } + + return( 0); + } + + FINLINE FLMUINT64 getLastChildId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + if( m_pCachedNode) + { + return( m_pCachedNode->getLastChildId()); + } + + return( 0); + } + + FINLINE FLMUINT64 getPrevSibId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + if( m_pCachedNode) + { + return( m_pCachedNode->getPrevSibId()); + } + + return( 0); + } + + FINLINE void setPrevSibId( + FLMUINT64 ui64PrevSibId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setPrevSibId( ui64PrevSibId); + } + + FINLINE FLMUINT64 getNextSibId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + if( m_pCachedNode) + { + return( m_pCachedNode->getNextSibId()); + } + + return( 0); + } + + FINLINE void setNextSibId( + FLMUINT64 ui64NextSibId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setNextSibId( ui64NextSibId); + } + + FINLINE FLMUINT getDataChildCount( void) + { + flmAssert( getNodeType() == ELEMENT_NODE); + + if( m_pCachedNode) + { + return( m_pCachedNode->getDataChildCount()); + } + + return( 0); + } + + FINLINE void setDataChildCount( + FLMUINT uiDataChildCount) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() == ELEMENT_NODE); + + m_pCachedNode->setDataChildCount( uiDataChildCount); + } + + FINLINE FLMUINT getModeFlags( void) + { + if( m_uiAttrNameId) + { + return( m_pCachedNode->getModeFlags( m_uiAttrNameId)); + } + else if( m_pCachedNode) + { + return( m_pCachedNode->getModeFlags()); + } + + return( 0); + } + + RCODE getNamespaceURI( + FLMBOOL bUnicode, + IF_Db * pDb, + void * pvNamespaceURI, + FLMUINT uiBufSize, + FLMUINT * puiCharsReturned); + + RCODE setNumber64( + IF_Db * pDb, + FLMINT64 i64Value, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0); + + RCODE setStorageValue( + F_Db * pDb, + void * pvValue, + FLMUINT uiValueLen, + FLMUINT uiEncDefId, + FLMBOOL bLast); + + FINLINE void setBlkAddr( + FLMUINT32 ui32BlkAddr) + { + m_pCachedNode->setBlkAddr( ui32BlkAddr); + } + + FINLINE void setOffsetIndex( + FLMUINT uiOffsetIndex) + { + m_pCachedNode->setOffsetIndex( uiOffsetIndex); + } + + FINLINE FLMUINT getOffsetIndex( void) + { + return( m_pCachedNode->getOffsetIndex()); + } + + FINLINE FLMUINT32 getBlkAddr( void) + { + return( m_pCachedNode->getBlkAddr()); + } + + FINLINE void unsetNodeDirtyAndNew( + F_Db * pDb) + { + m_pCachedNode->unsetNodeDirtyAndNew( pDb); + } + + FINLINE FLMUINT getPrefixId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getPrefixId()); + } + + FINLINE void setPrefixId( + FLMUINT uiPrefixId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setPrefixId( uiPrefixId); + } + + FINLINE FLMUINT getNameId( void) + { + if( m_uiAttrNameId) + { + return( m_uiAttrNameId); + } + + return( m_pCachedNode->getNameId()); + } + + FINLINE void setNameId( + FLMUINT uiNameId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setNameId( uiNameId); + } + + FINLINE FLMUINT getDataType( void) + { + if( m_uiAttrNameId) + { + F_AttrItem * pAttrItem; + + if( (pAttrItem = m_pCachedNode->getAttribute( + m_uiAttrNameId, NULL)) == NULL) + { + flmAssert( 0); + return( XFLM_UNKNOWN_TYPE); + } + + return( pAttrItem->m_uiDataType); + } + + return( m_pCachedNode->getDataType()); + } + + FINLINE void setDataType( + FLMUINT uiDataType) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setDataType( uiDataType); + } + + FINLINE FLMUINT getDataLength( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getDataLength()); + } + + FINLINE void setDataLength( + FLMUINT uiDataLength) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setDataLength( uiDataLength); + } + + FINLINE FLMBYTE * getDataPtr( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getDataPtr()); + } + + FINLINE FLMBOOL getQuickNumber64( + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getQuickNumber64( pui64Num, pbNeg)); + } + + FINLINE FLMBOOL nodeIsDirty( void) + { + return( m_pCachedNode->nodeIsDirty()); + } + + FINLINE FLMBOOL nodeUncommitted( void) + { + return( m_pCachedNode->nodeUncommitted()); + } + + FINLINE FLMUINT getStreamUseCount( void) + { + return( m_pCachedNode->getStreamUseCount()); + } + + FINLINE FLMUINT64 getAnnotationId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getAnnotationId()); + } + + FINLINE void setAnnotationId( + FLMUINT64 ui64AnnotationId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setAnnotationId( ui64AnnotationId); + } + + FINLINE void setLastChildId( + FLMUINT64 ui64LastChildId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setLastChildId( ui64LastChildId); + } + + FINLINE void setFirstChildId( + FLMUINT64 ui64FirstChildId) + { + flmAssert( nodeUncommitted()); + flmAssert( getNodeType() != ATTRIBUTE_NODE); + + m_pCachedNode->setFirstChildId( ui64FirstChildId); + } + + FINLINE FLMUINT getEncDefId( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getEncDefId()); + } + + FINLINE F_Database * getDatabase( void) + { + if( m_pCachedNode) + { + return( m_pCachedNode->getDatabase()); + } + + return( NULL); + } + + FINLINE RCODE openPendingInput( + F_Db * pDb, + FLMUINT uiNewDataType) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->openPendingInput( pDb, uiNewDataType)); + } + + FINLINE FLMBOOL findChildElm( + FLMUINT uiChildElmNameId, + FLMUINT * puiInsertPos) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->findChildElm( uiChildElmNameId, puiInsertPos)); + } + + FINLINE RCODE removeChildElm( + FLMUINT uiChildElmOffset) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->removeChildElm( uiChildElmOffset)); + } + + FINLINE FLMUINT getChildElmCount( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getChildElmCount()); + } + + FINLINE FLMUINT64 getChildElmNodeId( + FLMUINT uiChildElmOffset) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getChildElmNodeId( uiChildElmOffset)); + } + + FINLINE FLMUINT getChildElmNameId( + FLMUINT uiChildElmOffset) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getChildElmNameId( uiChildElmOffset)); + } + + FINLINE FLMBOOL hasAttributes( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->hasAttributes()); + } + + FINLINE void setFlags( + FLMUINT uiFlags) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->setFlags( uiFlags); + } + + FINLINE void unsetFlags( + FLMUINT uiFlags) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->unsetFlags( uiFlags); + } + + FINLINE void setEncDefId( + FLMUINT uiEncDefId) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->setEncDefId( uiEncDefId); + } + + FINLINE RCODE flushPendingInput( + F_Db * pDb, + FLMBOOL bLast) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->flushPendingInput( pDb, bLast)); + } + + FINLINE FLMUINT getDataBufSize( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getDataBufSize()); + } + + FINLINE RCODE resizeDataBuffer( + FLMUINT uiSize, + FLMBOOL bMutexAlreadyLocked) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->resizeDataBuffer( uiSize, bMutexAlreadyLocked)); + } + + FINLINE RCODE headerToBuf( + FLMBOOL bFixedSizeHeader, + FLMBYTE * pucBuf, + FLMUINT * puiHeaderSize, + XFLM_NODE_INFO * pNodeInfo, + F_Db * pDb) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->headerToBuf( bFixedSizeHeader, pucBuf, + puiHeaderSize, pNodeInfo, pDb)); + } + + FINLINE FLMINT64 getQuickINT64( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getQuickINT64()); + } + + FINLINE FLMUINT64 getQuickUINT64( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getQuickUINT64()); + } + + FINLINE void setUINT64( + FLMUINT64 ui64Value) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->setUINT64( ui64Value); + } + + FINLINE void setINT64( + FLMINT64 i64Value) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->setINT64( i64Value); + } + + FINLINE FLMUINT64 getMetaValue( void) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + return( m_pCachedNode->getMetaValue()); + } + + FINLINE void setMetaValue( + FLMUINT64 ui64Value) + { + flmAssert( getNodeType() != ATTRIBUTE_NODE); + m_pCachedNode->setMetaValue( ui64Value); + } + + FINLINE RCODE checkAttrList( void) + { + if( !m_pCachedNode) + { + return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + else if( m_pCachedNode->getNodeType() != ELEMENT_NODE) + { + return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); + } + else if( !m_pCachedNode->m_uiAttrCount) + { + return( RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND)); + } + return( NE_XFLM_OK); + } + + // Data + + F_CachedNode * m_pCachedNode; + F_DOMNode * m_pNextInPool; + + // Valid only if this is an attribute node + + FLMUINT m_uiAttrNameId; + +friend class F_Db; +friend class F_Database; +friend class F_BTreeIStream; +friend class F_NodeBufferIStream; +friend class F_Dict; +friend class F_XMLImport; +friend class F_NodePool; +friend class F_DbRebuild; +friend class F_Query; +friend class FSCollectionCursor; +friend class F_NodeCacheMgr; +friend class F_Rfl; +friend class F_CachedNode; +friend class F_OldNodeList; +friend class F_DbCheck; +friend class F_NodeInfo; +}; + +/*=========================================================================== +Desc: Pool manager for DOM nodes +===========================================================================*/ +class F_NodePool : public XF_RefCount, public XF_Base +{ +public: + + F_NodePool() + { + m_pFirstBTreeIStream = NULL; + m_hMutex = F_MUTEX_NULL; + } + + ~F_NodePool(); + + RCODE setup( void); + + RCODE allocBTreeIStream( + F_BTreeIStream ** ppBTreeIStream); + + void insertBTreeIStream( + F_BTreeIStream * pBTreeIStream); + +private: + + F_BTreeIStream * m_pFirstBTreeIStream; + F_MUTEX m_hMutex; + +friend class F_DOMNode; +friend class F_BTreeIStream; +friend class F_NodeBufferIStream; +}; + +typedef struct +{ + FLMUINT uiCollectionNum; + FLMUINT64 ui64HighestNodeIdFound; + FLMUINT64 ui64HighestNextNodeIdFound; + FLMUINT uiNumNextNodeIdsFound; + FLMUINT uiEncId; +} COLLECTION_INFO, * COLLECTION_INFO_p; + +typedef struct Recov_Dict_Node * RECOV_DICT_NODE_p; +typedef struct Recov_Dict_Info * RECOV_DICT_INFO_p; + +typedef struct Recov_Dict_Node +{ + RECOV_DICT_NODE_p pNext; + F_DOMNode * pNode; + FLMUINT64 ui64NodeId; + FLMUINT uiElmOffset; + FLMBOOL bAdded; + FLMBOOL bGotFromDataCollection; + FLMUINT32 ui32BlkAddress; +} RECOV_DICT_NODE; + +typedef struct Recov_Dict_Info +{ + RECOV_DICT_NODE * pRecovNodes; + F_Pool pool; +} RECOV_DICT_INFO; + +typedef struct RSIxKeyTag +{ + FLMBYTE pucRSKeyBuf[ MAX_KEY_SIZ]; + FLMUINT uiRSKeyLen; + FLMBYTE pucRSDataBuf[ MAX_KEY_SIZ]; + FLMUINT uiRSDataLen; +} RS_IX_KEY; + +/****************************************************************************** +Desc: +******************************************************************************/ +class F_KeyCollector : public XF_RefCount, public XF_Base +{ + +public: + + F_KeyCollector( + F_DbCheck * pDbCheck) + { + m_pDbCheck = pDbCheck; + m_ui64TotalKeys = 0; + } + + ~F_KeyCollector(){} + + RCODE addKey( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY * pKref); + + FLMUINT64 getTotalKeys() + { + return m_ui64TotalKeys; + } + +private: + + F_DbCheck * m_pDbCheck; + FLMUINT64 m_ui64TotalKeys; + +friend class F_DbCheck; +}; + + +typedef struct +{ + F_DOMNode * pNode; // DOM Node to examine to see if it contains + // dictionary information. + FLMUINT uiCollection; // Collection the node came from. + FLMUINT64 ui64NodeId; // NodeId of node. +} CHK_RECORD, * CHK_RECORD_p; + +typedef struct +{ + FLMUINT64 ui64BytesUsed; + FLMUINT64 ui64ElementCount; + FLMUINT64 ui64ContElementCount; + FLMUINT64 ui64ContElmBytes; + FLMUINT uiBlockCount; + FLMINT iErrCode; + FLMUINT uiNumErrors; +} BLOCK_INFO; + +typedef struct +{ + FLMUINT64 ui64KeyCount; + BLOCK_INFO BlockInfo; +} LEVEL_INFO; + +typedef struct +{ + FLMUINT uiLfNum; + eLFileType eLfType; + FLMUINT uiRootBlk; + FLMUINT uiNumLevels; + LEVEL_INFO * pLevelInfo; +} LF_HDR; + +/****************************************************************************** +Desc: +******************************************************************************/ +typedef struct State_Info +{ + F_Db * pDb; + FLMUINT32 ui32BlkAddress; + FLMUINT32 ui32NextBlkAddr; + FLMUINT32 ui32PrevBlkAddr; + FLMUINT32 ui32LastChildAddr; + FLMUINT uiElmLastFlag; + FLMUINT64 ui64KeyCount; + FLMUINT64 ui64KeyRefs; + FLMUINT uiBlkType; + FLMUINT uiLevel; + FLMUINT uiRootLevel; + F_COLLECTION * pCollection; + FLMUINT uiElmOffset; + FLMBYTE * pucElm; + FLMUINT uiElmLen; + FLMUINT uiElmKeyLenOffset; // New + FLMUINT uiElmKeyOffset; // New + FLMBYTE * pucElmKey; + FLMUINT uiElmKeyLen; + FLMUINT uiCurKeyLen; // Used in Rebuild... + FLMUINT uiElmDataLenOffset; // New + FLMUINT uiElmDataOffset; // uiElmRecOffset; + FLMUINT uiElmDataLen; // uiElmRecLen; + FLMBYTE * pucElmData; // pucElmRec; + FLMUINT uiElmCounts; // New + FLMUINT uiElmCountsOffset; // New + FLMUINT uiElmOADataLenOffset; + FLMUINT uiElmOADataLen; + FLMUINT64 ui64ElmNodeId; + FLMBOOL bValidKey; + F_BLK_HDR * pBlkHdr; + F_NodeVerifier * pNodeVerifier; + BLOCK_INFO BlkInfo; + F_BtResultSet * pNodeRS; + F_BtResultSet * pXRefRS; + FLMUINT uiCurrLf; +} STATE_INFO; + +/****************************************************************************** +Desc: +******************************************************************************/ +class F_NodeVerifier : public XF_RefCount, public XF_Base +{ +public: + + F_NodeVerifier(); + + ~F_NodeVerifier(); + + void Reset( + STATE_INFO * pStateInfo); + + RCODE AddData( + FLMUINT64 ui64NodeId, + void * pucData, + FLMUINT uiDataLen); + + RCODE finalize( + F_Db * pDb, + F_Dict * pDict, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMBOOL bSkipDOMLinkCheck, + FLMINT * piElmErrCodeRV); + + FINLINE void setupNodeRS( + F_BtResultSet * pRS) + { + m_pRS = pRS; + } + + FINLINE void setupXRefRS( + F_BtResultSet * pXRefRS) + + { + m_pXRefRS = pXRefRS; + } + +private: + + RCODE verifyNameId( + F_Db * pDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + F_NameTable * pNameTable, + FLMINT * piErrCode); + + RCODE verifyPrefixId( + F_Db * pDb, + FLMUINT uiPrefixId, + F_NameTable * pNameTable, + FLMINT * piErrCode); + + RCODE checkForIndexes( + F_Db * pDb, + F_Dict * pDict, + FLMUINT uiCollection); + + FLMBOOL m_bFinalizeCalled; + FLMBYTE * m_pucBuf; + FLMBYTE m_ucBuf[ MAX_DOM_HEADER_SIZE]; + + FLMUINT m_uiBufSize; + FLMUINT m_uiBytesInBuf; + FLMUINT m_uiOverallLength; + + F_NODE_INFO m_nodeInfo; + + F_BtResultSet * m_pRS; + F_BtResultSet * m_pXRefRS; +}; + +/****************************************************************************** +Desc: +******************************************************************************/ +typedef struct +{ + F_NODE_INFO nodeInfo; + FLMUINT uiStorageFlags; + FLMUINT uiDataBufSize; + FLMBYTE * pucData; + FLMBOOL bUseFilename; + FLMBOOL bGotFromDataCollection; + FLMBOOL bProcessed; + FLMBYTE ucSortKey; +} REBUILD_NODE_INFO; + +/****************************************************************************** +Desc: +******************************************************************************/ +class RebuildNodeInfoStack : public XF_RefCount, public XF_Base +{ +public: + + RebuildNodeInfoStack(); + + ~RebuildNodeInfoStack(); + + RCODE setup( void); + + RCODE push( + REBUILD_NODE_INFO * pRebuildNodeInfo); + + RCODE pop( + REBUILD_NODE_INFO * pRebuildNodeInfo); + + void reset( void); + +private: + + REBUILD_NODE_INFO * m_pStack; + FLMUINT m_uiStackSize; + FLMUINT m_uiNextPos; + FLMBOOL m_bSetupCalled; +}; + +/****************************************************************************** +Desc: +******************************************************************************/ +typedef struct +{ + FLMUINT uiStartOfEntry; + FLMUINT uiEndOfEntry; +} BlkStruct; + +/****************************************************************************** +Desc: DbCheck object for verifying the condition of the database. +******************************************************************************/ +class F_DbCheck : public XF_RefCount, public XF_Base +{ + +public: + F_DbCheck( void) + { + m_bSkipDOMLinkCheck = FALSE; + m_pXRefRS = NULL; + m_pDb = NULL; + m_pIxd = NULL; + m_pCollection = NULL; + m_pLFile = NULL; + m_pDbInfo = NULL; + m_pBtPool = NULL; + m_pResultSetDb = NULL; + m_pRandGen = NULL; + m_pDbCheckStatus = NULL; + f_memset( m_szResultSetDibName, 0, sizeof( m_szResultSetDibName)); + m_bPhysicalCorrupt = FALSE; + m_bIndexCorrupt = FALSE; + m_LastStatusRc = NE_XFLM_OK; + m_uiFlags = 0; + m_bStartedUpdateTrans = FALSE; + m_puiIxArray = NULL; + m_pIxRSet = NULL; + m_bGetNextRSKey = FALSE; + f_memset( &m_IxKey1, 0, sizeof( m_IxKey1)); + f_memset( &m_IxKey2, 0, sizeof( m_IxKey2)); + m_pCurrRSKey = NULL; + m_pPrevRSKey = NULL; + m_pBlkEntries = NULL; + m_uiBlkEntryArraySize = 0; + } + + ~F_DbCheck(); + + RCODE dbCheck( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiFlags, + IF_DbInfo ** ppDbInfo, + IF_DbCheckStatus * pDbCheck); + +private: + + FINLINE RCODE chkCallProgFunc( void) + { + if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) + { + m_LastStatusRc = m_pDbCheckStatus->reportProgress( &m_Progress); + } + return( m_LastStatusRc); + } + + RCODE chkReportError( + FLMINT iErrCode, + FLMUINT uiErrLocale, + FLMUINT uiErrLfNumber, + FLMUINT uiErrLfType, + FLMUINT uiErrBTreeLevel, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrParentBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT64 ui64ErrNodeId); + + FINLINE XFLM_PROGRESS_CHECK_INFO * getProgress( void) + { + return( &m_Progress); + } + + RCODE getBtResultSet( + F_BtResultSet ** ppBtRSet); + + RCODE createAndOpenResultSetDb( void); + + RCODE closeAndDeleteResultSetDb( void); + + RCODE getDictInfo( void); + + RCODE verifyBlkChain( + BLOCK_INFO * pBlkInfo, + FLMUINT uiLocale, + FLMUINT uiFirstBlkAddr, + FLMUINT uiBlkType, + FLMBOOL * pbStartOverRV); + + RCODE verifyLFHBlocks( + FLMBOOL * pbStartOverRV); + + RCODE verifyAvailList( + FLMBOOL * pbStartOverRV); + + RCODE blkRead( + FLMUINT uiBlkAddress, + F_BLK_HDR ** ppBlkHdr, + F_CachedBlock ** ppSCache, + FLMINT * piBlkErrCodeRV); + + RCODE verifySubTree( + STATE_INFO * pParentState, + STATE_INFO * pStateInfo, + FLMUINT uiBlkAddress, + FLMBYTE ** ppucResetKey, + FLMUINT uiResetKeyLen, + FLMUINT64 ui64ResetNodeId); + + RCODE buildIndexKeyList( + FLMUINT64 * pui64TotalKeys); + + RCODE verifyBTrees( + FLMBOOL * pbStartOverRV); + + RCODE setupLfTable(); + + RCODE setupIxInfo( void); + + RCODE getLfInfo( + LF_HDR * pLogicalFile, + LFILE * pLFile); + + RCODE verifyNodePointers( + STATE_INFO * pStateInfo, + FLMINT * piErrCode); + + RCODE verifyDOChain( + STATE_INFO * pParentState, + FLMUINT uiBlkAddr, + FLMINT * piElmErrCode); + + RCODE chkGetNextRSKey( void); + + RCODE verifyIXRSet( + STATE_INFO * pStateInfo); + + RCODE resolveIXMissingKey( + STATE_INFO * pStateInfo); + + RCODE verifyComponentInDoc( + ICD * pIcd, + FLMUINT uiComponent, + F_DataVector * pKey, + FLMBOOL * pbInDoc); + + RCODE getKeySource( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbKeyInDoc, + FLMBOOL * pbKeyInIndex); + + RCODE resolveRSetMissingKey( + STATE_INFO * pStateInfo); + + RCODE chkVerifyKeyExists( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbFoundRV); + + RCODE addDelKeyRef( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bDelete); + + RCODE reportIxError( + STATE_INFO * pStateInfo, + FLMINT iErrCode, + FLMBYTE * pucErrKey, + FLMUINT uiErrKeyLen, + FLMBOOL * pbFixErrRV); + + RCODE startUpdate( void); + + RCODE keyToVector( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + IF_DataVector ** ppKeyRV); + + RCODE verifyIXRefs( + STATE_INFO * pStateInfo, + FLMUINT64 ui64ResetNodeId); + + RCODE verifyBlockStructure( + FLMUINT uiBlockSize, + F_BTREE_BLK_HDR * pBlkHdr); + + RCODE chkEndUpdate( void); + + F_Db * m_pDb; + F_COLLECTION * m_pCollection; + IXD * m_pIxd; + LFILE * m_pLFile; + F_DbInfo * m_pDbInfo; + FLMBOOL m_bSkipDOMLinkCheck; + F_BtResultSet * m_pXRefRS; + F_BtPool * m_pBtPool; + F_RandomGenerator * m_pRandGen; + char m_szResultSetDibName [F_PATH_MAX_SIZE]; + F_Db * m_pResultSetDb; + IF_DbCheckStatus * m_pDbCheckStatus; + FLMBOOL m_bPhysicalCorrupt; + FLMBOOL m_bIndexCorrupt; + XFLM_PROGRESS_CHECK_INFO m_Progress; + RCODE m_LastStatusRc; + FLMUINT m_uiFlags; + FLMBOOL m_bStartedUpdateTrans; + FLMUINT * m_puiIxArray; + F_BtResultSet * m_pIxRSet; + FLMBOOL m_bGetNextRSKey; + RS_IX_KEY m_IxKey1; + RS_IX_KEY m_IxKey2; + RS_IX_KEY * m_pCurrRSKey; + RS_IX_KEY * m_pPrevRSKey; + BlkStruct * m_pBlkEntries; + FLMUINT m_uiBlkEntryArraySize; +friend class F_DbInfo; +friend class F_KeyCollector; +}; + +// Check Error Codes + +/* +** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE +** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP +*/ +#define FLM_BAD_CHAR 1 +#define FLM_BAD_ASIAN_CHAR 2 +#define FLM_BAD_CHAR_SET 3 +#define FLM_BAD_TEXT_FIELD 4 +#define FLM_BAD_NUMBER_FIELD 5 +#define FLM_BAD_FIELD_TYPE 6 +#define FLM_BAD_IX_DEF 7 +#define FLM_MISSING_REQ_KEY_FIELD 8 +#define FLM_BAD_TEXT_KEY_COLL_CHAR 9 +#define FLM_BAD_TEXT_KEY_CASE_MARKER 10 +#define FLM_BAD_NUMBER_KEY 11 +#define FLM_BAD_BINARY_KEY 12 +#define FLM_BAD_CONTEXT_KEY 13 +#define FLM_BAD_KEY_FIELD_TYPE 14 +//#define Not_Used_15 15 +//#define Not_Used_16 16 +//#define Not_Used_17 17 +#define FLM_BAD_KEY_LEN 18 +#define FLM_BAD_LFH_LIST_PTR 19 +#define FLM_BAD_LFH_LIST_END 20 +#define FLM_INCOMPLETE_NODE 21 // Not really an error. Part of a field has been split across entries +#define FLM_BAD_BLK_END 22 +#define FLM_KEY_COUNT_MISMATCH 23 +#define FLM_REF_COUNT_MISMATCH 24 +#define FLM_BAD_CONTAINER_IN_KEY 25 +#define FLM_BAD_BLK_HDR_ADDR 26 +#define FLM_BAD_BLK_HDR_LEVEL 27 +#define FLM_BAD_BLK_HDR_PREV 28 +/* +** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE +** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP +*/ +#define FLM_BAD_BLK_HDR_NEXT 29 +#define FLM_BAD_BLK_HDR_TYPE 30 +#define FLM_BAD_BLK_HDR_ROOT_BIT 31 +#define FLM_BAD_BLK_HDR_BLK_END 32 +#define FLM_BAD_BLK_HDR_LF_NUM 33 +#define FLM_BAD_AVAIL_LIST_END 34 +#define FLM_BAD_PREV_BLK_NEXT 35 +#define FLM_BAD_FIRST_ELM_FLAG 36 // NOTE: This is only needed during rebuild +#define FLM_BAD_LAST_ELM_FLAG 37 // NOTE: This is only needed during rebuild +#define FLM_BAD_LEM 38 +#define FLM_BAD_ELM_LEN 39 +#define FLM_BAD_ELM_KEY_SIZE 40 +#define FLM_BAD_ELM_KEY 41 +#define FLM_BAD_ELM_KEY_ORDER 42 +#define FLM_BAD_ELM_KEY_COMPRESS 43 // NOTE: 5.x keys are not compressed +#define FLM_BAD_CONT_ELM_KEY 44 +#define FLM_NON_UNIQUE_FIRST_ELM_KEY 45 +#define FLM_BAD_ELM_OFFSET 46 +#define FLM_BAD_ELM_INVALID_LEVEL 47 +#define FLM_BAD_ELM_FLD_NUM 48 +#define FLM_BAD_ELM_FLD_LEN 49 +#define FLM_BAD_ELM_FLD_TYPE 50 +#define FLM_BAD_ELM_END 51 +#define FLM_BAD_PARENT_KEY 52 +#define FLM_BAD_ELM_DOMAIN_SEN 53 +#define FLM_BAD_ELM_BASE_SEN 54 +#define FLM_BAD_ELM_IX_REF 55 +#define FLM_BAD_ELM_ONE_RUN_SEN 56 +#define FLM_BAD_ELM_DELTA_SEN 57 +#define FLM_BAD_ELM_DOMAIN 58 +/* +** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE +** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP +*/ +#define FLM_BAD_LAST_BLK_NEXT 59 +#define FLM_BAD_FIELD_PTR 60 +#define FLM_REBUILD_REC_EXISTS 61 +#define FLM_REBUILD_KEY_NOT_UNIQUE 62 +#define FLM_NON_UNIQUE_ELM_KEY_REF 63 +#define FLM_OLD_VIEW 64 +#define FLM_COULD_NOT_SYNC_BLK 65 +#define FLM_IX_REF_REC_NOT_FOUND 66 +#define FLM_IX_KEY_NOT_FOUND_IN_REC 67 +#define FLM_KEY_NOT_IN_KEY_REFSET 68 +#define FLM_BAD_BLK_CHECKSUM 69 +#define FLM_BAD_LAST_DRN 70 +#define FLM_BAD_FILE_SIZE 71 +#define FLM_BAD_FIRST_LAST_ELM_FLAG 72 +#define FLM_BAD_DATE_FIELD 73 +#define FLM_BAD_TIME_FIELD 74 +#define FLM_BAD_TMSTAMP_FIELD 75 +#define FLM_BAD_DATE_KEY 76 +#define FLM_BAD_TIME_KEY 77 +#define FLM_BAD_TMSTAMP_KEY 78 +#define FLM_BAD_BLOB_FIELD 79 + +/* +** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE +** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP +*/ + +#define FLM_BAD_PCODE_IXD_TBL 80 +#define FLM_NODE_QUARANTINED 81 +#define FLM_BAD_BLK_TYPE 82 +#define FLM_BAD_ELEMENT_CHAIN 83 +#define FLM_BAD_ELM_EXTRA_DATA 84 +#define FLM_BAD_BLOCK_STRUCTURE 85 +#define FLM_BAD_ROOT_PARENT 86 +#define FLM_BAD_ROOT_LINK 87 +#define FLM_BAD_PARENT_LINK 88 +#define FLM_BAD_INVALID_ROOT 89 +#define FLM_BAD_FIRST_CHILD_LINK 90 +#define FLM_BAD_LAST_CHILD_LINK 91 +#define FLM_BAD_PREV_SIBLING_LINK 92 +#define FLM_BAD_NEXT_SIBLING_LINK 93 +#define FLM_BAD_ANNOTATION_LINK 94 +#define FLM_UNSUPPORTED_NODE_TYPE 95 +#define FLM_BAD_INVALID_NAME_ID 96 +#define FLM_BAD_INVALID_PREFIX_ID 97 +#define FLM_BAD_DATA_BLOCK_COUNT 98 +#define FLM_BAD_AVAIL_SIZE 99 +#define FLM_BAD_NODE_TYPE 100 +#define FLM_BAD_CHILD_ELM_COUNT 101 +#define FLM_NUM_CORRUPT_ERRORS 101 + +/* +** WARNING: ANY CHANGES MADE TO THE CHECK ERROR CODE DEFINES MUST BE +** REFLECTED IN THE FlmCorruptStrings TABLE FOUND IN FLERRSTR.CPP +*/ + +class F_DbInfo : public IF_DbInfo, public XF_Base +{ +public: + + F_DbInfo() + { + m_uiLogicalCorruptions = 0; + m_uiLogicalRepairs = 0; + m_ui64FileSize = 0; + m_uiNumIndexes = 0; + m_uiNumCollections = 0; + m_uiNumLogicalFiles = 0; + m_pLogicalFiles = NULL; + f_memset( &m_dbHdr, 0, sizeof( m_dbHdr)); + f_memset( &m_AvailBlocks, 0, sizeof( m_AvailBlocks)); + f_memset( &m_LFHBlocks, 0, sizeof( m_LFHBlocks)); + } + + virtual ~F_DbInfo() + { + freeLogicalFiles(); + } + + FINLINE void freeLogicalFiles( void) + { + FLMUINT uiLoop; + + if (m_pLogicalFiles) + { + for (uiLoop = 0; uiLoop < m_uiNumLogicalFiles; uiLoop++) + { + if (m_pLogicalFiles [uiLoop].pLevelInfo) + { + f_free( &m_pLogicalFiles [uiLoop].pLevelInfo); + } + } + f_free( &m_pLogicalFiles); + } + m_uiNumLogicalFiles = 0; + m_uiNumIndexes = 0; + m_uiNumCollections = 0; + } + + FINLINE FLMUINT XFLMAPI getNumCollections( void) + { + return( m_uiNumCollections); + } + + FINLINE FLMUINT XFLMAPI getNumIndexes( void) + { + return( m_uiNumIndexes); + } + + FINLINE FLMUINT XFLMAPI getNumLogicalFiles( void) + { + return( m_uiNumLogicalFiles); + } + + FINLINE FLMUINT64 XFLMAPI getFileSize( void) + { + return( m_ui64FileSize); + } + + FINLINE XFLM_DB_HDR * XFLMAPI getDbHdr( void) + { + return( &m_dbHdr); + } + + FINLINE void XFLMAPI getAvailBlockStats( + FLMUINT64 * pui64BytesUsed, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors) + { + *pui64BytesUsed = m_LFHBlocks.ui64BytesUsed; + *puiBlockCount = m_AvailBlocks.uiBlockCount; + *piLastError = m_AvailBlocks.iErrCode; + *puiNumErrors = m_AvailBlocks.uiNumErrors; + } + + FINLINE void XFLMAPI getLFHBlockStats( + FLMUINT64 * pui64BytesUsed, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors) + { + *pui64BytesUsed = m_LFHBlocks.ui64BytesUsed; + *puiBlockCount = m_LFHBlocks.uiBlockCount; + *piLastError = m_LFHBlocks.iErrCode; + *puiNumErrors = m_LFHBlocks.uiNumErrors; + } + + void XFLMAPI getBTreeInfo( + FLMUINT uiNthLogicalFile, + FLMUINT * puiLfNum, + eLFileType * peLfType, + FLMUINT * puiRootBlkAddress, + FLMUINT * puiNumLevels); + + void XFLMAPI getBTreeBlockStats( + FLMUINT uiNthLogicalFile, + FLMUINT uiLevel, + FLMUINT64 * pui64KeyCount, + FLMUINT64 * pui64BytesUsed, + FLMUINT64 * pui64ElementCount, + FLMUINT64 * pui64ContElementCount, + FLMUINT64 * pui64ContElmBytes, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors); + +private: + + FLMUINT m_uiLogicalCorruptions; + FLMUINT m_uiLogicalRepairs; + FLMUINT64 m_ui64FileSize; + FLMUINT m_uiNumIndexes; + FLMUINT m_uiNumCollections; + FLMUINT m_uiNumLogicalFiles; + LF_HDR * m_pLogicalFiles; + XFLM_DB_HDR m_dbHdr; + BLOCK_INFO m_AvailBlocks; + BLOCK_INFO m_LFHBlocks; +friend class F_DbCheck; +}; + +typedef struct +{ + FLMUINT uiIndexNum; + FLMUINT uiCollection; + FLMUINT64 ui64DocId; +} DOC_IXD_XREF; + +// Verifier Flags + +#define CHK_NEXT_SIBLING_VERIFIED 0x0001 +#define CHK_PREV_SIBLING_VERIFIED 0x0002 +#define CHK_FIRST_CHILD_VERIFIED 0x0004 +#define CHK_LAST_CHILD_VERIFIED 0x0008 +#define CHK_PARENT_VERIFIED 0x0010 +#define CHK_ANNOTATION_VERIFIED 0x0020 +#define CHK_ROOT_VERIFIED 0x0040 + +// Bitmap field order + +#define CHK_BM_ROOT_ID 0x0001 +#define CHK_BM_PARENT_ID 0x0002 +#define CHK_BM_PREV_SIBLING 0x0004 +#define CHK_BM_NEXT_SIBLING 0x0008 +#define CHK_BM_FIRST_CHILD 0x0010 +#define CHK_BM_LAST_CHILD 0x0020 +#define CHK_BM_ANNOTATION 0x0040 +#define CHK_MAX_BM_ENTRIES 8 + +typedef struct +{ + FLMUINT16 ui16Flags; // Verify Flags to record visits/verifies + FLMUINT16 ui16BitMap; // Holds a map of present fields + FLMUINT64 ui64NodeId; +} NODE_RS_HDR; + +typedef struct +{ + NODE_RS_HDR hdr; + FLMUINT64 ui64FieldArray[ CHK_MAX_BM_ENTRIES]; +} NODE_RS_ENTRY; + +#define REBUILD_BLK_SIZE (1024 * 50) +#define REBUILD_RSET_ENTRY_SIZE 21 + +/*============================================================================= +Desc: Class to rebuild a broken database. This class is used by + F_DbSystem::dbRebuild() +=============================================================================*/ +class F_DbRebuild : public XF_RefCount, public XF_Base +{ +public: + + // Constructor and Destructor + + F_DbRebuild( void) + { + m_pDb = NULL; + m_pSFileHdl = NULL; + } + + ~F_DbRebuild() + { + } + + RCODE dbRebuild( + const char * pszSourceDbPath, + const char * pszSourceDataDir, + const char * pszDestDbPath, + const char * pszDestDataDir, + const char * pszDestRflDir, + const char * pDictPath, + const char * pszPassword, + XFLM_CREATE_OPTS * pCreateOpts, + FLMUINT64 * pui64TotNodes, + FLMUINT64 * pui64NodesRecov, + FLMUINT64 * pui64QuarantinedNodes, + IF_DbRebuildStatus * pRebuildStatus); + + FINLINE FLMUINT getBlockSize( void) + { + return( m_dbHdr.ui16BlockSize); + } + + FINLINE RCODE reportStatus( + FLMBOOL bForce = FALSE) + { + RCODE rc = NE_XFLM_OK; + + if( m_pRebuildStatus) + { + FLMUINT uiCurrentTime = FLM_GET_TIMER(); + FLMUINT uiElapTime = FLM_ELAPSED_TIME( uiCurrentTime, m_uiLastStatusTime); + + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiElapTime); + + if( bForce || uiElapTime >= 1) + { + m_uiLastStatusTime = uiCurrentTime; + m_callbackData.bStartFlag = FALSE; + if( RC_BAD( rc = m_pRebuildStatus->reportRebuild( &m_callbackData))) + { + m_cbrc = rc; + goto Exit; + } + } + } + + Exit: + + return( rc); + } + +private: + + RCODE rebuildDatabase( void); + + RCODE recoverNodes( + FLMBOOL bRecoverDictionary); + + FINLINE FLMBYTE getRSetPrefix( + FLMUINT uiNameId) + { + if( uiNameId == ELM_PREFIX_TAG) + { + return( 1); + } + + if( uiNameId == ELM_ATTRIBUTE_TAG) + { + return( 2); + } + + if( uiNameId == ELM_ELEMENT_TAG) + { + return( 3); + } + + if( uiNameId == ELM_COLLECTION_TAG) + { + return( 4); + } + + return( 5); + } + + FINLINE void buildRSetEntry( + FLMBYTE ucPrefix, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiBlockAddr, + FLMUINT uiElmNumber, + FLMBYTE * pucBuffer) + { + pucBuffer[ 0] = ucPrefix; + longToByte( (FLMUINT32)uiCollection, &pucBuffer[ 1]); + long64ToByte( ui64NodeId, &pucBuffer[ 5]); + longToByte( (FLMUINT32)uiBlockAddr, &pucBuffer[ 13]); + longToByte( (FLMUINT32)uiElmNumber, &pucBuffer[ 17]); + } + + FINLINE void extractRSetEntry( + FLMBYTE * pucBuffer, + FLMUINT * puiCollection, + FLMUINT64 * pui64NodeId, + FLMUINT * puiBlockAddr, + FLMUINT * puiElmNumber) + { + if( puiCollection) + { + *puiCollection = byteToLong( &pucBuffer[ 1]); + } + + if( pui64NodeId) + { + *pui64NodeId = byteToLong64( &pucBuffer[ 5]); + } + + if( puiBlockAddr) + { + *puiBlockAddr = byteToLong( &pucBuffer[ 13]); + } + + if( puiElmNumber) + { + *puiElmNumber = byteToLong( &pucBuffer[ 17]); + } + } + + RCODE recoverTree( + F_RebuildNodeIStream * pIStream, + FResultSet * pNonRootRSet, + F_DOMNode * pParentNode, + F_CachedNode * pRecovCachedNode, + FLMBYTE * pucNodeIV); + + FINLINE RCODE reportCorruption( + FLMINT iErrCode, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT64 ui64ErrNodeId) + { + RCODE rc; + + if( m_pRebuildStatus) + { + m_corruptInfo.iErrCode = iErrCode; + m_corruptInfo.uiErrBlkAddress = uiErrBlkAddress; + m_corruptInfo.uiErrElmOffset = uiErrElmOffset; + m_corruptInfo.ui64ErrNodeId = ui64ErrNodeId; + rc = m_pRebuildStatus->reportRebuildErr( &m_corruptInfo); + m_corruptInfo.iErrCode = 0; + return( rc); + } + + return( NE_XFLM_OK); + } + + RCODE determineBlkSize( + FLMUINT * puiBlkSizeRV); + + F_Db * m_pDb; + F_SuperFileHdl * m_pSFileHdl; + IF_DbRebuildStatus * m_pRebuildStatus; + FLMBOOL m_bBadHeader; + FLMUINT m_uiLastStatusTime; + XFLM_DB_HDR m_dbHdr; + XFLM_CREATE_OPTS m_createOpts; + XFLM_REBUILD_INFO m_callbackData; + XFLM_CORRUPT_INFO m_corruptInfo; + RCODE m_cbrc; + +friend class F_RebuildNodeIStream; +}; + +RCODE chkBlkRead( + F_DbInfo * pDbInfo, + FLMUINT uiBlkAddress, + F_BLK_HDR ** ppBlkHdr, + F_CachedBlock ** ppSCache, + FLMINT * piBlkErrCodeRV); + +FLMINT flmCompareKeys( + FLMBYTE * pBuf1, + FLMUINT uiBuf1Len, + FLMBYTE * pBuf2, + FLMUINT uiBuf2Len); + +void flmInitReadState( + STATE_INFO * pStateInfo, + FLMBOOL * pbStateInitialized, + FLMUINT uiVersionNum, + F_Db * pDb, + LF_HDR * pLogicalFile, + FLMUINT uiLevel, + FLMUINT uiBlkType, + FLMBYTE * pucKeyBuffer); + +FLMINT flmVerifyBlockHeader( + STATE_INFO * pStateInfo, + BLOCK_INFO * pBlockInfoRV, + FLMUINT uiBlockSize, + FLMUINT uiExpNextBlkAddr, + FLMUINT uiExpPrevBlkAddr, + FLMBOOL bCheckEOF); + +RCODE flmVerifyElement( + STATE_INFO * pStateInfo, + LFILE * pLFile, + IXD * pIxd, + FLMINT * piErrCode); + +void getEntryInfo( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT uiOffset, + FLMBYTE ** ppucElm, + FLMUINT * puiElmLen, + FLMUINT * puiElmKeyLen, + FLMUINT * puiElmDataLen, + FLMBYTE ** ppucElmKey, + FLMBYTE ** ppucElmData); + +FINLINE RCODE F_NodeCacheMgr::allocDOMNode( + F_DOMNode ** ppDOMNode) +{ + flmAssert( *ppDOMNode == NULL); + + if( m_pFirstNode) + { + f_resetStackInfo( m_pFirstNode); + *ppDOMNode = m_pFirstNode; + m_pFirstNode = m_pFirstNode->m_pNextInPool; + (*ppDOMNode)->m_pNextInPool = NULL; + } + else + { + if( (*ppDOMNode = f_new F_DOMNode) == NULL) + { + return( RC_SET( NE_XFLM_MEM)); + } + } + + return( NE_XFLM_OK); +} + +FINLINE RCODE F_NodeCacheMgr::makeWriteCopy( + F_Db * pDb, + F_CachedNode ** ppCachedNode) +{ + F_CachedNode * pCachedNode = *ppCachedNode; + + if( pCachedNode->getLowTransId() < pDb->m_ui64CurrTransID) + { + return( gv_XFlmSysData.pNodeCacheMgr->_makeWriteCopy( + pDb, ppCachedNode)); + } + else if( pCachedNode->getStreamUseCount()) + { + // The only thread that would be using this version of + // the node is the updater thread. It would be illegal + // for the updater thread to have an input stream going + // while trying to update this. + + return( RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP)); + } + + return( NE_XFLM_OK); +} + +#endif // FLAIMSYS_H diff --git a/version5/src/flalloc.cpp b/version5/src/flalloc.cpp new file mode 100644 index 0000000..3815edc --- /dev/null +++ b/version5/src/flalloc.cpp @@ -0,0 +1,1798 @@ +//------------------------------------------------------------------------------ +// 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: flalloc.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#ifdef FLM_WIN + + // This pragma is needed because FLAIM may be built with a + // packing other than 8-bytes for Windows (such as 1-byte packing). + // Code in FLAIM that uses windows structures and system calls + // MUST use 8-byte packing (the packing used by the O/S). + // See Microsoft technical article Q117388. + + #pragma pack( push, enter_windows, 8) + #include + #pragma pack( pop, enter_windows) +#endif + +#ifdef FLM_UNIX + #ifdef HAVE_DLADDR + #include + #endif +#endif + +#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 + +#ifdef USE_ALT_MEM_MANAGER + + // If USE_ALT_MEM_MANAGER is defined, memory allocations will be based on + // a user-supplied module that implements f_alt_malloc, f_alt_free, and + // f_alt_realloc. + + // Prototypes + + extern "C" + { + void * f_alt_malloc( unsigned size); + void f_alt_free( void * memblock); + void * f_alt_realloc( void * memblock, unsigned size); + } + +#endif + +#define MEM_PTR_INIT_ARRAY_SIZE 512 +#define MEM_MAX_STACK_WALK_DEPTH 32 + +// If stack tracking is on, leak checking also +// needs to be on. + +#ifndef FLM_DEBUG + #ifdef DEBUG_SIM_OUT_OF_MEM + #undef DEBUG_SIM_OUT_OF_MEM + #endif +#endif + +#ifdef FLM_DEBUG + +// Platform specific stuff needed for tracking leaks + +#ifdef FLM_NLM + extern "C" + { + void GetClosestSymbol( + BYTE * szBuffer, + LONG udAddress); + } +#endif + +// Local function prototypes + +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); + +#ifdef DEBUG_SIM_OUT_OF_MEM + +//one of every OUT_OF_MEM_FREQUENCY calls will fail + +#define OUT_OF_MEM_FREQUENCY 40000 + +//OUT_OF_MEM_SEQUENCE_LENGTH calls in a row will fail + +#define OUT_OF_MEM_SEQUENCE_LENGTH 10 + +FLMBOOL SimulateOutOfMemory() +{ + if ( + //is the flag turned on + (gv_XFlmSysData.uiOutOfMemSimEnabledFlag == + (FLMUINT)OUT_OF_MEM_SIM_ENABLED_FLAG) && + + //continuing a sequence of failures + ((gv_XFlmSysData.uiSimOutOfMemFailSequence > 0) || + + //failing randomly for the first time, and starting a new sequence + (f_randomChoice( &gv_XFlmSysData.memSimRandomGen, 0, OUT_OF_MEM_FREQUENCY) == 0) + )) + { + gv_XFlmSysData.uiSimOutOfMemFailTotal++; + gv_XFlmSysData.uiSimOutOfMemFailSequence++; + //if reached the end of failure sequence, reset back to 0 so the + //sequence will cease + if ( gv_XFlmSysData.uiSimOutOfMemFailSequence >= OUT_OF_MEM_SEQUENCE_LENGTH) + { + gv_XFlmSysData.uiSimOutOfMemFailSequence = 0; + } + return TRUE; + } + else + { + return FALSE; + } +} + +#endif //#ifdef DEBUG_SIM_OUT_OF_MEM + +#if defined( FLM_NLM) + +/************************************************************************ +Desc: Returns the current value of EBP--the value of the caller's stack + frame pointer. +*************************************************************************/ +void * memGetEBP(void); + +#ifdef FLM_MWERKS_NLM + + void * memGetEBP(void) + { + __asm + { + mov eax,[ebp] + } + } + +#else + + #pragma aux memGetEBP = "mov eax,ebp"; + +#endif + +/************************************************************************ +Desc: Returns the value at SS:[POS+OFFSET]. +*************************************************************************/ +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 + +/************************************************************************ +Desc: +*************************************************************************/ +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); // skip addrs we already know + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *)uiEbp, 4); // Caller's return addr + + 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); // Caller's frame ptr + + if (!uiEbp || uiEbp <= uiOldEbp || uiEbp > uiOldEbp + 5000) + { + break; + } + + uiRtnAddr = (FLMUINT) memValueAtStackOffset( (void *) uiEbp, 4); // Caller's return addr + } + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} + +#elif defined( FLM_WIN) + +/******************************************************************** +Desc: Walk the call stack. +*********************************************************************/ +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_XFlmSysData.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_XFlmSysData.hMemTrackingMutex); + + uiAddresses [uiLoop] = 0; + if ((puiAddresses = (FLMUINT *)os_malloc( + sizeof( FLMUINT) * (uiLoop+1))) != NULL) + { + f_memcpy( puiAddresses, &uiAddresses [0], sizeof( FLMUINT) * (uiLoop + 1)); + } + return( puiAddresses); +} +#else +FLMUINT * memWalkStack() +{ + return( NULL); +} +#endif + +/******************************************************************** +Desc: Initialize memory tracking +*********************************************************************/ +FSTATIC FLMBOOL initMemTracking( void) +{ + RCODE rc; + F_MUTEX memMutex; + + if (!gv_XFlmSysData.bMemTrackingInitialized && !gv_XFlmSysData.uiInitThreadId) + { + gv_XFlmSysData.uiInitThreadId = f_threadId(); + rc = f_mutexCreate( &memMutex); + f_sleep( 50); + + // Only set to initialized if we were the last thread + // to set gv_XFlmSysData.uiInitThreadId + + if (f_threadId() == gv_XFlmSysData.uiInitThreadId) + { + if (RC_OK( rc)) + { + gv_XFlmSysData.hMemTrackingMutex = memMutex; + } + else + { + gv_XFlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + } +#ifdef FLM_WIN + SymSetOptions( SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + gv_XFlmSysData.hMemProcess = GetCurrentProcess(); + SymInitialize( gv_XFlmSysData.hMemProcess, NULL, TRUE); +#endif + gv_XFlmSysData.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_XFlmSysData.bMemTrackingInitialized) + { + f_sleep( 10); + } + return( (gv_XFlmSysData.hMemTrackingMutex != F_MUTEX_NULL) ? TRUE : FALSE); +} + +/******************************************************************** +Desc: Save memory tracking information - called on alloc or realloc. +*********************************************************************/ +FSTATIC void saveMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + FLMUINT uiNewCnt; + FLMUINT uiId; + void ** pNew; + + if (gv_XFlmSysData.bTrackLeaks && initMemTracking()) + { + f_mutexLock( gv_XFlmSysData.hMemTrackingMutex); + + // See if there is enough room in the array + + if (gv_XFlmSysData.uiMemNumPtrs == gv_XFlmSysData.uiMemTrackingPtrArraySize) + { + + // If array is not initialized, use initial count. Otherwise + // double the size. + + uiNewCnt = (FLMUINT)((!gv_XFlmSysData.uiMemTrackingPtrArraySize) + ? MEM_PTR_INIT_ARRAY_SIZE + : gv_XFlmSysData.uiMemTrackingPtrArraySize * 2); + if ((pNew = (void **)os_malloc( sizeof( void *) * uiNewCnt)) != NULL) + { + + // Copy the pointers from the old array, if any, + // into the newly allocated array. + + if (gv_XFlmSysData.uiMemTrackingPtrArraySize) + { + f_memcpy( pNew, gv_XFlmSysData.ppvMemTrackingPtrs, + sizeof( void *) * gv_XFlmSysData.uiMemTrackingPtrArraySize); + os_free( gv_XFlmSysData.ppvMemTrackingPtrs); + gv_XFlmSysData.ppvMemTrackingPtrs = NULL; + } + f_memset( &pNew [gv_XFlmSysData.uiMemTrackingPtrArraySize], 0, + sizeof( void *) * (uiNewCnt - gv_XFlmSysData.uiMemTrackingPtrArraySize)); + gv_XFlmSysData.ppvMemTrackingPtrs = pNew; + gv_XFlmSysData.uiMemTrackingPtrArraySize = uiNewCnt; + } + } + + // If we are still full, we were not able to reallocate memory, so we + // do nothing. + + if (gv_XFlmSysData.uiMemNumPtrs == gv_XFlmSysData.uiMemTrackingPtrArraySize) + { + pHdr->uiAllocationId = 0; + } + else + { + // Find an empty slot - there has to be one! + + uiId = gv_XFlmSysData.uiMemNextPtrSlotToUse; + while (gv_XFlmSysData.ppvMemTrackingPtrs [uiId]) + { + if (++uiId == gv_XFlmSysData.uiMemTrackingPtrArraySize) + { + uiId = 0; + } + } + + // Allocation ID in the header is offset by one to avoid + // using a value of zero. + + pHdr->uiAllocationId = uiId + 1; + gv_XFlmSysData.ppvMemTrackingPtrs [uiId] = pHdr; + gv_XFlmSysData.uiMemNumPtrs++; + if ((gv_XFlmSysData.uiMemNextPtrSlotToUse = uiId + 1) == + gv_XFlmSysData.uiMemTrackingPtrArraySize) + { + gv_XFlmSysData.uiMemNextPtrSlotToUse = 0; + } + } + pHdr->uiAllocCnt = ++gv_XFlmSysData.uiAllocCnt; + f_mutexUnlock( gv_XFlmSysData.hMemTrackingMutex); + } + else + { + pHdr->uiAllocationId = 0; + pHdr->uiAllocCnt = 0; + } + + // Follow the stack. + + if (gv_XFlmSysData.bTrackLeaks && gv_XFlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } + else + { + pHdr->puiStack = NULL; + } +} + +/******************************************************************** +Desc: Update memory tracking information - called after realloc +*********************************************************************/ +FSTATIC void updateMemTrackingInfo( + F_MEM_HDR * pHdr) +{ + if (pHdr->puiStack) + { + os_free( pHdr->puiStack); + pHdr->puiStack = NULL; + } + if (gv_XFlmSysData.bTrackLeaks && gv_XFlmSysData.bStackWalk) + { + pHdr->puiStack = memWalkStack(); + } +} + +/******************************************************************** +Desc: Free memory tracking information - called on free. +*********************************************************************/ +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_XFlmSysData.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_XFlmSysData.ppvMemTrackingPtrs [uiId - 1] = NULL; + flmAssert( gv_XFlmSysData.uiMemNumPtrs); + gv_XFlmSysData.uiMemNumPtrs--; + + if ( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hMemTrackingMutex); + } + } + + // Free the stack information, if any. + + if (puiStack) + { + os_free( puiStack); + } +} + +/******************************************************************** +Desc: Log memory leaks. +*********************************************************************/ +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_XFlmSysData.bTrackLeaks; + + gv_XFlmSysData.bTrackLeaks = FALSE; + + // Need a big buffer to show an entire stack. + + uiMsgBufSize = 20480; + if ((pszMessageBuffer = (char *)os_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 *)os_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_XFlmSysData.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) + { + os_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_XFlmSysData.bLogLeaks = TRUE; + } + else if (iRet == IDABORT) + { + flmAssert( 0); + } +#else + gv_XFlmSysData.bLogLeaks = TRUE; +#endif + + if (gv_XFlmSysData.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.Open( pszErrPath, XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE, + &pFileHdl))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = FileSystem.Create( pszErrPath, XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE, + &pFileHdl); + } + } + else + { + // Position to append to file. + + rc = pFileHdl->Seek( 0, XFLM_IO_SEEK_END, NULL); + } + + // If we successfully opened the file, write to it. + + if (RC_OK( rc)) + { + if (RC_OK( pFileHdl->Write( XFLM_IO_CURRENT_POS, + (FLMUINT)(pszTmp - pszMessageBuffer), + pszMessageBuffer, &uiDummy))) + { + (void)pFileHdl->Flush(); + } + pFileHdl->Close(); + } + } +//Exit: + if (pFileHdl) + { + pFileHdl->Release(); + } + + if( bClearFileSys) + { + gv_pFileSystem = NULL; + } + if (pszMessageBuffer != &szTmpBuffer [0]) + { + os_free( pszMessageBuffer); + } + + gv_XFlmSysData.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_XFlmSysData.hMemTrackingMutex); + for (uiId = 0; uiId < gv_XFlmSysData.uiMemTrackingPtrArraySize; uiId++) + { + if ((pHdr = (F_MEM_HDR *)gv_XFlmSysData.ppvMemTrackingPtrs [uiId]) != NULL) + { + logMemLeak( pHdr); + freeMemTrackingInfo( TRUE, uiId + 1, pHdr->puiStack); + } + } + + // Free the memory pointer array. + + os_free( gv_XFlmSysData.ppvMemTrackingPtrs); + gv_XFlmSysData.ppvMemTrackingPtrs = NULL; + gv_XFlmSysData.uiMemTrackingPtrArraySize = 0; + gv_XFlmSysData.uiMemNumPtrs = 0; + + f_mutexUnlock( gv_XFlmSysData.hMemTrackingMutex); + + // Free up the mutex. + + f_mutexDestroy( &gv_XFlmSysData.hMemTrackingMutex); + + // Reset to unitialized state. + + gv_XFlmSysData.uiInitThreadId = 0; + gv_XFlmSysData.hMemTrackingMutex = F_MUTEX_NULL; + gv_XFlmSysData.bMemTrackingInitialized = FALSE; +#ifdef FLM_WIN + SymCleanup( gv_XFlmSysData.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_XFLM_OK; + F_MEM_HDR * pHdr; + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_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_XFLM_OK; + F_MEM_HDR * pHdr; + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } +#endif + + if ((pHdr = (F_MEM_HDR *)os_malloc( uiSize + + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_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_XFLM_OK; + F_MEM_HDR * pNewHdr; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } +#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_XFLM_MEM); + goto Exit; + } + + #endif + + // Cannot realloc memory that was allocated via a new operator + + if (pOldHdr->bAllocFromNewOp) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_MEM); + goto Exit; + } + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; +#endif + + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_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_XFLM_OK; + F_MEM_HDR * pNewHdr; + FLMUINT uiOldSize; +#ifdef FLM_DEBUG + F_MEM_HDR * pOldHdr; + FLMUINT uiOldAllocationId; + FLMUINT * puiOldStack; +#endif + +#ifdef DEBUG_SIM_OUT_OF_MEM + if ( SimulateOutOfMemory()) + { + *ppvPtr = NULL; + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } +#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_XFLM_MEM); + goto Exit; + } + + #endif + + // Cannot realloc memory that was allocated via a new operator + + if (pOldHdr->bAllocFromNewOp) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_MEM); + goto Exit; + } + + uiOldAllocationId = pOldHdr->uiAllocationId; + puiOldStack = pOldHdr->puiStack; + +#endif + + uiOldSize = F_GET_MEM_DATA_SIZE( *ppvPtr); + if ((pNewHdr = (F_MEM_HDR *)os_realloc( F_GET_ALLOC_PTR( *ppvPtr), + uiSize + sizeof( F_MEM_HDR) + + F_PICKET_FENCE_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_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_XFLM_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_XFLM_MEM); + } + + #endif + + freeMemTrackingInfo( FALSE, pHdr->uiAllocationId, pHdr->puiStack); +#endif + os_free( F_GET_ALLOC_PTR( *ppvPtr)); + *ppvPtr = NULL; + } +} + +#ifdef FLM_DEBUG +/******************************************************************** +Desc: Reset the stack information for an allocation. +*********************************************************************/ +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_XFlmSysData.hMemTrackingMutex); + pHdr->uiAllocCnt = ++gv_XFlmSysData.uiAllocCnt; + f_mutexUnlock( gv_XFlmSysData.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 F_Pool::poolAlloc( + FLMUINT uiSize, + void ** ppvPtr + ) +{ + RCODE rc = NE_XFLM_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; // Point to free space + pBlock->uiFreeOffset += uiSize; // Modify free offset + pBlock->uiFreeSize -= uiSize; // Modify free size + + m_uiBytesAllocated += uiSize; + *ppvPtr = (void *)pucFreePtr; + } + else + { + *ppvPtr = NULL; + } + return( rc); +} + +/**************************************************************************** +Desc: Allocates a block of memory from a memory pool. +****************************************************************************/ +RCODE 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 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 F_Pool::poolReset( + void * pvMark, + // [IN] If pvMark is NULL, the first pool block is emptied and all + // other blocks in the list are released to the operating system. + // If pvMark is non-NULL, the pool list is searched for the block + // containing pvMark. All pool blocks following pvMark are + // released and the block containing pvMark is is reset to + // the byte referenced by 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; + +#ifdef FLM_MEM_CHK + { + /* memset the reset memory so someone pointing to it will get a error. */ + FLMBYTE * pucPtr = (FLMBYTE *) pBlock; + pucPtr += pBlock->uiFreeOffset; + f_memset( pucPtr, 'r', pBlock->uiFreeSize); // Set memory to 'r' for Reset + } +#endif + } + + // 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) // free until pvMark found +{ + 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; + +#if defined( FLM_MEM_CHK) || defined( MEM_TEST) + { + // memset the memory so someone pointing to it will get a error. + FLMBYTE * pucPtr = (FLMBYTE *)pBlock; + pucPtr += pBlock->uiFreeOffset; + f_memset( pucPtr, 'r', pBlock->uiFreeSize); // Set memory to 'r' for Reset + } +#endif + + // 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; + } +} + +#undef new +#undef delete +/**************************************************************************** +Desc: +****************************************************************************/ +void * XF_Base::operator new( + FLMSIZET uiSize) +{ + void * pvReturnPtr = NULL; + + // NOTICE: You should be using f_new so that we can track memory leaks + + RC_UNEXPECTED_ASSERT( NE_XFLM_MEM); + +#ifdef FLM_DEBUG + f_allocImp( uiSize, &pvReturnPtr, TRUE, "unknown", 0); +#else + f_allocImp( uiSize, &pvReturnPtr); +#endif + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * XF_Base::operator new[]( + FLMSIZET uiSize) +{ + void * pvReturnPtr = NULL; + + // NOTICE: You should be using f_new[] so that we can track memory leaks + +#ifdef FLM_DEBUG + f_allocImp( uiSize, &pvReturnPtr, TRUE, "unknown", 0); +#else + f_allocImp( uiSize, &pvReturnPtr); +#endif + + return( pvReturnPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * XF_Base::operator new( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +{ + void * pvReturnPtr = NULL; + + f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine); + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * XF_Base::operator new[]( + FLMSIZET uiSize, + const char * pszFile, + int iLine) +{ + void * pvReturnPtr = NULL; + + f_allocImp( uiSize, &pvReturnPtr, TRUE, pszFile, iLine); + + return( pvReturnPtr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void XF_Base::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + f_freeImp( &ptr, TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void XF_Base::operator delete[]( + void * ptr) +{ + if( !ptr) + { + return; + } + f_freeImp( &ptr, TRUE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) +void XF_Base::operator delete( + void * ptr, + char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + f_freeImp( &ptr, TRUE); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( FLM_WATCOM_NLM) && !defined( FLM_SOLARIS) +void XF_Base::operator delete[]( + void * ptr, + char *, // file + int) // line +{ + if( !ptr) + { + return; + } + + f_freeImp( &ptr, TRUE); +} +#endif + +/************************************************************************ +Desc: +*************************************************************************/ +void * F_OSBase::operator new( + FLMSIZET uiSize) +{ + return( os_malloc( uiSize)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_OSBase::operator new( + FLMSIZET uiSize, + const char *, // pszFile, + int) // iLine) +{ + return( os_malloc( uiSize)); +} +#endif + +/************************************************************************ +Desc: +*************************************************************************/ +void F_OSBase::operator delete( + void * ptr) +{ + os_free( ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_OSBase::operator delete[]( + void * ptr) +{ + os_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 +{ + os_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 +{ + os_free( &ptr); +} +#endif diff --git a/version5/src/flbackup.cpp b/version5/src/flbackup.cpp new file mode 100644 index 0000000..3ef9a84 --- /dev/null +++ b/version5/src/flbackup.cpp @@ -0,0 +1,2586 @@ +//------------------------------------------------------------------------------ +// Desc: Backup and restore Routines +// +// 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: flbackup.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +// Typedefs + +typedef struct +{ + FLMUINT64 ui64BytesToDo; + FLMUINT64 ui64BytesDone; +} DB_BACKUP_INFO, * DB_BACKUP_INFO_p; + +// Local classes + +class F_BackerStream : public XF_RefCount, public XF_Base +{ +public: + + F_BackerStream( void); + ~F_BackerStream( void); + + RCODE setup( + FLMUINT uiMTUSize, + IF_RestoreClient * pRestoreObj); + + RCODE setup( + FLMUINT uiMTUSize, + IF_BackupClient * pClient); + + RCODE startThreads( void); + + void shutdownThreads( void); + + RCODE read( + FLMUINT uiLength, + FLMBYTE * pucData, + FLMUINT * puiBytesRead = NULL); + + RCODE write( + FLMUINT uiLength, + FLMBYTE * pucData, + FLMUINT * puiBytesWritten = NULL); + + RCODE flush( void); + + FINLINE FLMUINT64 getByteCount( void) + { + // Returns the total number of bytes read or written. + + return( m_ui64ByteCount); + } + + FINLINE FLMUINT getMTUSize( void) + { + return( m_uiMTUSize); + } + +private: + + // Methods + + RCODE signalThread( void); + + RCODE _setup( void); + + static RCODE readThread( + F_Thread * pThread); + + static RCODE writeThread( + F_Thread * pThread); + + // Data + + FLMBOOL m_bSetup; + FLMBOOL m_bFirstRead; + FLMBOOL m_bFinalRead; + FLMUINT m_uiBufOffset; + FLMUINT64 m_ui64ByteCount; + IF_RestoreClient * m_pRestoreObj; + F_SEM m_hDataSem; + F_SEM m_hIdleSem; + F_Thread * m_pThread; + RCODE m_rc; + FLMBYTE * m_pucInBuf; + FLMUINT * m_puiInOffset; + FLMBYTE * m_pucOutBuf; + FLMUINT * m_puiOutOffset; + FLMBYTE * m_pucBufs[ 2]; + FLMUINT m_uiOffsets[ 2]; + FLMUINT m_uiMTUSize; + IF_BackupClient * m_pClient; +}; + +// Constants + +#define FLM_BACKER_SIGNATURE_OFFSET 0 +#define FLM_BACKER_SIGNATURE ((FLMBYTE *) "!DB_BACKUP_FILE!") +#define FLM_BACKER_SIGNATURE_SIZE 16 +#define FLM_BACKER_VERSION_OFFSET 16 +#define FLM_BACKER_VERSION_5_0_0 500 +#define FLM_BACKER_VERSION FLM_BACKER_VERSION_5_0_0 +#define FLM_BACKER_DB_BLOCK_SIZE_OFFSET 20 +#define FLM_BACKER_BFMAX_OFFSET 24 +#define FLM_BACKER_MTU_OFFSET 28 +#define FLM_BACKER_TIME_OFFSET 32 +#define FLM_BACKER_DB_NAME_OFFSET 36 +#define FLM_BACKER_BACKUP_TYPE_OFFSET 40 +#define FLM_BACKER_NEXT_INC_SERIAL_NUM 44 +#define FLM_BACKER_DB_VERSION 60 + +// The backer MTU size must be a multiple of the largest +// supported block size. Additionally, it must be at least +// 2 * FLM_BACKER_MAX_DB_BLOCK_SIZE. DO NOT CHANGE THE MTU +// SIZE UNLESS YOU ALSO BUMP THE BACKUP VERSION. + +#define FLM_BACKER_MTU_SIZE ((FLMUINT) 1024 * 512) +#define FLM_BACKER_MAX_FILE_SIZE ((FLMUINT) 1024 * 1024 * 1024 * 2) // 2 Gigabytes +#define FLM_BACKER_MIN_DB_BLOCK_SIZE ((FLMUINT) 2 * 1024) +#define FLM_BACKER_MAX_DB_BLOCK_SIZE ((FLMUINT) 16 * 1024) + +// Backup block header + +#define FLM_BACKER_BLK_HDR_SIZE ((FLMUINT) 4) +#define FLM_BACKER_BLK_ADDR_OFFSET 0 + +// Local prototypes + +FSTATIC RCODE flmRestoreFile( + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus, + F_SuperFileHdl * pSFile, + FLMBOOL bIncremental, + FLMUINT * puiDbVersion, + FLMUINT * puiNextIncSeqNum, + FLMBOOL * pbRflPreserved, + eRestoreAction * peAction, + FLMBOOL * pbOKToRetry); + +// Functions + +/*************************************************************************** +Desc : Prepares FLAIM to backup a database. +Notes: Only one backup of a particular database can be active at any time +*END************************************************************************/ +RCODE F_Db::backupBegin( + eDbBackupType eBackupType, + eDbTransType eTransType, + FLMUINT uiMaxLockWait, + IF_Backup ** ppBackup) +{ + F_Backup * pBackup = NULL; + FLMBOOL bBackupFlagSet = FALSE; + FLMUINT uiLastCPFileNum; + FLMUINT uiLastTransFileNum; + FLMUINT uiDbVersion; + XFLM_DB_HDR * pDbHdr; + RCODE rc = NE_XFLM_OK; + + // Initialize the handle + + *ppBackup = NULL; + + // Make sure we are not being called inside a transaction + + if( getTransType() != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Verify that the application has specified a valid transaction type. + + if( eTransType != XFLM_READ_TRANS && eTransType != XFLM_UPDATE_TRANS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Make sure a valid backup type has been specified + + uiDbVersion = getDbVersion(); + + // See if a backup is currently running against the database. If so, + // return an error. Otherwise, set the backup flag on the FFILE. + + m_pDatabase->lockMutex(); + if( m_pDatabase->m_bBackupActive) + { + m_pDatabase->unlockMutex(); + rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); + goto Exit; + } + else + { + bBackupFlagSet = TRUE; + m_pDatabase->m_bBackupActive = TRUE; + } + m_pDatabase->unlockMutex(); + + // Allocate the backup handle + + if( (pBackup = f_new F_Backup) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pBackup->m_pDb = this; + pBackup->m_uiDbVersion = uiDbVersion; + + // Start a transaction + + if( RC_BAD( rc = beginTrans( eTransType, uiMaxLockWait, + XFLM_DONT_KILL_TRANS | XFLM_DONT_POISON_CACHE, &pBackup->m_dbHdr))) + { + goto Exit; + } + + pBackup->m_bTransStarted = TRUE; + pBackup->m_eTransType = eTransType; + pDbHdr = &pBackup->m_dbHdr; + + // Don't allow an incremental backup to be performed + // if a full backup has not yet been done. + + if( eBackupType == XFLM_INCREMENTAL_BACKUP && + pDbHdr->ui64LastBackupTransID == 0) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + pBackup->m_eBackupType = eBackupType; + + // Set the next incremental backup serial number. This is + // done regardless of the backup type to prevent the wrong + // set of incremental backup files from being applied + // to a database. + + if( RC_BAD( rc = f_createSerialNumber( + pBackup->m_ucNextIncSerialNum))) + { + goto Exit; + } + + // Get the incremental sequence number from the DB header + + pBackup->m_uiIncSeqNum = (FLMUINT)pDbHdr->ui32IncBackupSeqNum; + + // Determine the transaction ID of the last backup + + pBackup->m_ui64LastBackupTransId = pDbHdr->ui64LastBackupTransID; + + // Get the block change count + + pBackup->m_uiBlkChgSinceLastBackup = + (FLMUINT)pDbHdr->ui32BlksChangedSinceBackup; + + // Get the current transaction ID + + pBackup->m_ui64TransId = pBackup->m_pDb->getTransID(); + + // Get the logical end of file + + pBackup->m_uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; + + // Get the first required RFL file needed by the restore. + + uiLastCPFileNum = (FLMUINT)pDbHdr->ui32RflLastCPFileNum; + uiLastTransFileNum = (FLMUINT)pDbHdr->ui32RflCurrFileNum; + + flmAssert( uiLastCPFileNum <= uiLastTransFileNum); + + pBackup->m_uiFirstReqRfl = uiLastCPFileNum < uiLastTransFileNum + ? uiLastCPFileNum + : uiLastTransFileNum; + + flmAssert( pBackup->m_uiFirstReqRfl); + + // Get the database block size + + pBackup->m_uiBlockSize = getBlockSize(); + + // Get the database path + + (void)getDbControlFileName( pBackup->m_szDbPath, + sizeof( pBackup->m_szDbPath)); + + // Done + + *ppBackup = pBackup; + pBackup = NULL; + +Exit: + + if( RC_BAD( rc)) + { + if( pBackup) + { + if( pBackup->m_bTransStarted) + { + abortTrans(); + } + + pBackup->Release(); + } + + if( bBackupFlagSet) + { + m_pDatabase->lockMutex(); + m_pDatabase->m_bBackupActive = FALSE; + m_pDatabase->unlockMutex(); + } + } + + return( rc); +} + +/**************************************************************************** +Desc : Constructor +****************************************************************************/ +F_Backup::F_Backup() +{ + m_pDb = NULL; + m_bTransStarted = FALSE; + reset(); +} + +/**************************************************************************** +Desc : Destructor +****************************************************************************/ +F_Backup::~F_Backup() +{ + endBackup(); +} + +/**************************************************************************** +Desc : Reset member variables to their initial state +****************************************************************************/ +void F_Backup::reset( void) +{ + if( m_bTransStarted) + { + m_pDb->abortTrans(); + m_bTransStarted = FALSE; + } + + m_pDb = NULL; + m_eTransType = XFLM_NO_TRANS; + m_ui64TransId = 0; + m_ui64LastBackupTransId = 0; + m_uiDbVersion = 0; + m_uiBlkChgSinceLastBackup = 0; + m_uiBlockSize = 0; + m_uiLogicalEOF = 0; + m_uiFirstReqRfl = 0; + m_uiIncSeqNum = 0; + m_bCompletedBackup = FALSE; + m_eBackupType = XFLM_FULL_BACKUP; + m_backupRc = NE_XFLM_OK; +} + +/**************************************************************************** +Desc : Streams the contents of a database to the write hook supplied by + the application. +Notes: This routine attempts to create a backup of a database without + excluding any readers or updaters. However, if the backup runs + too long in an environment where extensive updates are happening, + an old view error could be returned. +****************************************************************************/ +RCODE F_Backup::backup( + const char * pszBackupPath, + const char * pszPassword, + IF_BackupClient * ifpClient, + IF_BackupStatus * ifpStatus, + FLMUINT * puiIncSeqNum) +{ + FLMBOOL bFullBackup = TRUE; + FLMINT iFileNum; + FLMUINT uiBlkAddr; + FLMUINT uiTime; + F_CachedBlock * pSCache = NULL; + XFLM_DB_HDR * pDbHdr; + DB_BACKUP_INFO backupInfo; + FLMUINT uiBlockFileOffset; + FLMUINT uiCount; + FLMUINT uiBlockCount; + FLMUINT uiBlockCountLastCB = 0; + FLMUINT uiBytesToPad; + F_BackerStream * pBackerStream = NULL; + FLMBYTE * pucBlkBuf = NULL; + FLMUINT uiBlkBufOffset; + FLMUINT uiBlkBufSize; + FLMUINT uiMaxCSBlocks; + FLMUINT uiCPTransOffset; + FLMUINT uiMaxFileSize; + FLMBOOL bReleaseClient = FALSE; + FLMBOOL bMustUnlock = FALSE; + RCODE rc = NE_XFLM_OK; + + if( puiIncSeqNum) + { + *puiIncSeqNum = 0; + } + + // Make sure a backup attempt has not been made with this + // backup handle. + + if( m_bCompletedBackup) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( RC_BAD( m_backupRc)) + { + rc = m_backupRc; + goto Exit; + } + + // Look at the backup type + + if( m_eBackupType == XFLM_INCREMENTAL_BACKUP) + { + if( puiIncSeqNum) + { + *puiIncSeqNum = m_uiIncSeqNum; + } + + bFullBackup = FALSE; + } + + // Set up the callback + + if( !ifpClient) + { + if( !pszBackupPath) + { + rc = RC_SET( NE_XFLM_INVALID_PARM); + goto Exit; + } + + ifpClient = f_new F_DefaultBackupClient( pszBackupPath); + + if (ifpClient == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + bReleaseClient = TRUE; + } + + // Allocate and initialize the backer stream object + + if( (pBackerStream = f_new F_BackerStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, ifpClient))) + { + goto Exit; + } + + // Allocate a temporary buffer + + uiBlkBufSize = FLM_BACKER_MTU_SIZE; + uiMaxCSBlocks = uiBlkBufSize / m_uiBlockSize; + if( RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf))) + { + goto Exit; + } + + // Setup the status callback info + + f_memset( &backupInfo, 0, sizeof( DB_BACKUP_INFO)); + + // Setup the backup file header + + uiBlkBufOffset = 0; + f_memset( pucBlkBuf, 0, m_uiBlockSize); + f_memcpy( &pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], + FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE); + + UD2FBA( FLM_BACKER_VERSION, + &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]); + UD2FBA( m_uiBlockSize, + &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); + uiMaxFileSize = (FLMUINT)m_dbHdr.ui32MaxFileSize; + UD2FBA( uiMaxFileSize, + &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); + UD2FBA( FLM_BACKER_MTU_SIZE, + &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]); + f_timeGetSeconds( &uiTime); + UD2FBA( uiTime, + &pucBlkBuf[ FLM_BACKER_TIME_OFFSET]); + + uiCount = f_strlen( m_szDbPath); + + if( uiCount <= 3) + { + pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET] = + (FLMBYTE)m_szDbPath[ uiCount - 6]; + pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 1] = + (FLMBYTE)m_szDbPath[ uiCount - 5]; + pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 2] = + (FLMBYTE)m_szDbPath[ uiCount - 4]; + pucBlkBuf[ FLM_BACKER_DB_NAME_OFFSET + 3] = '\0'; + } + + UD2FBA( (FLMUINT32)m_eBackupType, + &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); + + // Set the next incremental serial number in the backup's + // header so that it can be put into the database's log header + // after the backup has been restored. + + f_memcpy( &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], + m_ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); + + // Set the database version number + + UD2FBA( (FLMUINT32)m_uiDbVersion, + &pucBlkBuf[ FLM_BACKER_DB_VERSION]); + + uiBlkBufOffset += m_uiBlockSize; + + // Copy the database header into the backup's buffer + + f_memset( &pucBlkBuf[ uiBlkBufOffset], 0, m_uiBlockSize); + f_memcpy( &pucBlkBuf[ uiBlkBufOffset], &m_dbHdr, sizeof( XFLM_DB_HDR)); + pDbHdr = (XFLM_DB_HDR *)(&pucBlkBuf[ uiBlkBufOffset]); + uiBlkBufOffset += m_uiBlockSize; + + // Fix up the log header + + if( !pDbHdr->ui8RflKeepFiles) + { + // Put zero in as the last transaction offset so that the current + // RFL file will be created if it does not exist after the database + // is restored. This has basically the same effect as setting the + // offset to 512 if the RFL file has already been created. + + pDbHdr->ui32RflLastTransOffset = 0; + uiCPTransOffset = 512; + + // Create new serial numbers for the RFL. We don't want anyone + // to be able to branch into a "no-keep" RFL sequence. + + if (RC_BAD( rc = f_createSerialNumber( + pDbHdr->ucLastTransRflSerialNum))) + { + goto Exit; + } + + if (RC_BAD( rc = f_createSerialNumber( + pDbHdr->ucNextRflSerialNum))) + { + goto Exit; + } + } + else + { + uiCPTransOffset = (FLMUINT)pDbHdr->ui32RflLastTransOffset; + if( !uiCPTransOffset) + { + uiCPTransOffset = 512; + } + } + + // Set the CP offsets to the last trans offsets. This is done + // because the backup could actually read dirty (committed) blocks + // from the cache, resulting in a backup set that contains blocks + // that are more recent than the ones currently on disk. + + pDbHdr->ui32RflLastCPFileNum = pDbHdr->ui32RflCurrFileNum; + pDbHdr->ui64RflLastCPTransID = pDbHdr->ui64CurrTransID; + pDbHdr->ui32RflLastCPOffset = (FLMUINT32)uiCPTransOffset; + pDbHdr->ui32RblEOF = (FLMUINT32)m_uiBlockSize; + pDbHdr->ui32RblFirstCPBlkAddr = 0; + + // If a password was used, wrap the database key in that password + if (pszPassword && *pszPassword) + { + FLMBYTE * pucTmp = NULL; + + // Need to get a lock on the database - mostly to prevent the very + // unlikely possibility of another thread attempting to use the + // database key at the same time we are. + // (Carson found this in his random testing when one thread did + // a wrapKey while another did a backup.) + + if ((m_pDb->m_uiFlags & FDB_HAS_FILE_LOCK) == 0) + { + if (RC_BAD( rc = m_pDb->dbLock(XFLM_LOCK_EXCLUSIVE, 0, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bMustUnlock = TRUE; + } + rc = m_pDb->getDatabase()->m_pWrappingKey->getKeyToStore( &pucTmp, + &pDbHdr->ui32DbKeyLen, + (FLMBYTE *)pszPassword, NULL); + if (bMustUnlock) + { + m_pDb->dbUnlock(); + bMustUnlock = FALSE; + } + if (RC_BAD( rc)) + { + if (pucTmp) + { + f_free( &pucTmp); + } + goto Exit; + } + + f_memcpy( pDbHdr->DbKey, pucTmp, pDbHdr->ui32DbKeyLen); + f_free( &pucTmp); + } + + // Header should already be in native format. + + flmAssert( !hdrIsNonNativeFormat( pDbHdr)); + + // Calculate and set the CRC + + pDbHdr->ui32HdrCRC = calcDbHdrCRC( pDbHdr); + + // Output the header + + if( RC_BAD( rc = pBackerStream->write( uiBlkBufOffset, pucBlkBuf))) + { + goto Exit; + } + + // There is no way to quickly compute the actual number of bytes + // that will be written to the backup. This is due, in part, to the + // fact that the backup compresses unused space out of blocks before + // storing them. + + backupInfo.ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, m_uiLogicalEOF); + uiBlockFileOffset = 0; + uiBlockCount = 0; + iFileNum = 1; + + for( ;;) + { + if( uiBlockFileOffset >= uiMaxFileSize) + { + uiBlockFileOffset = 0; + iFileNum++; + } + + uiBlkAddr = FSBlkAddress( iFileNum, uiBlockFileOffset); + if( !FSAddrIsBelow( uiBlkAddr, m_uiLogicalEOF)) + { + break; + } + + // Get the block + + if( RC_BAD( rc = m_pDb->m_pDatabase->getBlock( m_pDb, NULL, + uiBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + + if( bFullBackup || + pSCache->getBlockPtr()->ui64TransID > m_ui64LastBackupTransId) + { + uiBlkBufOffset = 0; + if ((FLMUINT)pSCache->getBlockPtr()->ui16BlkBytesAvail > + m_uiBlockSize - blkHdrSize( pSCache->getBlockPtr())) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Output the backup header for the block + + UD2FBA( (FLMUINT32)uiBlkAddr, + &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); + + uiBlkBufOffset += FLM_BACKER_BLK_HDR_SIZE; + + // Copy the block into the block buffer and prepare it + // for writing. + + f_memcpy( &pucBlkBuf[ uiBlkBufOffset], + pSCache->getBlockPtr(), m_uiBlockSize); + + // Encrypt the block if needed. + + if (RC_BAD( rc = m_pDb->m_pDatabase->encryptBlock( m_pDb->m_pDict, + &pucBlkBuf[ uiBlkBufOffset]))) + { + goto Exit; + } + + if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, + (F_BLK_HDR *)(&pucBlkBuf [uiBlkBufOffset])))) + { + goto Exit; + } + + uiBlkBufOffset += m_uiBlockSize; + + // Write the block to the backup stream + + if( RC_BAD( rc = pBackerStream->write( + uiBlkBufOffset, pucBlkBuf))) + { + goto Exit; + } + + uiBlockCount++; + } + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + uiBlockFileOffset += m_uiBlockSize; + + // Call the status callback + + if ((uiBlockCount - uiBlockCountLastCB) > 100) + { + if( ifpStatus) + { + backupInfo.ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, + uiBlkAddr); + if( RC_BAD( rc = ifpStatus->backupStatus( + backupInfo.ui64BytesToDo, + backupInfo.ui64BytesDone))) + { + goto Exit; + } + } + + uiBlockCountLastCB = uiBlockCount; + } + } + + // Output the end-of-backup marker + + f_memset( pucBlkBuf, 0xFF, sizeof( FLM_BACKER_BLK_HDR_SIZE)); + if( RC_BAD( rc = pBackerStream->write( FLM_BACKER_BLK_HDR_SIZE, + pucBlkBuf))) + { + goto Exit; + } + + // Pad the backup so that FlmDbRestore will never read more + // data from the input stream than the backup wrote to it. + + uiBytesToPad = (FLMUINT32)(pBackerStream->getMTUSize() - + (pBackerStream->getByteCount() % pBackerStream->getMTUSize())); + + if( uiBytesToPad < pBackerStream->getMTUSize()) + { + f_memset( pucBlkBuf, 0, uiBytesToPad); + if( RC_BAD( rc = pBackerStream->write( uiBytesToPad, pucBlkBuf))) + { + goto Exit; + } + } + + // Because of the double buffering, we need to have one empty + // buffer at the end of the file. + + f_memset( pucBlkBuf, 0, pBackerStream->getMTUSize()); + if( RC_BAD( rc = pBackerStream->write( pBackerStream->getMTUSize(), + pucBlkBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = pBackerStream->flush())) + { + goto Exit; + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + if( pBackerStream) + { + pBackerStream->Release(); + } + + // Call the status callback now that the background + // thread has terminated. + + if( RC_OK( rc) && ifpStatus) + { + backupInfo.ui64BytesDone = backupInfo.ui64BytesToDo; + (void)ifpStatus->backupStatus( backupInfo.ui64BytesToDo, + backupInfo.ui64BytesDone); + } + + if( pucBlkBuf) + { + f_free( &pucBlkBuf); + } + + if( RC_OK( rc)) + { + m_bCompletedBackup = TRUE; + } + + if ( bReleaseClient) + { + ifpClient->Release(); + } + + m_backupRc = rc; + return( rc); +} + +/**************************************************************************** +Area : MISC +Desc : Ends the backup, updating the log header if needed. +****************************************************************************/ +RCODE F_Backup::endBackup( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + if( !m_bCompletedBackup) + { + goto Exit; + } + + // End the transaction + + flmAssert( m_eTransType != XFLM_NO_TRANS); + if( RC_BAD( rc = m_pDb->abortTrans())) + { + goto Exit; + } + m_eTransType = XFLM_NO_TRANS; + m_bTransStarted = FALSE; + + // Start an update transaction. + + if( RC_BAD( rc = m_pDb->beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Update log header fields + + m_pDb->m_pDatabase->m_uncommittedDbHdr.ui64LastBackupTransID = + m_ui64TransId; + + // Since there may have been transactions during the backup, + // we need to take into account the number of blocks that have + // changed during the backup when updating the + // ui32BlksChangedSinceBackup statistic. + + m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32BlksChangedSinceBackup -= + (FLMUINT32)m_uiBlkChgSinceLastBackup; + + // Bump the incremental backup sequence number + + if( m_eBackupType == XFLM_INCREMENTAL_BACKUP) + { + m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32IncBackupSeqNum++; + } + + // Always change the incremental backup serial number. This is + // needed so that if the user performs a full backup, runs some + // transactions against the database, performs another full backup, + // and then performs an incremental backup we will know that the + // incremental backup cannot be restored against the first full + // backup. + + f_memcpy( + m_pDb->m_pDatabase->m_uncommittedDbHdr.ucIncBackupSerialNum, + m_ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); + + // Commit the transaction and perform a checkpoint so that the + // modified log header values will be written. + + bStartedTrans = FALSE; + if( RC_BAD( m_pDb->commitTrans( 0, TRUE))) + { + goto Exit; + } + +Exit: + + // Abort the active transaction (if any) + + if( bStartedTrans) + { + m_pDb->abortTrans(); + } + + // Unset the backup flag + + if( m_pDb) + { + m_pDb->m_pDatabase->lockMutex(); + m_pDb->m_pDatabase->m_bBackupActive = FALSE; + m_pDb->m_pDatabase->unlockMutex(); + } + + // Clear the object + + reset(); + + // Done. + + return( rc); +} + +/**************************************************************************** +Desc: Restores a database from backup +Notes: This routine does not restore referenced BLOBs. +****************************************************************************/ +RCODE F_DbSystem::dbRestore( + const char * pszDbPath, + // [IN] Path of database that is being restored. This is the + // same path format that FlmDbCreate expects + // (i.e., c:\flaim\flm.db). + const char * pszDataDir, + // [IN] Directory where data files are located. + const char * pszRflDir, + // [IN] RFL log file directory. NULL can be passed to indicate + // that the files are located in the same directory as the + // database (specified above). + const char * pszBackupPath, + // [IN] Directory and name of the backup file set. + // This parameter is required only if the default + // BACKER_READ_HOOK is used. Otherwise, NULL can be + // passed as the value of this parameter. + const char * pszPassword, + // [IN] Password that was used durning the backup + IF_RestoreClient * pRestoreObj, + // [IN] Object to be used to read data from the backup set. + IF_RestoreStatus * pRestoreStatus) + // [IN] Object for reporting the status of the restore + // operation +{ + IF_FileHdl * pFileHdl = NULL; + IF_FileHdl * pLockFileHdl = NULL; + F_SuperFileHdl * pSFile = NULL; + FLMBYTE szBasePath[ F_PATH_MAX_SIZE]; + char szTmpPath[ F_PATH_MAX_SIZE]; + FLMUINT uiDbVersion; + FLMUINT uiNextIncNum; + eRestoreAction eAction = XFLM_RESTORE_ACTION_CONTINUE; // default action... + FLMBOOL bRflPreserved; + FLMBOOL bMutexLocked = FALSE; + IF_Db * pDb = NULL; + F_Database * pDatabase = NULL; + F_FSRestore * pFSRestoreObj = NULL; + FLMBOOL bOKToRetry; + RCODE rc = NE_XFLM_OK; + + // Set up the callback + + if( !pRestoreObj) + { + if( !pszBackupPath || *pszBackupPath == 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + if( (pFSRestoreObj = f_new F_FSRestore) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFSRestoreObj->setup( pszDbPath, + pszBackupPath, pszRflDir))) + { + goto Exit; + } + + // Note: If we wanted to be absolutely correct, we'd do an AddRef on + // pFSRestoreObj because there's going to be two pointers pointing at + // it. It really doesn't matter in this case, though because + // pFSRestoreObj is local to this function and will get deleted before + // the function exits. + + pRestoreObj = (IF_RestoreClient *)pFSRestoreObj; + } + + // Get the base path + + getDbBasePath( (char *)szBasePath, pszDbPath, NULL); + + // Lock the global mutex + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Look up the file using findDatabase to see if the file is already open. + // May unlock and re-lock the global mutex.. + + if( RC_BAD( rc = findDatabase( pszDbPath, pszDataDir, &pDatabase))) + { + goto Exit; + } + + // If the database is open, we cannot perform a restore + + if( pDatabase) + { + rc = RC_SET( NE_XFLM_DATABASE_OPEN); + pDatabase = NULL; + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + goto Exit; + } + + // Allocate the F_Database object. This will prevent other threads from + // opening the database while the restore is being performed. + + if( RC_BAD( rc = allocDatabase( pszDbPath, pszDataDir, FALSE, &pDatabase))) + { + goto Exit; + } + + // Unlock the global mutex + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Create a lock file. If this fails, it could indicate + // that the destination database exists and is in use by another + // process. + + f_sprintf( szTmpPath, "%s.lck", szBasePath); + if( RC_BAD( rc = flmCreateLckFile( szTmpPath, &pLockFileHdl))) + { + goto Exit; + } + + // Create the control file and set up the super file object + + if( RC_BAD( rc = gv_pFileSystem->Create( pszDbPath, XFLM_IO_RDWR, &pFileHdl))) + { + goto Exit; + } + + // Allocate a super file object + // NOTE: Do not use extended cache for this super-file object. + + if( (pSFile = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pSFile->Setup( + pDatabase->m_pFileIdList, pszDbPath, pszDataDir))) + { + goto Exit; + } + + // Open the backup set + + if( RC_BAD( rc = pRestoreObj->openBackupSet())) + { + goto Exit; + } + + // Restore the data in the backup set + + if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pRestoreStatus, + pSFile, FALSE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, + &eAction, NULL))) + { + goto Exit; + } + + // See if we should continue + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + + // Close the backup set + + if( RC_BAD( rc = pRestoreObj->close())) + { + goto Exit; + } + + // Apply any available incremental backups. uiNextIncNum will be 0 if + // the database version does not support incremental backups. + + if( uiNextIncNum) + { + FLMUINT uiCurrentIncNum; + + for( ;;) + { + uiCurrentIncNum = uiNextIncNum; + if( RC_BAD( rc = pRestoreObj->openIncFile( uiCurrentIncNum))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmRestoreFile( pRestoreObj, pRestoreStatus, + pSFile, TRUE, &uiDbVersion, &uiNextIncNum, &bRflPreserved, + &eAction, &bOKToRetry))) + { + RCODE tmpRc; + + if( !bOKToRetry) + { + // Cannot retry the operation or continue ... the + // database is in an unknown state. + + goto Exit; + } + + if( pRestoreStatus) + { + if( RC_BAD( tmpRc = + pRestoreStatus->reportError( &eAction, rc))) + { + rc = tmpRc; + goto Exit; + } + } + + if( eAction == XFLM_RESTORE_ACTION_RETRY || + eAction == XFLM_RESTORE_ACTION_CONTINUE) + { + // Abort the current file (if any) + + if( RC_BAD( rc = pRestoreObj->abortFile())) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_CONTINUE) + { + // Break out and begin processing the RFL + + break; + } + + // Otherwise, retry opening the incremental file + + uiNextIncNum = uiCurrentIncNum; + continue; + } + goto Exit; + } + + // See if we should continue + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + + // Close the current file + + if( RC_BAD( rc = pRestoreObj->close())) + { + goto Exit; + } + } + } + } + + // Force everything out to disk + + if( RC_BAD( rc = pSFile->Flush())) + { + goto Exit; + } + + pSFile->Release(); + pSFile = NULL; + + // Don't do anything with the RFL if the preserve flag + // isn't set. + + if( !bRflPreserved) + { + pRestoreObj = NULL; + pRestoreStatus = NULL; + } + + // Open the file and apply any available RFL files. The + // lock file handle is passed to the openDatabase call so + // that we don't have to give up our lock until the + // restore is complete. Also, we don't want to resume + // any indexing at this point. By not resuming the indexes, + // we can perform a DB diff of two restored databases that + // should be identical without having differences in the + // tracker container due to background indexing. + + rc = openDatabase( pDatabase, + pszDbPath, pszDataDir, + pszRflDir, pszPassword, XFLM_DONT_RESUME_THREADS, + TRUE, pRestoreObj, pRestoreStatus, pLockFileHdl, &pDb); + pLockFileHdl = NULL; + + if( RC_BAD( rc)) + { + pDatabase = NULL; + goto Exit; + } + + // If a password was needed to open the database, we need to clear it so it + //can be opened without a password. + + if (pszPassword && pszPassword[0] != 0) + { + if (RC_BAD( rc = pDb->wrapKey())) + { + goto Exit; + } + } + + // Close the database + + pDb->Release(); + pDb = NULL; + +Exit: + + if( pSFile) + { + // Need to release the super file handle before cleaning up the + // FFILE because the super file still has a reference to the + // FFILE's file ID list. + + pSFile->Release(); + } + + if( pDatabase) + { + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + if (RC_BAD( rc)) + { + pDatabase->newDatabaseFinish( rc); + } + + if( !pDatabase->m_uiOpenIFDbCount) + { + pDatabase->freeDatabase(); + } + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + if( pDb) + { + pDb->Release(); + } + + if( pFileHdl) + { + pFileHdl->Release(); + } + + if( pLockFileHdl) + { + pLockFileHdl->Release(); + } + + if( pFSRestoreObj) + { + pFSRestoreObj->Release(); + } + + // If restore failed, remove all database files (excluding RFL files) + + if( RC_BAD( rc)) + { + dbRemove( pszDbPath, pszDataDir, NULL, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc : Restores a full or incremental backup +*END************************************************************************/ +FSTATIC RCODE flmRestoreFile( + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus, + F_SuperFileHdl * pSFile, + FLMBOOL bIncremental, + FLMUINT * puiDbVersion, + FLMUINT * puiNextIncSeqNum, + FLMBOOL * pbRflPreserved, + eRestoreAction * peAction, + FLMBOOL * pbOKToRetry) +{ + FLMUINT uiBytesWritten; + FLMUINT uiLogicalEOF; + FLMUINT uiBlkAddr; + FLMUINT uiBlockCount = 0; + FLMUINT uiBlockSize; + FLMUINT uiDbVersion; + FLMUINT uiMaxFileSize; + FLMUINT uiBackupMaxFileSize; + FLMUINT uiPriorBlkFile = 0; + FLMUINT uiSectorSize; + XFLM_DB_HDR * pDbHdr; + FLMBYTE ucIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; + FLMBYTE ucNextIncSerialNum[ XFLM_SERIAL_NUM_SIZE]; + FLMUINT uiIncSeqNum; + FLMBYTE * pucBlkBuf = NULL; + char szPath[ F_PATH_MAX_SIZE]; + FLMUINT uiBlkBufSize; + FLMUINT uiPriorBlkAddr = 0; + FLMUINT64 ui64BytesToDo = 0; + FLMUINT64 ui64BytesDone = 0; + eDbBackupType eBackupType; + F_BackerStream * pBackerStream = NULL; + RCODE rc = NE_XFLM_OK; + FLMUINT32 ui32CRC; + F_BLK_HDR * pBlkHdr; + + // Initialize the "ok-to-retry" flag + + if( pbOKToRetry) + { + *pbOKToRetry = TRUE; + } + +#ifdef FLM_WIN + + // Don't want to do extra file extensions or flush when file + // is extended. Setting the extend size to ~0 has the effect + // of not updating the directory entry (a time-consuming operation) + // on Windows platforms. On all other platforms, we will just + // go with the default behavior. + + pSFile->setExtendSize( (FLMUINT)(~0)); + +#endif + + + // Set up the backer stream object + + if( (pBackerStream = f_new F_BackerStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pBackerStream->setup( FLM_BACKER_MTU_SIZE, pRestoreObj))) + { + goto Exit; + } + + // Get the path of the .DB file (file 0). + + if( RC_BAD( rc = pSFile->GetFilePath( 0, szPath))) + { + goto Exit; + } + + // Get the sector size + + if( RC_BAD( rc = gv_pFileSystem->GetSectorSize( + szPath, &uiSectorSize))) + { + goto Exit; + } + + // Allocate a temporary buffer. Try to align the buffer on a sector + // boundary to avoid memcpy operatons in the file system. + + uiBlkBufSize = FLM_BACKER_MTU_SIZE; + if( uiSectorSize) + { + uiBlkBufSize = (((uiBlkBufSize / uiSectorSize) + 1) * uiSectorSize); + } + +#ifdef FLM_WIN + if ((pucBlkBuf = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)uiBlkBufSize, MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( uiBlkBufSize, &pucBlkBuf))) + { + goto Exit; + } +#endif + + // Read and verify the backup header + + if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_MIN_DB_BLOCK_SIZE, + pucBlkBuf))) + { + goto Exit; + } + + if( FB2UD( &pucBlkBuf[ FLM_BACKER_VERSION_OFFSET]) != FLM_BACKER_VERSION) + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); + goto Exit; + } + + if( f_strncmp( &pucBlkBuf[ FLM_BACKER_SIGNATURE_OFFSET], + FLM_BACKER_SIGNATURE, FLM_BACKER_SIGNATURE_SIZE) != 0) + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); + goto Exit; + } + + uiBlockSize = (FLMUINT)FB2UW( &pucBlkBuf[ FLM_BACKER_DB_BLOCK_SIZE_OFFSET]); + if( uiBlockSize > FLM_BACKER_MAX_DB_BLOCK_SIZE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Get the maximum file size from the backup header. + + uiBackupMaxFileSize = (FLMUINT)FB2UD( &pucBlkBuf[ FLM_BACKER_BFMAX_OFFSET]); + + // Make sure the MTU is correct + + if( FB2UD( &pucBlkBuf[ FLM_BACKER_MTU_OFFSET]) != FLM_BACKER_MTU_SIZE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Make sure the backup type is correct + + eBackupType = (eDbBackupType)FB2UD( + &pucBlkBuf[ FLM_BACKER_BACKUP_TYPE_OFFSET]); + + if( (eBackupType == XFLM_INCREMENTAL_BACKUP && !bIncremental) || + (eBackupType == XFLM_FULL_BACKUP && bIncremental)) + { + // Do not allow an incremental backup to be restored directly. The + // only way to restore an incremental backup is to provide the + // incremental files when requested by FlmDbRestore. Also, we don't + // want to allow the user to mistakenly hand us a full backup when + // we are expecting an incremental backup. + + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Grab the "next" incremental backup serial number + + f_memcpy( ucNextIncSerialNum, + &pucBlkBuf[ FLM_BACKER_NEXT_INC_SERIAL_NUM], + XFLM_SERIAL_NUM_SIZE); + + // Get the database version from the backup header + + uiDbVersion = FB2UD( &pucBlkBuf[ FLM_BACKER_DB_VERSION]); + if( puiDbVersion) + { + *puiDbVersion = uiDbVersion; + } + + // Seek to the database header block + + if( uiBlockSize > FLM_BACKER_MIN_DB_BLOCK_SIZE) + { + if( RC_BAD( rc = pBackerStream->read( + uiBlockSize - FLM_BACKER_MIN_DB_BLOCK_SIZE, pucBlkBuf))) + { + goto Exit; + } + } + + // Read the database header block from the backup + + if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf))) + { + goto Exit; + } + + // Sanity check - make sure the block size in the backup header + // is the same as the size in the database header + + pDbHdr = (XFLM_DB_HDR *)pucBlkBuf; + + // Calculate the CRC before doing any conversions. + + ui32CRC = calcDbHdrCRC( pDbHdr); + + // Convert to native platform format, if necessary. + + if (hdrIsNonNativeFormat( pDbHdr)) + { + convertDbHdr( pDbHdr); + } + + // Validate the checksum + + if (ui32CRC != pDbHdr->ui32HdrCRC) + { + rc = RC_SET( NE_XFLM_HDR_CRC); + goto Exit; + } + + if( uiBlockSize != (FLMUINT)pDbHdr->ui16BlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Compare the database version in the DB header with + // the one extracted from the backup header + + if( (FLMUINT)pDbHdr->ui32DbVersion != uiDbVersion) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + uiMaxFileSize = (FLMUINT)pDbHdr->ui32MaxFileSize; + + // Set the database version number and block size into the + // super file handle. We only do this if the file being restored + // is the full backup. It will always be first in the restore sequence, + // and thus we only need to set these values into the super file handle + // at that time. + + if( !bIncremental) + { + pSFile->SetBlockSize( uiBlockSize); + } + + // Make sure the maximum block file size matches what was read from the + // backup header. + + if( uiBackupMaxFileSize != uiMaxFileSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Get the logical EOF from the log header + + uiLogicalEOF = (FLMUINT)pDbHdr->ui32LogicalEOF; + + // Are RFL files being preserved? + + if( pbRflPreserved) + { + *pbRflPreserved = pDbHdr->ui8RflKeepFiles + ? TRUE + : FALSE; + } + + // Get the incremental backup sequence number + + uiIncSeqNum = (FLMUINT)pDbHdr->ui32IncBackupSeqNum; + *puiNextIncSeqNum = uiIncSeqNum; + + if( bIncremental) + { + (*puiNextIncSeqNum)++; + } + + // Get information about the incremental backup + + if( bIncremental) + { + FLMUINT uiTmp; + XFLM_DB_HDR dbHdr; + + f_memcpy( ucIncSerialNum, pDbHdr->ucIncBackupSerialNum, + XFLM_SERIAL_NUM_SIZE); + + // Compare the incremental backup sequence number to the value in the + // database's DB header. + + if( RC_BAD( rc = pSFile->ReadHeader( 0, sizeof( XFLM_DB_HDR), + &dbHdr, &uiTmp))) + { + goto Exit; + } + if (hdrIsNonNativeFormat( &dbHdr)) + { + convertDbHdr( &dbHdr); + } + + if( (FLMUINT)dbHdr.ui32IncBackupSeqNum != uiIncSeqNum) + { + rc = RC_SET( NE_XFLM_INVALID_FILE_SEQUENCE); + goto Exit; + } + + // Compare the incremental backup serial number to the value in the + // database's log header. + + if( f_memcmp( ucIncSerialNum, dbHdr.ucIncBackupSerialNum, + XFLM_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( NE_XFLM_SERIAL_NUM_MISMATCH); + goto Exit; + } + + // Increment the incremental backup sequence number + + pDbHdr->ui32IncBackupSeqNum = (FLMUINT32)(uiIncSeqNum + 1); + } + + // At the start of a backup, either incremental or full, + // we generate a new incremental serial number. This is needed so + // that if the user performs a full backup, runs some transactions + // against the database, performs another full backup, and then + // performs an incremental backup we will know that the incremental + // backup cannot be restored against the first full backup. + + // Since the new serial number is not written to the database's log + // header until after the backup completes, we need to put the + // new serial number in the log header during the restore. In doing + // so, the log header will contain the correct serial number for a + // subsequent incremental backup that may have been made. + + f_memcpy( pDbHdr->ucIncBackupSerialNum, + ucNextIncSerialNum, XFLM_SERIAL_NUM_SIZE); + + // DB Header is in native format. Set the CRC + // before writing it out. + + pDbHdr->ui32HdrCRC = calcDbHdrCRC( pDbHdr); + pDbHdr = NULL; + + // Set the "ok-to-retry" flag + + if( pbOKToRetry) + { + *pbOKToRetry = FALSE; + } + + // Write the database header + + if( RC_BAD( rc = pSFile->WriteHeader( 0, + uiBlockSize, pucBlkBuf, &uiBytesWritten))) + { + goto Exit; + } + + // The status callback will give a general idea of how much work + // is left to do. We don't have any way to get the total size + // of the stream to give a correct count, so a close estimate + // will have to suffice. + ui64BytesToDo = FSGetSizeInBytes( uiMaxFileSize, uiLogicalEOF); + + // Write the blocks in the backup file to the database + + for (;;) + { + if( RC_BAD( rc = pBackerStream->read( FLM_BACKER_BLK_HDR_SIZE, + pucBlkBuf))) + { + goto Exit; + } + + uiBlockCount++; + uiBlkAddr = FB2UD( &pucBlkBuf[ FLM_BACKER_BLK_ADDR_OFFSET]); + + // Are we done? + + if( uiBlkAddr == 0xFFFFFFFF) + { + break; + } + + if( !uiBlkAddr || + !FSAddrIsBelow( uiBlkAddr, uiLogicalEOF) || + (uiPriorBlkAddr && !FSAddrIsBelow( uiPriorBlkAddr, uiBlkAddr))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Read and process the block + + if( RC_BAD( rc = pBackerStream->read( uiBlockSize, pucBlkBuf))) + { + goto Exit; + } + + pBlkHdr = (F_BLK_HDR *)pucBlkBuf; + + // Convert the entire block to native format if necessary. + + if (RC_BAD( rc = flmPrepareBlockForUse( uiBlockSize, pBlkHdr))) + { + if (rc == NE_XFLM_BLOCK_CRC) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + } + goto Exit; + } + if( (FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddr) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + // Prepare the block for writing. + + if (RC_BAD( rc = flmPrepareBlockToWrite( uiBlockSize, pBlkHdr))) + { + goto Exit; + } + + // Write the block to the database + +#ifdef FLM_UNIX + + // Unix systems can have sector sizes that are larger than our + // typical 4K database blocks. The Unix implementation of SectorWrite + // (called by WriteBlock) will write the passed-in block and clobber any + // additional data beyond the end of the block to the end of the sector if + // it has enough room in the block buffer to write a full sector. If the + // block buffer is less than a full sector, the Unix SectorWrite will only + // write out the amount requested, not a full sector. + + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlockSize, + NULL, &uiBytesWritten))) +#else + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlkBufSize, + NULL, &uiBytesWritten))) +#endif + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + // Create a new block file + + if( FSGetFileNumber( uiBlkAddr) != (uiPriorBlkFile + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INCONSISTENT_BACKUP); + goto Exit; + } + + if( RC_BAD( rc = pSFile->CreateFile( + FSGetFileNumber( uiBlkAddr)))) + { + goto Exit; + } + +#ifdef FLM_UNIX + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlockSize, + NULL, &uiBytesWritten))) +#else + if( RC_BAD( rc = pSFile->WriteBlock( uiBlkAddr, + uiBlockSize, pucBlkBuf, uiBlkBufSize, + NULL, &uiBytesWritten))) +#endif + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + uiPriorBlkAddr = uiBlkAddr; + uiPriorBlkFile = FSGetFileNumber( uiBlkAddr); + + if( pRestoreStatus && (uiBlockCount & 0x7F) == 0x7F) + { + ui64BytesDone = FSGetSizeInBytes( uiMaxFileSize, uiBlkAddr); + if( RC_BAD( rc = pRestoreStatus->reportProgress( peAction, + ui64BytesToDo, + ui64BytesDone))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + } + + if( pRestoreStatus) + { + // Call the status callback one last time. + + ui64BytesDone = ui64BytesToDo; + if( RC_BAD( rc = pRestoreStatus->reportProgress( peAction, ui64BytesToDo, + ui64BytesDone))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + // It is safe to jump to exit at this point + + goto Exit; + } + } + +Exit: + + if( pucBlkBuf) + { +#ifdef FLM_WIN + (void)VirtualFree( pucBlkBuf, 0, MEM_RELEASE); +#else + f_free( &pucBlkBuf); +#endif + } + + if( pBackerStream) + { + pBackerStream->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_DefaultBackupClient::F_DefaultBackupClient( + const char * pszBackupPath) +{ + m_pFileHdl64 = NULL; + m_ui64Offset = 0; + m_rc = NE_XFLM_OK; + + f_strncpy( m_szPath, pszBackupPath, F_PATH_MAX_SIZE - 1); +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_DefaultBackupClient::~F_DefaultBackupClient() +{ + if (m_pFileHdl64) + { + m_pFileHdl64->Close(); + m_pFileHdl64->Release(); + } +} + + +/**************************************************************************** +Desc: Default hook for creating a backup file set +****************************************************************************/ +RCODE F_DefaultBackupClient::WriteData( + const void * pvBuffer, + FLMUINT uiBytesToWrite) +{ + FLMUINT uiBytesWritten; + RCODE rc = m_rc; + + if( RC_BAD( rc)) + { + goto Exit; + } + + if( m_pFileHdl64 == 0) + { + // Remove any existing backup files + + if( (m_pFileHdl64 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl64->Delete( m_szPath)) && + rc != NE_XFLM_IO_PATH_NOT_FOUND && + rc != NE_XFLM_IO_INVALID_FILENAME) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl64->Create( m_szPath))) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + goto Exit; + } + } + + rc = m_pFileHdl64->Write( m_ui64Offset, + uiBytesToWrite, (FLMBYTE *)pvBuffer, &uiBytesWritten); + m_ui64Offset += uiBytesWritten; + +Exit: + + if( RC_BAD( rc)) + { + m_rc = rc; + if( m_pFileHdl64) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + } + } + + return( rc); +} + +// F_BackerStream methods + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_BackerStream::F_BackerStream( void) +{ + m_bSetup = FALSE; + m_bFirstRead = TRUE; + m_bFinalRead = FALSE; + m_ui64ByteCount = 0; + m_uiBufOffset = 0; + m_pRestoreObj = NULL; + m_hDataSem = F_SEM_NULL; + m_hIdleSem = F_SEM_NULL; + m_pThread = NULL; + m_rc = NE_XFLM_OK; + m_pucInBuf = NULL; + m_puiInOffset = NULL; + m_pucOutBuf = NULL; + m_puiOutOffset = NULL; + m_pucBufs[ 0] = NULL; + m_pucBufs[ 1] = NULL; + m_uiOffsets[ 0] = 0; + m_uiOffsets[ 1] = 0; + m_uiMTUSize = 0; + m_pClient = NULL; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_BackerStream::~F_BackerStream( void) +{ + shutdownThreads(); + + if( m_hDataSem != F_SEM_NULL) + { + f_semDestroy( &m_hDataSem); + } + + if( m_hIdleSem != F_SEM_NULL) + { + f_semDestroy( &m_hIdleSem); + } + + if( m_pucBufs[ 0]) + { + f_free( &m_pucBufs[ 0]); + } + + if( m_pucBufs[ 1]) + { + f_free( &m_pucBufs[ 1]); + } +} + + +/**************************************************************************** +Desc: Start any background threads +****************************************************************************/ +RCODE F_BackerStream::startThreads( void) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pThread) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + // The semaphore handles better be null + + flmAssert( m_hDataSem == F_SEM_NULL); + flmAssert( m_hIdleSem == F_SEM_NULL); + + // Create a semaphore to signal the background thread + // that data is available + + if( RC_BAD( rc = f_semCreate( &m_hDataSem))) + { + goto Exit; + } + + // Create a semaphore to signal when the background thread + // is idle + + if( RC_BAD( rc = f_semCreate( &m_hIdleSem))) + { + goto Exit; + } + + // Start the thread + + if( m_pClient) + { + if( RC_BAD( rc = f_threadCreate( &m_pThread, + F_BackerStream::writeThread, "backup", + FLM_DEFAULT_THREAD_GROUP, 0, (void *)this))) + { + goto Exit; + } + } + else if( m_pRestoreObj) + { + if( RC_BAD( rc = f_threadCreate( &m_pThread, + F_BackerStream::readThread, "restore", + FLM_DEFAULT_THREAD_GROUP, 0, (void *)this))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Shut down any background threads +****************************************************************************/ +void F_BackerStream::shutdownThreads( void) +{ + if( m_pThread) + { + // Shut down the background read or write thread. + + m_pThread->setShutdownFlag(); + f_semSignal( m_hDataSem); + f_threadDestroy( &m_pThread); + + // Now that the thread has terminated, it is safe + // to destroy the data and idle semaphores. + + f_semDestroy( &m_hDataSem); + f_semDestroy( &m_hIdleSem); + } +} + +/**************************************************************************** +Desc: Setup method to use the backer stream as an input stream +****************************************************************************/ +RCODE F_BackerStream::setup( + FLMUINT uiMTUSize, + IF_RestoreClient * pRestoreObj) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( pRestoreObj); + flmAssert( !m_bSetup); + + m_pRestoreObj = pRestoreObj; + m_uiMTUSize = uiMTUSize; + + if( RC_BAD( rc = _setup())) + { + goto Exit; + } + + // Fire up the background threads + + if( RC_BAD( rc = startThreads())) + { + goto Exit; + } + + m_bSetup = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup method to use the backer stream as an output stream +****************************************************************************/ +RCODE F_BackerStream::setup( + FLMUINT uiMTUSize, + IF_BackupClient * pClient) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( pClient); + flmAssert( !m_bSetup); + + m_pClient = pClient; + m_uiMTUSize = uiMTUSize; + + if( RC_BAD( rc = _setup())) + { + goto Exit; + } + + // Fire up the background threads + + if( RC_BAD( rc = startThreads())) + { + goto Exit; + } + + m_bSetup = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Performs setup operations common to read and write streams +****************************************************************************/ +RCODE F_BackerStream::_setup( void) +{ + RCODE rc = NE_XFLM_OK; + + // Allocate a buffer for reading or writing blocks + + if( (m_uiMTUSize < (2 * FLM_BACKER_MAX_DB_BLOCK_SIZE)) || + m_uiMTUSize % FLM_BACKER_MAX_DB_BLOCK_SIZE) + { + rc = RC_SET( NE_XFLM_INVALID_PARM); + goto Exit; + } + + // Allocate buffers for reading or writing + + if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 0]))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( m_uiMTUSize, &m_pucBufs[ 1]))) + { + goto Exit; + } + + m_pucInBuf = m_pucBufs[ 0]; + m_puiInOffset = &m_uiOffsets[ 0]; + + m_pucOutBuf = m_pucBufs[ 1]; + m_puiOutOffset = &m_uiOffsets[ 1]; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads data from the input stream +****************************************************************************/ +RCODE F_BackerStream::read( + FLMUINT uiLength, + FLMBYTE * pucData, + FLMUINT * puiBytesRead) +{ + FLMUINT uiBufSize; + FLMBYTE * pucBuf; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesAvail; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetup); + flmAssert( m_pRestoreObj); + flmAssert( uiLength); + + if( m_bFirstRead) + { + m_bFirstRead = FALSE; + + // Prime the pump. Call signalThread twice ... once to + // get the first chunk of data and a second time to have + // the background thread pre-fetch the next chunk. A backup + // will always have at least two MTU data chunks, so we should + // never get an IO_END_OF_FILE error. If we do, the restore + // operation needs to abort (which will happen because the + // error will be returned to the caller of this routine). + + if( RC_BAD( rc = signalThread()) || + RC_BAD( rc = signalThread())) + { + goto Exit; + } + } + + while( uiLength) + { + uiBufSize = *m_puiOutOffset; + pucBuf = m_pucOutBuf; + + uiBytesAvail = uiBufSize - m_uiBufOffset; + flmAssert( uiBytesAvail); + + if( uiBytesAvail < uiLength) + { + f_memcpy( &pucData[ uiBytesRead], + &pucBuf[ m_uiBufOffset], uiBytesAvail); + m_uiBufOffset += uiBytesAvail; + uiBytesRead += uiBytesAvail; + uiLength -= uiBytesAvail; + } + else + { + f_memcpy( &pucData[ uiBytesRead], + &pucBuf[ m_uiBufOffset], uiLength); + m_uiBufOffset += uiLength; + uiBytesRead += uiLength; + uiLength = 0; + } + + if( m_uiBufOffset == uiBufSize) + { + m_uiBufOffset = 0; + if( RC_BAD( rc = signalThread())) + { + // Since we are reading MTU-sized units and the restore + // code knows when to stop reading, we should never + // get an IO_END_OF_FILE error back from a call to + // signalThread(). If we do, we need to return the + // error to the caller (FlmDbRestore). + goto Exit; + } + } + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + m_ui64ByteCount += (FLMUINT64)uiBytesRead; + return( rc); +} + +/**************************************************************************** +Desc: Writes data to the output stream +****************************************************************************/ +RCODE F_BackerStream::write( + FLMUINT uiLength, + FLMBYTE * pucData, + FLMUINT * puiBytesWritten) +{ + FLMUINT uiMaxWriteSize; + FLMUINT uiBytesWritten = 0; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetup); + flmAssert( m_pClient); + flmAssert( uiLength); + + while( uiLength) + { + uiMaxWriteSize = m_uiMTUSize - *m_puiInOffset; + flmAssert( uiMaxWriteSize); + + if( uiMaxWriteSize < uiLength) + { + f_memcpy( &m_pucInBuf[ *m_puiInOffset], + &pucData[ uiBytesWritten], uiMaxWriteSize); + (*m_puiInOffset) += uiMaxWriteSize; + uiBytesWritten += uiMaxWriteSize; + uiLength -= uiMaxWriteSize; + } + else + { + f_memcpy( &m_pucInBuf[ *m_puiInOffset], + &pucData[ uiBytesWritten], uiLength); + (*m_puiInOffset) += uiLength; + uiBytesWritten += uiLength; + uiLength = 0; + } + + if( (*m_puiInOffset) == m_uiMTUSize) + { + if( RC_BAD( rc = signalThread())) + { + goto Exit; + } + } + } + +Exit: + + if( puiBytesWritten) + { + *puiBytesWritten = uiBytesWritten; + } + + m_ui64ByteCount += (FLMUINT64)uiBytesWritten; + return( rc); +} + +/**************************************************************************** +Desc: Flushes any pending writes to the output stream +****************************************************************************/ +RCODE F_BackerStream::flush( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetup); + + if( m_pClient && m_pThread) + { + if( *m_puiInOffset) + { + if( RC_BAD( rc = signalThread())) + { + goto Exit; + } + } + + // Wait for the background thread to become idle. When it + // does, we know that all writes have completed. + + if( RC_BAD( rc = f_semWait( m_hIdleSem, F_SEM_WAITFOREVER))) + { + goto Exit; + } + + // If the background thread set an error code, we need to return it. + + rc = m_rc; + + // At this point, we know the background thread is either waiting + // for the data semaphore to be signaled or it has exited due to + // an error. We need to re-signal the idle semaphore so that + // other F_BackerStream calls (i.e., additional calls to + // flush, etc.) will not block waiting for it to be signaled + // since it won't be signaled by the background thread until + // after the data semaphore has been signaled again. + + f_semSignal( m_hIdleSem); + + // Jump to exit if we have a bad rc + + if( RC_BAD( rc)) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Signals the read or write thread indicating that data is needed or + that data is available. +****************************************************************************/ +RCODE F_BackerStream::signalThread( void) +{ + FLMBYTE * pucTmp; + FLMUINT * puiTmp; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetup); + + // Return an error if we don't have a thread. + + if( !m_pThread) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + // Wait for the thread to become idle + + if( RC_BAD( rc = f_semWait( m_hIdleSem, F_SEM_WAITFOREVER))) + { + goto Exit; + } + + if( RC_BAD( rc = m_rc)) + { + // If m_rc is bad, we know that the background thread has + // exited and will not signal the idle semaphore again. + // Thus, we will re-signal the idle semaphore so that if the + // code using this class happens to call flush() or some + // other method that waits on the idle semaphore, we + // won't wait forever on something that will never happen. + + f_semSignal( m_hIdleSem); + + // Check the error code + + if( rc == NE_XFLM_IO_END_OF_FILE && !m_bFinalRead) + { + m_bFinalRead = TRUE; + } + else + { + goto Exit; + } + } + + pucTmp = m_pucOutBuf; + puiTmp = m_puiOutOffset; + + m_pucOutBuf = m_pucInBuf; + m_puiOutOffset = m_puiInOffset; + + m_pucInBuf = pucTmp; + m_puiInOffset = puiTmp; + + *(m_puiInOffset) = 0; + + if( !m_bFinalRead) + { + // Signal the thread to read or write data + + f_semSignal( m_hDataSem); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This thread reads data in the background +****************************************************************************/ +RCODE F_BackerStream::readThread( + F_Thread * pThread) +{ + F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); + RCODE rc = NE_XFLM_OK; + + for( ;;) + { + f_semSignal( pBackerStream->m_hIdleSem); + + if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, + F_SEM_WAITFOREVER))) + { + goto Exit; + } + + if( pThread->getShutdownFlag()) + { + break; + } + + if( RC_BAD( rc = pBackerStream->m_pRestoreObj->read( + pBackerStream->m_uiMTUSize, pBackerStream->m_pucInBuf, + pBackerStream->m_puiInOffset))) + { + goto Exit; + } + } + +Exit: + + pBackerStream->m_rc = rc; + f_semSignal( pBackerStream->m_hIdleSem); + return( rc); +} + +/**************************************************************************** +Desc: This thread writes data in the background +****************************************************************************/ +RCODE F_BackerStream::writeThread( + F_Thread * pThread) +{ + F_BackerStream * pBackerStream = (F_BackerStream *)pThread->getParm1(); + RCODE rc = NE_XFLM_OK; + + for( ;;) + { + f_semSignal( pBackerStream->m_hIdleSem); + + if( RC_BAD( rc = f_semWait( pBackerStream->m_hDataSem, + F_SEM_WAITFOREVER))) + { + goto Exit; + } + + if( *(pBackerStream->m_puiOutOffset)) + { + if( RC_BAD( rc = pBackerStream->m_pClient->WriteData( + pBackerStream->m_pucOutBuf, *(pBackerStream->m_puiOutOffset)))) + { + goto Exit; + } + + // Reset *puiOutOffset so that we won't re-write + // the same data again if we receive a shut-down + // signal. + + *(pBackerStream->m_puiOutOffset) = 0; + } + + // Need to put the thread shutdown check here + // so that if the thread is signaled twice, + // once to do final work and once to shut down, + // we will actually do the work before we exit. + + if( pThread->getShutdownFlag()) + { + break; + } + } + +Exit: + + pBackerStream->m_rc = rc; + f_semSignal( pBackerStream->m_hIdleSem); + return( rc); +} diff --git a/version5/src/flblddb.cpp b/version5/src/flblddb.cpp new file mode 100644 index 0000000..e3fe53c --- /dev/null +++ b/version5/src/flblddb.cpp @@ -0,0 +1,2858 @@ +//------------------------------------------------------------------------------ +// Desc: Routines for rebuilding a corrupted database. +// +// 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: flblddb.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef union +{ + FLMBYTE * pucBlk; + F_BLK_HDR * pBlkHdr; + F_BTREE_BLK_HDR * pBTreeBlkHdr; +} F_BLOCK_UNION; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct +{ + FLMUINT uiBlockSize; + FLMUINT uiCollection; + FLMUINT64 ui64ElmNodeId; + FLMUINT uiElmNumber; + FLMBYTE * pucElm; + FLMUINT uiElmLen; + FLMBYTE * pucElmKey; + FLMUINT uiElmKeyLen; + FLMBYTE * pucElmData; + FLMUINT uiElmDataLen; + FLMUINT uiOverallDataLen; + FLMUINT uiDataOnlyBlkAddr; + FLMUINT32 ui32NextBlkInChain; + FLMUINT32 ui32BlkAddr; + FLMUINT uiNumKeysInBlk; +} F_ELM_INFO; + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct +{ + FLMUINT uiFileNumber; + FLMUINT uiFileOffset; + FLMUINT uiBlockSize; + FLMUINT uiBlockBytes; + FLMUINT uiCurOffset; + F_ELM_INFO elmInfo; + F_BLOCK_UNION blkUnion; +} F_SCAN_STATE; + +// Local function prototypes + +FSTATIC void flmGetCreateOpts( + XFLM_DB_HDR * pDbHdr, + XFLM_CREATE_OPTS * pCreateOpts); + +FSTATIC FLMINT bldGetElmInfo( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT uiBlockSize, + FLMUINT uiElmNumber, + F_ELM_INFO * pElmInfo); + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE void bldFreeCachedNode( + F_CachedNode ** ppCachedNode) +{ + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + (*ppCachedNode)->decrNodeUseCount(); + delete *ppCachedNode; + *ppCachedNode = NULL; + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +class F_RebuildNodeIStream : public IF_IStream, public XF_Base +{ +public: + + F_RebuildNodeIStream() + { + m_bOpen = FALSE; + m_pucFirstElmBlk = NULL; + m_pucTmpBlk = NULL; + m_pCurState = NULL; + m_pDbRebuild = NULL; + } + + ~F_RebuildNodeIStream() + { + close(); + } + + RCODE open( + F_DbRebuild * pRebuild, + FLMBOOL bRecovDictionary); + + void XFLMAPI close( void); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + RCODE readNode( + FLMUINT32 ui32BlkAddr, + FLMUINT uiElmNumber, + F_CachedNode ** ppCachedNode, + FLMBYTE * pucIV); + + RCODE getNextNode( + F_CachedNode ** ppCachedNode, + F_ELM_INFO * pElmInfo, + FLMBYTE * pucIV); + + RCODE getNextNodeInfo( + F_ELM_INFO * pElmInfo, + F_NODE_INFO * pNodeInfo); + +private: + + RCODE readBlock( + IF_FileHdl * pFileHdl, + FLMUINT uiFileNumber, + FLMUINT uiFileOffset, + F_SCAN_STATE * pScanState); + + RCODE readContinuationElm( void); + + RCODE readNextFirstElm( void); + + RCODE readNextSequentialBlock( + F_SCAN_STATE * pScanState); + + RCODE readFirstDataOnlyBlock( void); + + RCODE readNextDataOnlyBlock( void); + + FINLINE FLMBOOL doCollection( + FLMUINT uiCollectionNum, + FLMBOOL bDoDictCollections) + { + if( !bDoDictCollections) + { + if( isDictCollection( uiCollectionNum)) + { + return( FALSE); + } + + return( (uiCollectionNum == XFLM_DATA_COLLECTION || + uiCollectionNum <= XFLM_MAX_COLLECTION_NUM) + ? TRUE + : FALSE); + } + else + { + return( isDictCollection( uiCollectionNum)); + } + } + + F_DbRebuild * m_pDbRebuild; + FLMBYTE * m_pucFirstElmBlk; + FLMBYTE * m_pucTmpBlk; + + F_SCAN_STATE m_firstElmState; + F_SCAN_STATE m_tmpState; + F_SCAN_STATE * m_pCurState; + FLMBOOL m_bOpen; + FLMBOOL m_bRecovDictionary; +}; + +/*************************************************************************** +Desc: Comparison object for node result sets +***************************************************************************/ +class F_NodeResultSetCompare : public IF_ResultSetCompare, public XF_Base +{ + inline RCODE XFLMAPI compare( + const void * pvData1, + FLMUINT uiLength1, + const void * pvData2, + FLMUINT uiLength2, + FLMINT * piCompare) + { + FLMBYTE * pucData1 = (FLMBYTE *)pvData1; + FLMBYTE * pucData2 = (FLMBYTE *)pvData2; + FLMUINT uiCollection1; + FLMUINT uiCollection2; + FLMUINT64 ui64NodeId1; + FLMUINT64 ui64NodeId2; + +#ifdef FLM_DEBUG + flmAssert( uiLength1 == uiLength2); +#else + F_UNREFERENCED_PARM( uiLength1); + F_UNREFERENCED_PARM( uiLength2); +#endif + + if( *pucData1 < *pucData2) + { + *piCompare = -1; + } + else if( *pucData1 > *pucData2) + { + *piCompare = 1; + } + else + { + uiCollection1 = byteToLong( &pucData1[ 1]); + uiCollection2 = byteToLong( &pucData2[ 1]); + + if( uiCollection1 < uiCollection2) + { + *piCompare = -1; + } + else if( uiCollection1 > uiCollection2) + { + *piCompare = 1; + } + else + { + ui64NodeId1 = byteToLong64( &pucData1[ 5]); + ui64NodeId2 = byteToLong64( &pucData2[ 5]); + + if( ui64NodeId1 < ui64NodeId2) + { + *piCompare = -1; + } + else if( ui64NodeId1 > ui64NodeId2) + { + *piCompare = 1; + } + else + { + *piCompare = 0; + } + } + } + + return( NE_XFLM_OK); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_ResultSetCompare::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_ResultSetCompare::Release()); + } +}; + +/**************************************************************************** +Desc : Rebuilds a damaged database. +****************************************************************************/ +RCODE F_DbRebuild::dbRebuild( + const char * pszSourceDbPath, + // [IN] Source database path. This parameter specifies + // the path and file name of the database which is to be + // rebuilt. + const char * pszSourceDataDir, + // [IN] Source directory for data files. + const char * pszDestDbPath, + // [IN] Destination database path. This parameter specifies + // the path and file name of a database to be created during + // the rebuild. + const char * pszDestDataDir, + // [IN] Destination directory for data files. + const char * pszDestRflDir, + // [IN] Destination database's RFL path. This parameter specifies + // the path of the destination RFL directory. NULL can be passed + // if the RFL files are stored in the same directory as the other + // destination database files. + const char * pszDictPath, + // [IN] Dictionary path. Specifies the path of the + // data dictionary file to be used when rebuilding the + // database. The file should contain a copy of the + // data dictionary that was used when the source + // database was originally created. + const char * pszPassword, + // [IN] Pointer to a password to use when extracting the database key if + // the database is encrypted. + XFLM_CREATE_OPTS * pCreateOpts, + // [IN] Create options. Specifies the create options + // which should be used when creating the temporary + // database. Once the rebuild is complete, the + // temporary database file is copied over the source + // database file. Any create options specified for the + // temporary database are inherited by the rebuilt source + // database. + FLMUINT64 * pui64TotNodes, + // [OUT] Number of nodes in the source database. + // An estimate of the number of nodes that were in the + // source database is returned. This may not be exact. + FLMUINT64 * pui64NodesRecov, + // [OUT] Number of nodes recovered. + FLMUINT64 * pui64DiscardedDocs, + // [OUT] Number of quarantined nodes. + IF_DbRebuildStatus * ifpDbRebuild + // [IN] Pointer to a user-provided interface. NULL may be passed as the + // value of this parameter if status reporting is not needed. + ) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = NULL; + FLMBOOL bDatabaseLocked = FALSE; + FLMBOOL bWriteLocked = FALSE; + ServerLockObject * pWriteLockObj = NULL; + ServerLockObject * pDatabaseLockObj = NULL; + FLMBOOL bMutexLocked = FALSE; + IF_FileHdl * pLockFileHdl = NULL; + eDbLockType eCurrLockType; + FLMUINT uiThreadId; + FLMUINT uiNumExclQueued; + FLMUINT uiNumSharedQueued; + FLMUINT uiPriorityCount; + FLMBOOL bUsedDatabase = FALSE; + FLMBOOL bWaited; + F_DbSystem dbSystem; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen; + F_SEM hWaitSem = F_SEM_NULL; + FLMUINT uiRflToken = 0; + F_CCS * pWrappingKey = NULL; + + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + m_bBadHeader = FALSE; + m_cbrc = NE_XFLM_OK; + +Retry: + + // See if there is a database object for this file + // May unlock and re-lock the global mutex. + + if( RC_BAD( rc = dbSystem.findDatabase( pszSourceDbPath, pszSourceDataDir, + &pDatabase))) + { + goto Exit; + } + + // If we didn't find a database object, get an exclusive lock on the file. + + if( !pDatabase) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Attempt to get an exclusive lock on the file. + + if( RC_BAD( rc = flmCreateLckFile( pszSourceDbPath, &pLockFileHdl))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDatabase->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // The call to flmVerifyFileUse will wait if the file is in + // the process of being opened by another thread. + + if( RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) + { + goto Exit; + } + + if( bWaited) + { + goto Retry; + } + + // Increment the open count on the Database so it will not + // disappear while we are rebuilding the file. + + pDatabase->incrOpenCount(); + bUsedDatabase = TRUE; + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // See if the thread already has a file lock. If so, there + // is no need to obtain another. However, we also want to + // make sure there is no write lock. If there is, + // we cannot do the rebuild right now. + + pDatabase->m_pDatabaseLockObj->GetLockInfo( (FLMINT)0, &eCurrLockType, + &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, &uiPriorityCount); + + if( eCurrLockType == XFLM_LOCK_EXCLUSIVE && uiThreadId == f_threadId()) + { + // See if there is already a transaction going. + + pDatabase->m_pWriteLockObj->GetLockInfo( (FLMINT)0, &eCurrLockType, + &uiThreadId, &uiNumExclQueued, &uiNumSharedQueued, + &uiPriorityCount); + + if( eCurrLockType == XFLM_LOCK_EXCLUSIVE && + uiThreadId == f_threadId()) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + } + else + { + pDatabaseLockObj = pDatabase->m_pDatabaseLockObj; + pDatabaseLockObj->AddRef(); + + if (RC_BAD( rc = pDatabaseLockObj->Lock( NULL, hWaitSem, + TRUE, FALSE, TRUE, XFLM_NO_TIMEOUT, 0))) + { + goto Exit; + } + + bDatabaseLocked = TRUE; + } + + // Lock the write object to eliminate contention with + // the checkpoint thread. + + pWriteLockObj = pDatabase->m_pWriteLockObj; + pWriteLockObj->AddRef(); + + // Only contention here is with the checkpoint thread. + // Wait forever for the checkpoint thread to give + // up the lock. + + if( RC_BAD( rc = pDatabase->dbWriteLock( hWaitSem))) + { + goto Exit; + } + bWriteLocked = TRUE; + } + + f_memset( &m_dbHdr, 0, sizeof( XFLM_DB_HDR)); + f_memset( &m_createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + f_memset( &m_callbackData, 0, sizeof( XFLM_REBUILD_INFO)); + f_memset( &m_corruptInfo, 0, sizeof( XFLM_CORRUPT_INFO)); + m_pRebuildStatus = ifpDbRebuild; + m_uiLastStatusTime = 0; + + // Open the damaged database + + if( (m_pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pSFileHdl->Setup( NULL, + pszSourceDbPath, pszSourceDataDir))) + { + goto Exit; + } + + // Check the header information to see if we were in the middle + // of a previous copy. + + if( RC_BAD( rc = flmGetHdrInfo( m_pSFileHdl, &m_dbHdr))) + { + m_bBadHeader = TRUE; + + if( rc == NE_XFLM_HDR_CRC) + { + m_bBadHeader = TRUE; + rc = NE_XFLM_OK; + + if (!pCreateOpts) + { + flmGetCreateOpts( &m_dbHdr, &m_createOpts); + pCreateOpts = &m_createOpts; + } + } + else if( rc == NE_XFLM_UNSUPPORTED_VERSION || rc == NE_XFLM_NEWER_FLAIM) + { + goto Exit; + } + else if( rc == NE_XFLM_NOT_FLAIM || + !F_DbSystem::validBlockSize( m_dbHdr.ui16BlockSize)) + { + FLMUINT uiSaveBlockSize; + FLMUINT uiCalcBlockSize; + + if( !pCreateOpts) + { + if( rc != NE_XFLM_NOT_FLAIM) + { + flmGetCreateOpts( &m_dbHdr, &m_createOpts); + } + else + { + flmGetCreateOpts( NULL, &m_createOpts); + } + + // Set block size to zero, so we will always take the calculated + // block size below. + + m_createOpts.uiBlockSize = 0; + pCreateOpts = &m_createOpts; + } + + // Try to determine the correct block size. + + if( RC_BAD( rc = determineBlkSize( &uiCalcBlockSize))) + { + goto Exit; + } + + uiSaveBlockSize = pCreateOpts->uiBlockSize; + pCreateOpts->uiBlockSize = uiCalcBlockSize; + + // Initialize the database header to useable values. + + flmInitDbHdr( pCreateOpts, FALSE, FALSE, &m_dbHdr); + + // Only use the passed-in block size (uiSaveBlockSize) if it + // was non-zero. + + if( uiSaveBlockSize) + { + pCreateOpts->uiBlockSize = uiSaveBlockSize; + } + } + else + { + goto Exit; + } + } + else + { + if( !pCreateOpts) + { + flmGetCreateOpts( &m_dbHdr, &m_createOpts); + pCreateOpts = &m_createOpts; + } + } + + // If the corrupt database has a key, and we are built with NICI, + // we will extract it. + + if( m_dbHdr.ui32DbKeyLen) + { +#ifndef FLM_USE_NICI + + rc = RC_SET( NE_XFLM_UNSUPPORTED_FEATURE); + goto Exit; + +#else + + if( (pWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pWrappingKey->init( TRUE, FLM_NICI_AES))) + { + goto Exit; + } + + // If the key was encrypted in a password, then the pszPassword + // parameter better be the key used to encrypt it. If the key was + // not encrypted in a password, then pszPassword parameter should be NULL. + + if( RC_BAD( rc = pWrappingKey->setKeyFromStore( m_dbHdr.DbKey, + (FLMBYTE *)pszPassword, NULL))) + { + goto Exit; + } + +#endif + } + + // Delete the destination database in case it already exists. + + if( RC_BAD( rc = dbSystem.dbRemove( pszDestDbPath, pszDestDataDir, + pszDestRflDir, TRUE))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // If no block size has been specified or determined yet, use what we + // read from the database header. + + if( !pCreateOpts->uiBlockSize) + { + pCreateOpts->uiBlockSize = m_dbHdr.ui16BlockSize; + } + + m_pSFileHdl->SetBlockSize( m_dbHdr.ui16BlockSize); + + // Create the destination database + + if( RC_BAD( rc = dbSystem.dbCreate( pszDestDbPath, pszDestDataDir, + pszDestRflDir, pszDictPath, NULL, pCreateOpts, + (IF_Db **)&m_pDb))) + { + goto Exit; + } + + // Check for a key from the corrupt database. + + if( pWrappingKey) + { + if( RC_BAD( rc = m_pDb->transBegin( + XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT, 0))) + { + goto Exit; + } + + ui32KeyLen = XFLM_MAX_ENC_KEY_SIZE; + if( RC_BAD( rc = pWrappingKey->getKeyToStore( + &pucWrappingKey, (FLMUINT32 *)&ui32KeyLen, + (FLMBYTE *)pszPassword, NULL))) + { + goto Exit; + } + + f_memcpy( m_pDb->m_pDatabase->m_uncommittedDbHdr.DbKey, + pucWrappingKey, ui32KeyLen); + + m_pDb->m_pDatabase->m_uncommittedDbHdr.ui32DbKeyLen = ui32KeyLen; + f_free( &pucWrappingKey); + pucWrappingKey = NULL; + + // Write out the log header + + if (RC_BAD( rc = m_pDb->m_pDatabase->writeDbHdr( m_pDb->m_pDbStats, + m_pDb->m_pSFileHdl, &m_pDb->m_pDatabase->m_uncommittedDbHdr, + NULL, TRUE))) + { + goto Exit; + } + + m_pDb->m_bHadUpdOper = TRUE; + + if( RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + + // To make the new key active, we need to close the + // database and reopen it. + + m_pDb->Release(); + m_pDb = NULL; + + if( RC_BAD( rc = dbSystem.openDb( pszDestDbPath, pszDestDataDir, + pszDestRflDir, pszPassword, 0, (IF_Db **)&m_pDb))) + { + goto Exit; + } + } + + m_pDb->m_uiFlags |= FDB_REBUILDING_DATABASE; + + // Disable RFL logging + + m_pDb->m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + + // Rebuild the database + + if( RC_BAD( rc = rebuildDatabase())) + { + goto Exit; + } + +Exit: + + if( pucWrappingKey) + { + f_free( pucWrappingKey); + } + + if( pWrappingKey) + { + pWrappingKey->Release(); + } + + if( uiRflToken) + { + m_pDb->m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if( bUsedDatabase) + { + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + pDatabase->decrOpenCount(); + } + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + } + + // Unlock the file, if it is locked. + + if( bWriteLocked) + { + pDatabase->dbWriteUnlock(); + bWriteLocked = FALSE; + } + + if( bDatabaseLocked) + { + RCODE rc3; + + if( RC_BAD( rc3 = pDatabaseLockObj->Unlock( TRUE, NULL))) + { + if (RC_OK( rc)) + { + rc = rc3; + } + } + + bDatabaseLocked = FALSE; + } + + if( pWriteLockObj) + { + pWriteLockObj->Release( FALSE); + pWriteLockObj = NULL; + } + + if( pDatabaseLockObj) + { + pDatabaseLockObj->Release( FALSE); + pDatabaseLockObj = NULL; + } + + if( pLockFileHdl) + { + (void)pLockFileHdl->Close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + + if( m_pDb) + { + m_pDb->Release(); + m_pDb = NULL; + } + + if( m_pSFileHdl) + { + m_pSFileHdl->Release(); + m_pSFileHdl = NULL; + } + + if( pui64TotNodes) + { + *pui64TotNodes = m_callbackData.ui64TotNodes; + } + + if( pui64NodesRecov) + { + *pui64NodesRecov = m_callbackData.ui64NodesRecov; + } + + if( pui64DiscardedDocs) + { + *pui64DiscardedDocs = m_callbackData.ui64DiscardedDocs; + } + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DbRebuild::rebuildDatabase( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + + m_corruptInfo.uiErrLocale = XFLM_LOCALE_B_TREE; + m_corruptInfo.uiErrLfType = XFLM_LF_COLLECTION; + + m_callbackData.ui64NodesRecov = 0; + m_callbackData.ui64DiscardedDocs = 0; + + // Do a first pass to recover any dictionary items that may not have + // been added from the dictionary file that was passed into the rebuild + // function. + + if( m_dbHdr.ui32DbVersion < XFLM_VER_5_12) + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); + goto Exit; + } + + // Recover the dictionary + + m_callbackData.iDoingFlag = REBUILD_RECOVER_DICT; + m_callbackData.bStartFlag = TRUE; + + if( RC_BAD( rc = recoverNodes( TRUE))) + { + goto Exit; + } + + // Reset nodes recovered to zero after the dictionary pass + + m_callbackData.ui64TotNodes = 0; + m_callbackData.ui64NodesRecov = 0; + + // Recover data + + m_callbackData.iDoingFlag = REBUILD_RECOVER_DATA; + m_callbackData.bStartFlag = TRUE; + + if( RC_BAD( rc = recoverNodes( FALSE))) + { + goto Exit; + } + + // Try and preserve other things in the log header + + if( !m_bBadHeader) + { + F_Database * pDatabase; + XFLM_DB_HDR * pDbHdr; + + if( RC_BAD( m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + bStartedTrans = TRUE; + pDatabase = m_pDb->getDatabase(); + pDbHdr = pDatabase->getUncommittedDbHdr(); + + // Set the commit count one less than the old database's + // This is because it will be incremented if the transaction + // successfully commits - which will make it exactly right. + + if( pDbHdr->ui64TransCommitCnt < m_dbHdr.ui64TransCommitCnt - 1) + { + pDbHdr->ui64TransCommitCnt = m_dbHdr.ui64TransCommitCnt - 1; + } + + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( bStartedTrans) + { + m_pDb->transAbort(); + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DbRebuild::recoverNodes( + FLMBOOL bRecoverDictionary) +{ + RCODE rc = NE_XFLM_OK; + FResultSet * pRootRSet = NULL; + FResultSet * pNonRootRSet = NULL; + F_CachedNode * pRecovRoot = NULL; + F_RebuildNodeIStream * pIStream = NULL; + FLMUINT64 ui64RootCount; + FLMUINT64 ui64NonRootCount; + FLMUINT64 ui64RSPosition; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64TransStartPos = 0; + FLMUINT64 ui64LastAbortPos = 0; + F_ELM_INFO elmInfo; + FLMUINT uiRSetEntrySize; + FLMUINT uiBlockAddr; + FLMUINT uiElmNumber; + FLMUINT uiTime; + FLMUINT uiTransStartTime = 0; + FLMUINT uiMaxTransTime; + IF_ResultSetCompare * pCompareRSEntry = NULL; + FLMBOOL bStartedTrans = FALSE; + F_NODE_INFO nodeInfo; + FLMBYTE ucIV[ 16]; + FLMBYTE ucRSetBuffer[ REBUILD_RSET_ENTRY_SIZE]; + XFLM_REBUILD_INFO transStartRebuildInfo; + + // Allocate result sets + + if( (pCompareRSEntry = f_new F_NodeResultSetCompare) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( (pRootRSet = f_new FResultSet( REBUILD_BLK_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pRootRSet->setupResultSet( (char *)".", + pCompareRSEntry, 0, TRUE, FALSE))) + { + goto Exit; + } + + if( (pNonRootRSet = f_new FResultSet( REBUILD_BLK_SIZE)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNonRootRSet->setupResultSet( (char *)".", + pCompareRSEntry, 0, TRUE, FALSE))) + { + goto Exit; + } + + // Allocate and configure the rebuild input stream + + if( (pIStream = f_new F_RebuildNodeIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pIStream->open( this, bRecoverDictionary))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Recover nodes from the source database + + for( ;;) + { + if( RC_BAD( rc = pIStream->getNextNodeInfo( &elmInfo, &nodeInfo))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if( !nodeInfo.ui64ParentId) + { + buildRSetEntry( + getRSetPrefix( nodeInfo.uiNameId), + nodeInfo.uiCollection, nodeInfo.ui64NodeId, + elmInfo.ui32BlkAddr, elmInfo.uiElmNumber, ucRSetBuffer); + + if( RC_BAD( rc = pRootRSet->addEntry( ucRSetBuffer, + sizeof( ucRSetBuffer)))) + { + goto Exit; + } + } + else + { + buildRSetEntry( + 0, + nodeInfo.uiCollection, nodeInfo.ui64NodeId, + elmInfo.ui32BlkAddr, elmInfo.uiElmNumber, ucRSetBuffer); + + if( RC_BAD( rc = pNonRootRSet->addEntry( ucRSetBuffer, + sizeof( ucRSetBuffer)))) + { + goto Exit; + } + } + + m_callbackData.ui64TotNodes++; + } + + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transAbort())) + { + goto Exit; + } + + // Sort the result sets + + if( RC_BAD( rc = pRootRSet->finalizeResultSet( &ui64RootCount))) + { + goto Exit; + } + + if( RC_BAD( rc = pNonRootRSet->finalizeResultSet( &ui64NonRootCount))) + { + goto Exit; + } + + m_callbackData.ui64TotNodes = ui64RootCount + ui64NonRootCount; + FLM_SECS_TO_TIMER_UNITS( 30, uiMaxTransTime); + + // Add the nodes to the destination database + + for( ui64RSPosition = 0; ui64RSPosition < ui64RootCount; ui64RSPosition++) + { +Retry: + uiTime = FLM_GET_TIMER(); + + if( !bStartedTrans) + { + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + bStartedTrans = TRUE; + ui64TransStartPos = ui64RSPosition; + uiTransStartTime = FLM_GET_TIMER(); + f_memcpy( &transStartRebuildInfo, &m_callbackData, + sizeof( XFLM_REBUILD_INFO)); + } + + if( RC_BAD( rc = pRootRSet->setPosition( ui64RSPosition))) + { + goto Exit; + } + + if( RC_BAD( rc = pRootRSet->getCurrent( ucRSetBuffer, + sizeof( ucRSetBuffer), &uiRSetEntrySize))) + { + goto Exit; + } + + extractRSetEntry( ucRSetBuffer, NULL, NULL, + &uiBlockAddr, &uiElmNumber); + + if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, + uiElmNumber, &pRecovRoot, ucIV))) + { + if( RC_BAD( m_cbrc)) + { + rc = m_cbrc; + goto Exit; + } + + rc = NE_XFLM_OK; + continue; + } + + ui64NodeId = pRecovRoot->getNodeId(); + + if( pRecovRoot->getCollection() == XFLM_DICT_COLLECTION) + { + if( ui64NodeId == XFLM_DICTINFO_DOC_ID) + { + // No need to recover the dictinfo document + // since it will be rebuilt as we add nodes + // to the destination database + + m_callbackData.ui64NodesRecov++; + continue; + } + } + + if( ui64LastAbortPos && ui64RSPosition == ui64LastAbortPos) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + + ui64LastAbortPos = 0; + m_callbackData.ui64DiscardedDocs++; + continue; + } + + if( RC_BAD( rc = recoverTree( pIStream, + pNonRootRSet, NULL, pRecovRoot, ucIV))) + { + if( RC_BAD( m_cbrc)) + { + rc = m_cbrc; + goto Exit; + } + + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transAbort())) + { + goto Exit; + } + + ui64LastAbortPos = ui64RSPosition; + ui64RSPosition = ui64TransStartPos; + f_memcpy( &m_callbackData, &transStartRebuildInfo, + sizeof( XFLM_REBUILD_INFO)); + + if( RC_BAD( rc = reportStatus( TRUE))) + { + goto Exit; + } + + if( !ui64RSPosition) + { + continue; + } + + goto Retry; + } + + if( FLM_ELAPSED_TIME( uiTime, uiTransStartTime) >= uiMaxTransTime) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + } + + if( bStartedTrans) + { + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + if( pRecovRoot) + { + bldFreeCachedNode( &pRecovRoot); + } + + if( pIStream) + { + pIStream->Release(); + } + + if( bStartedTrans) + { + m_pDb->transAbort(); + } + + if( pRootRSet) + { + pRootRSet->Release(); + } + + if( pNonRootRSet) + { + pNonRootRSet->Release(); + } + + if( pCompareRSEntry) + { + pCompareRSEntry->Release(); + pCompareRSEntry = NULL; + } + + return( RC_BAD( rc) ? rc : reportStatus( TRUE)); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DbRebuild::recoverTree( + F_RebuildNodeIStream * pIStream, + FResultSet * pNonRootRSet, + F_DOMNode * pParentNode, + F_CachedNode * pRecovCachedNode, + FLMBYTE * pucNodeIV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId = pRecovCachedNode->getNodeId(); + FLMUINT64 ui64ChildId; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttrNode = NULL; + F_CachedNode * pRecovChild = NULL; + eDomNodeType eRecovNodeType = pRecovCachedNode->getNodeType(); + FLMUINT uiCollection = pRecovCachedNode->getCollection(); + FLMUINT uiBlockAddr; + FLMUINT uiElmNumber; + FLMBYTE ucTmpRSetBuffer[ REBUILD_RSET_ENTRY_SIZE]; + FLMBYTE ucChildIV[ 16]; + + // Create the node + + if( !pParentNode) + { + if( eRecovNodeType == DOCUMENT_NODE) + { + if( RC_BAD( rc = m_pDb->createDocument( uiCollection, + (IF_DOMNode **)&pNode, &ui64NodeId))) + { + goto Exit; + } + } + else if( eRecovNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = m_pDb->createRootElement( uiCollection, + pRecovCachedNode->getNameId(), + (IF_DOMNode **)&pNode, &ui64NodeId))) + { + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pParentNode->createNode( m_pDb, eRecovNodeType, + pRecovCachedNode->getNameId(), XFLM_LAST_CHILD, + (IF_DOMNode **)&pNode, &ui64NodeId))) + { + goto Exit; + } + } + + // Set the value + + if( pRecovCachedNode->getDataLength()) + { + if( pRecovCachedNode->getModeFlags() & FDOM_VALUE_ON_DISK) + { + FLMUINT uiEncDefId = pRecovCachedNode->getEncDefId(); + FLMBYTE ucTmpBuf[ FLM_ENCRYPT_CHUNK_SIZE]; + FLMUINT uiBytesRead; + + for( ;;) + { + if( RC_BAD( rc = pIStream->read( ucTmpBuf, + sizeof( ucTmpBuf), &uiBytesRead))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + if( !uiBytesRead) + { + break; + } + } + + if( uiEncDefId) + { + if( RC_BAD( rc = m_pDb->decryptData( uiEncDefId, pucNodeIV, + ucTmpBuf, uiBytesRead, ucTmpBuf, sizeof( ucTmpBuf)))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pNode->setStorageValue( m_pDb, ucTmpBuf, + uiBytesRead, uiEncDefId, FALSE))) + { + goto Exit; + } + + if( uiBytesRead < sizeof( ucTmpBuf)) + { + break; + } + } + + if( RC_BAD( rc = pNode->setStorageValue( m_pDb, + NULL, 0, uiEncDefId, TRUE))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->setStorageValue( m_pDb, + pRecovCachedNode->getDataPtr(), pRecovCachedNode->getDataLength(), + pRecovCachedNode->getEncDefId(), TRUE))) + { + goto Exit; + } + } + } + + // Add attributes + + if( pRecovCachedNode->m_uiAttrCount) + { + FLMUINT uiLoop; + F_AttrItem * pAttrItem; + + for (uiLoop = 0; uiLoop < pRecovCachedNode->m_uiAttrCount; uiLoop++) + { + pAttrItem = pRecovCachedNode->m_ppAttrList [uiLoop]; + if( RC_BAD( rc = pNode->createAttribute( m_pDb, + pAttrItem->m_uiNameId, (IF_DOMNode **)&pAttrNode))) + { + goto Exit; + } + + if( pAttrItem->getAttrDataLength()) + { + if( RC_BAD( rc = pAttrNode->setStorageValue( m_pDb, + pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength(), + pAttrItem->getAttrEncDefId(), TRUE))) + { + goto Exit; + } + } + + if( pAttrItem->getAttrModeFlags()) + { + if( RC_BAD( rc = pAttrNode->addModeFlags( m_pDb, + pAttrItem->getAttrModeFlags()))) + { + goto Exit; + } + } + } + } + + // Set the node's flags + + if( pRecovCachedNode->getPersistentFlags()) + { + if( RC_BAD( rc = pNode->addModeFlags( m_pDb, + pRecovCachedNode->getPersistentFlags()))) + { + goto Exit; + } + } + + m_callbackData.ui64NodesRecov++; + + // Recover the node's children (if any) + + ui64ChildId = pRecovCachedNode->getFirstChildId(); + + while( ui64ChildId) + { + buildRSetEntry( 0, uiCollection, ui64ChildId, 0, 0, ucTmpRSetBuffer); + + if( RC_BAD( rc = pNonRootRSet->findMatch( ucTmpRSetBuffer, + REBUILD_RSET_ENTRY_SIZE, ucTmpRSetBuffer, NULL))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + extractRSetEntry( ucTmpRSetBuffer, NULL, NULL, + &uiBlockAddr, &uiElmNumber); + + if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, + uiElmNumber, &pRecovChild, ucChildIV))) + { + goto Exit; + } + + if( RC_BAD( rc = recoverTree( pIStream, + pNonRootRSet, pNode, pRecovChild, ucChildIV))) + { + goto Exit; + } + + ui64ChildId = pRecovChild->getNextSibId(); + } + + // Recover the annotation node (if any) + + if( pRecovCachedNode->getAnnotationId()) + { + buildRSetEntry( 0, + uiCollection, pRecovCachedNode->getAnnotationId(), + 0, 0, ucTmpRSetBuffer); + + if( RC_BAD( rc = pNonRootRSet->findMatch( ucTmpRSetBuffer, + REBUILD_RSET_ENTRY_SIZE, ucTmpRSetBuffer, NULL))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + extractRSetEntry( ucTmpRSetBuffer, NULL, NULL, + &uiBlockAddr, &uiElmNumber); + + if( RC_BAD( rc = pIStream->readNode( (FLMUINT32)uiBlockAddr, + uiElmNumber, &pRecovChild, ucChildIV))) + { + goto Exit; + } + + if( RC_BAD( rc = recoverTree( pIStream, + pNonRootRSet, pNode, pRecovChild, ucChildIV))) + { + goto Exit; + } + } + + // Set the metavalue + + if( pRecovCachedNode->getMetaValue()) + { + if( RC_BAD( rc = pNode->setMetaValue( m_pDb, + pRecovCachedNode->getMetaValue()))) + { + goto Exit; + } + } + + // Set the prefix ID + + if( pRecovCachedNode->getPrefixId()) + { + if( RC_BAD( rc = pNode->setPrefixId( m_pDb, + pRecovCachedNode->getPrefixId()))) + { + goto Exit; + } + } + + // Call document done if this was a root node + + if( !pParentNode) + { + if( RC_BAD( rc = m_pDb->documentDone( pNode))) + { + goto Exit; + } + } + +Exit: + + if( RC_BAD( rc)) + { + if( pNode) + { + (void)pNode->deleteNode( m_pDb); + } + + m_callbackData.ui64DiscardedDocs++; + } + + if( pNode) + { + pNode->Release(); + } + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( pRecovChild) + { + bldFreeCachedNode( &pRecovChild); + } + + return( rc); +} + +/*************************************************************************** +Desc: Function to extract information about the current element +***************************************************************************/ +FSTATIC FLMINT bldGetElmInfo( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT uiBlockSize, + FLMUINT uiElmNumber, + F_ELM_INFO * pElmInfo) +{ + FLMINT iErrCode = 0; + FLMBYTE * pucElm = NULL; + FLMUINT uiElmLen = 0; + FLMUINT uiElmKeyLen = 0; + FLMUINT uiElmDataLen = 0; + FLMUINT uiOverallDataLen = 0; + FLMUINT uiDataOnlyBlkAddr = 0; + FLMBYTE * pucElmKey = NULL; + FLMBYTE * pucElmData = NULL; + FLMBYTE * pucBlkEnd; + FLMBOOL bNeg; + FLMUINT uiNumKeys = pBlkHdr->ui16NumKeys; + FLMUINT uiBytesProcessed; + FLMUINT16 * pui16OffsetArray; + FLMUINT64 ui64ElmNodeId = 0; + + if( uiElmNumber >= uiNumKeys) + { + flmAssert( 0); + iErrCode = FLM_BAD_ELM_OFFSET; + goto Exit; + } + + pui16OffsetArray = (FLMUINT16 *)((FLMBYTE *)pBlkHdr + + sizeofBTreeBlkHdr( pBlkHdr)); + pucElm = (FLMBYTE *)pBlkHdr + bteGetEntryOffset( pui16OffsetArray, uiElmNumber); + pucBlkEnd = (FLMBYTE *)pBlkHdr + uiBlockSize; + + switch( pBlkHdr->stdBlkHdr.ui8BlkType) + { + case BT_LEAF: + { + if( pucElm + 2 > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiElmKeyLen = FB2UW( pucElm); + uiElmLen = uiElmKeyLen + 2; + break; + } + + case BT_LEAF_DATA: + { + FLMBYTE ucFlags = *pucElm; + FLMBYTE * pucPtr = &pucElm[ 1]; + + if( ucFlags & BTE_FLAG_KEY_LEN) + { + if( pucPtr + 2 > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiElmKeyLen = FB2UW( pucPtr); + uiElmLen = uiElmKeyLen + 2; + pucPtr += 2; + } + else + { + if( pucPtr > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiElmKeyLen = *pucPtr; + uiElmLen = uiElmKeyLen + 1; + pucPtr++; + } + + if( ucFlags & BTE_FLAG_DATA_LEN) + { + if( pucPtr + 2 > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiElmDataLen = FB2UW( pucPtr); + uiElmLen += (uiElmDataLen + 2); + pucPtr += 2; + } + else + { + if( pucPtr > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiElmDataLen = *pucPtr; + uiElmLen += uiElmDataLen + 1; + pucPtr++; + } + + if( ucFlags & BTE_FLAG_OA_DATA_LEN) + { + uiOverallDataLen = FB2UD( pucPtr); + uiElmLen += 4; + pucPtr += 4; + } + + pucElmKey = pucPtr; + pucPtr += uiElmKeyLen; + + pucElmData = pucPtr; + pucPtr += uiElmDataLen; + + if( bteDataBlockFlag( pucElm)) + { + if( uiElmDataLen != 4) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + uiDataOnlyBlkAddr = FB2UD( pucElmData); + } + + break; + } + + default: + { + iErrCode = FLM_BAD_BLK_TYPE; + goto Exit; + } + } + + if( pucElm + uiElmLen > pucBlkEnd) + { + iErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + if( uiElmKeyLen) + { + if( RC_BAD( flmCollation2Number( uiElmKeyLen, pucElmKey, + &ui64ElmNodeId, &bNeg, &uiBytesProcessed))) + { + iErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + + if( bNeg || uiBytesProcessed != uiElmKeyLen || !ui64ElmNodeId) + { + iErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + } + else + { + // If the key length is zero, then this MUST be the last block! + + if( pBlkHdr->stdBlkHdr.ui32NextBlkInChain) + { + iErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + } + + if( !uiOverallDataLen) + { + uiOverallDataLen = uiElmDataLen; + } + +Exit: + + pElmInfo->uiCollection = pBlkHdr->ui16LogicalFile; + pElmInfo->uiBlockSize = uiBlockSize; + pElmInfo->uiElmNumber = uiElmNumber; + pElmInfo->pucElm = pucElm; + pElmInfo->uiElmLen = uiElmLen; + pElmInfo->pucElmKey = pucElmKey; + pElmInfo->uiElmKeyLen = uiElmKeyLen; + pElmInfo->pucElmData = pucElmData; + pElmInfo->uiElmDataLen = uiElmDataLen; + pElmInfo->uiOverallDataLen = uiOverallDataLen; + pElmInfo->uiDataOnlyBlkAddr = uiDataOnlyBlkAddr; + pElmInfo->ui64ElmNodeId = ui64ElmNodeId; + pElmInfo->ui32BlkAddr = pBlkHdr->stdBlkHdr.ui32BlkAddr; + pElmInfo->ui32NextBlkInChain = pBlkHdr->stdBlkHdr.ui32NextBlkInChain; + pElmInfo->uiNumKeysInBlk = pBlkHdr->ui16NumKeys; + + return( iErrCode); +} + +/**************************************************************************** +Desc: Extract create options from the DB header. +****************************************************************************/ +FSTATIC void flmGetCreateOpts( + XFLM_DB_HDR * pDbHdr, + XFLM_CREATE_OPTS * pCreateOpts) +{ + f_memset( pCreateOpts, 0, sizeof( XFLM_CREATE_OPTS)); + if( pDbHdr) + { + pCreateOpts->uiBlockSize = pDbHdr->ui16BlockSize; + pCreateOpts->uiVersionNum = pDbHdr->ui32DbVersion; + pCreateOpts->uiDefaultLanguage = pDbHdr->ui8DefaultLanguage; + pCreateOpts->uiMinRflFileSize = pDbHdr->ui32RflMinFileSize; + pCreateOpts->uiMaxRflFileSize = pDbHdr->ui32RflMaxFileSize; + pCreateOpts->bKeepRflFiles = (FLMBOOL)(pDbHdr->ui8RflKeepFiles + ? TRUE + : FALSE); + pCreateOpts->bLogAbortedTransToRfl = + (FLMBOOL)(pDbHdr->ui8RflKeepAbortedTrans + ? TRUE + : FALSE); + } + else + { + pCreateOpts->uiBlockSize = XFLM_DEFAULT_BLKSIZ; + pCreateOpts->uiVersionNum = XFLM_CURRENT_VERSION_NUM; + pCreateOpts->uiDefaultLanguage = XFLM_DEFAULT_LANG; + pCreateOpts->uiMinRflFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; + pCreateOpts->uiMaxRflFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; + pCreateOpts->bKeepRflFiles = XFLM_DEFAULT_KEEP_RFL_FILES_FLAG; + pCreateOpts->bLogAbortedTransToRfl = XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG; + } +} + +/**************************************************************************** +Desc : Rebuilds a damaged database. +Notes: All the important stuff is handled by the F_DbRebuild::dbRebuild + function (below). All this call does is create an F_DbRebuild object, + call dbRebuild on it, delete the obj and return the RCODE. +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::dbRebuild( + const char * pszSourceDbPath, + const char * pszSourceDataDir, + const char * pszDestDbPath, + const char * pszDestDataDir, + const char * pszDestRflDir, + const char * pszDictPath, + const char * pszPassword, + XFLM_CREATE_OPTS * pCreateOpts, + FLMUINT64 * pui64TotNodes, + FLMUINT64 * pui64NodesRecov, + FLMUINT64 * pui64DiscardedDocs, + IF_DbRebuildStatus * ifpDbRebuild) +{ + RCODE rc = NE_XFLM_OK; + F_DbRebuild * pRbldObj = NULL; + + if( (pRbldObj = f_new F_DbRebuild) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + rc = pRbldObj->dbRebuild( pszSourceDbPath, pszSourceDataDir, + pszDestDbPath, pszDestDataDir, pszDestRflDir, + pszDictPath, pszPassword, pCreateOpts, + pui64TotNodes, pui64NodesRecov, pui64DiscardedDocs, + ifpDbRebuild); + +Exit: + + if( pRbldObj) + { + pRbldObj->Release(); + } + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::open( + F_DbRebuild * pDbRebuild, + FLMBOOL bRecovDictionary) +{ + RCODE rc = NE_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + m_pDbRebuild = pDbRebuild; + m_pDbRebuild->AddRef(); + m_bRecovDictionary = bRecovDictionary; + + f_memset( &m_firstElmState, 0, sizeof( F_SCAN_STATE)); + f_memset( &m_tmpState, 0, sizeof( F_SCAN_STATE)); + + if( RC_BAD( rc = f_alloc( m_pDbRebuild->getBlockSize(), + &m_pucFirstElmBlk))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( m_pDbRebuild->getBlockSize(), + &m_pucTmpBlk))) + { + goto Exit; + } + + m_firstElmState.blkUnion.pucBlk = m_pucFirstElmBlk; + m_tmpState.blkUnion.pucBlk = m_pucTmpBlk; + m_pCurState = NULL; + m_bOpen = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + close(); + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +void F_RebuildNodeIStream::close( void) +{ + if( m_pucFirstElmBlk) + { + f_free( &m_pucFirstElmBlk); + } + + if( m_pucTmpBlk) + { + f_free( &m_pucTmpBlk); + } + + if( m_pDbRebuild) + { + m_pDbRebuild->Release(); + m_pDbRebuild = NULL; + } + + m_pCurState = NULL; + m_bOpen = FALSE; + + f_memset( &m_firstElmState, 0, sizeof( F_SCAN_STATE)); + f_memset( &m_tmpState, 0, sizeof( F_SCAN_STATE)); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readBlock( + IF_FileHdl * pFileHdl, + FLMUINT uiFileNumber, + FLMUINT uiFileOffset, + F_SCAN_STATE * pScanState) +{ + RCODE rc = NE_XFLM_OK; + F_Dict * pDict; + FLMUINT uiBlockSize = m_pDbRebuild->getBlockSize(); + FLMUINT uiBlkEnd; + FLMUINT16 ui16BlkBytesAvail; + FLMUINT32 ui32CRC; + F_BLK_HDR * pBlkHdr = pScanState->blkUnion.pBlkHdr; + FLMBYTE * pucBlk = pScanState->blkUnion.pucBlk; + + if( !pFileHdl) + { + if( RC_BAD( rc = m_pDbRebuild->m_pSFileHdl->GetFileHdl( + uiFileNumber, FALSE, &pFileHdl))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pFileHdl->SectorRead( uiFileOffset, uiBlockSize, + pucBlk, NULL))) + { + goto Exit; + } + + // Determine if we should convert the block here. Calculation of CRC + // should be on unconverted block. + + ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; + + if( blkIsNonNativeFormat( pBlkHdr)) + { + convert16( &ui16BlkBytesAvail); + } + + if( ui16BlkBytesAvail > (uiBlockSize - blkHdrSize( pBlkHdr))) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + uiBlkEnd = (blkIsNewBTree( pBlkHdr) + ? uiBlockSize + : uiBlockSize - (FLMUINT)ui16BlkBytesAvail); + + // Decrypt the block + + if( isEncryptedBlk( pBlkHdr)) + { + if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDictionary( &pDict))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDatabase()->decryptBlock( + pDict, pucBlk))) + { + goto Exit; + } + } + + // Compute the checksum and convert the block + + ui32CRC = calcBlkCRC( pBlkHdr, uiBlkEnd); + + if( blkIsNonNativeFormat( pBlkHdr)) + { + convertBlk( uiBlockSize, pBlkHdr); + } + + // Does the checksum match? + + if( ui32CRC != pBlkHdr->ui32BlkCRC) + { + rc = RC_SET( NE_XFLM_BLOCK_CRC); + goto Exit; + } + + // Make sure the transaction ID looks valid + + if( !m_pDbRebuild->m_bBadHeader && + pBlkHdr->ui64TransID > m_pDbRebuild->m_dbHdr.ui64LastRflCommitID) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // If this is a data-only block with a next sibling, + // the block should be full + + if( pBlkHdr->ui8BlkType == BT_DATA_ONLY && + pBlkHdr->ui32NextBlkInChain && + pBlkHdr->ui16BlkBytesAvail) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + pScanState->uiFileNumber = uiFileNumber; + pScanState->uiFileOffset = uiFileOffset; + pScanState->uiBlockSize = uiBlockSize; + pScanState->uiBlockBytes = uiBlockSize - pBlkHdr->ui16BlkBytesAvail; + pScanState->uiCurOffset = 0; + f_memset( &pScanState->elmInfo, 0, sizeof( F_ELM_INFO)); + + if( RC_BAD( rc = m_pDbRebuild->reportStatus())) + { + goto Exit; + } + +Exit: + + return( RC_BAD( rc) ? rc : m_pDbRebuild->reportStatus()); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readNextSequentialBlock( + F_SCAN_STATE * pScanState) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkCollectionNum; + IF_FileHdl * pFileHdl = NULL; + FLMUINT uiBlockSize = m_pDbRebuild->getBlockSize(); + FLMUINT uiTryNextCount = 0; + + if( pScanState->uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + for( ;;) + { + if( pScanState->uiFileOffset + uiBlockSize >= + m_pDbRebuild->m_dbHdr.ui32MaxFileSize || + !pScanState->uiFileNumber) + { +TryNextFile: + + pScanState->uiFileOffset = 0; + pScanState->uiFileNumber++; + + if( pScanState->uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER || + uiTryNextCount > 5) + { + pScanState->uiFileNumber = MAX_DATA_BLOCK_FILE_NUMBER + 1; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pDbRebuild->m_pSFileHdl->GetFileHdl( + pScanState->uiFileNumber, FALSE, &pFileHdl))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + uiTryNextCount++; + goto TryNextFile; + } + + goto Exit; + } + } + else + { + pScanState->uiFileOffset += uiBlockSize; + } + + if( RC_BAD( rc = readBlock( pFileHdl, pScanState->uiFileNumber, + pScanState->uiFileOffset, pScanState))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + goto TryNextFile; + } + else if( rc == NE_XFLM_DATA_ERROR) + { + rc = NE_XFLM_OK; + continue; + } + + goto Exit; + } + + // Make sure the block end looks correct + + if( pScanState->blkUnion.pBlkHdr->ui16BlkBytesAvail > + uiBlockSize - blkHdrSize( pScanState->blkUnion.pBlkHdr)) + { + if( RC_BAD( rc = m_pDbRebuild->reportCorruption( + FLM_BAD_BLK_HDR_BLK_END, + pScanState->blkUnion.pBlkHdr->ui32BlkAddr, 0, 0))) + { + goto Exit; + } + + continue; + } + + // Verify the block address + + if( pScanState->blkUnion.pBlkHdr->ui32BlkAddr != + FSBlkAddress( pScanState->uiFileNumber, pScanState->uiFileOffset)) + { + if( RC_BAD( rc = m_pDbRebuild->reportCorruption( + FLM_BAD_BLK_HDR_ADDR, pScanState->blkUnion.pBlkHdr->ui32BlkAddr, + 0, 0))) + { + goto Exit; + } + + continue; + } + + // Determine if this is a block that should be processed + + if( FSGetFileOffset( pScanState->blkUnion.pBlkHdr->ui32BlkAddr) == + pScanState->uiFileOffset && + (pScanState->blkUnion.pBlkHdr->ui8BlkType == BT_LEAF || + pScanState->blkUnion.pBlkHdr->ui8BlkType == BT_LEAF_DATA) && + pScanState->blkUnion.pBTreeBlkHdr->ui8BlkLevel == 0 && + (uiBlkCollectionNum = pScanState->blkUnion.pBTreeBlkHdr->ui16LogicalFile) != 0 && + isContainerBlk( pScanState->blkUnion.pBTreeBlkHdr) && + doCollection( uiBlkCollectionNum, m_bRecovDictionary)) + { + if( !m_bRecovDictionary) + { + if( RC_BAD( rc = m_pDbRebuild->m_pDb->getDict()->getCollection( + uiBlkCollectionNum, NULL))) + { + if( rc != NE_XFLM_BAD_COLLECTION) + { + goto Exit; + } + + rc = NE_XFLM_OK; + continue; + } + } + + break; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readNextFirstElm( void) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iErrCode = 0; + + m_pCurState = NULL; + +GetNextElement: + + if( !m_firstElmState.uiFileNumber || + m_firstElmState.elmInfo.uiElmNumber + 1 >= + m_firstElmState.blkUnion.pBTreeBlkHdr->ui16NumKeys) + { + if( RC_BAD( rc = readNextSequentialBlock( &m_firstElmState))) + { + goto Exit; + } + } + else + { + m_firstElmState.elmInfo.uiElmNumber++; + } + + // Extract information about the element + + if( (iErrCode = bldGetElmInfo( m_firstElmState.blkUnion.pBTreeBlkHdr, + m_firstElmState.uiBlockSize, m_firstElmState.elmInfo.uiElmNumber, + &m_firstElmState.elmInfo)) != 0) + { + if( RC_BAD( rc = m_pDbRebuild->reportCorruption( iErrCode, + FSBlkAddress( m_firstElmState.uiFileNumber, m_firstElmState.uiFileOffset), + m_firstElmState.elmInfo.uiElmNumber, + m_firstElmState.elmInfo.ui64ElmNodeId))) + { + goto Exit; + } + + goto GetNextElement; + } + + if( !bteFirstElementFlag( m_firstElmState.elmInfo.pucElm) || + !m_firstElmState.elmInfo.uiElmKeyLen) + { + goto GetNextElement; + } + + if( m_firstElmState.elmInfo.uiDataOnlyBlkAddr) + { + if( RC_BAD( rc = readFirstDataOnlyBlock())) + { + if( RC_BAD( m_pDbRebuild->m_cbrc)) + { + rc = m_pDbRebuild->m_cbrc; + goto Exit; + } + + goto GetNextElement; + } + } + else + { + m_pCurState = &m_firstElmState; + m_pCurState->uiCurOffset = 0; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readContinuationElm( void) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iErrCode = 0; + + if( m_pCurState->elmInfo.uiElmNumber + 1 >= + m_pCurState->blkUnion.pBTreeBlkHdr->ui16NumKeys) + { + F_BLK_HDR * pBlkHdr = m_pCurState->blkUnion.pBlkHdr; + + if( RC_BAD( rc = readBlock( NULL, + FSGetFileNumber( pBlkHdr->ui32NextBlkInChain), + FSGetFileOffset( pBlkHdr->ui32NextBlkInChain), &m_tmpState))) + { + goto Exit; + } + + if( m_tmpState.blkUnion.pBlkHdr->ui64TransID < + m_firstElmState.blkUnion.pBlkHdr->ui64TransID) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + m_pCurState = &m_tmpState; + } + else + { + m_pCurState->elmInfo.uiElmNumber++; + } + + // Extract information about the element + + if( (iErrCode = bldGetElmInfo( + m_pCurState->blkUnion.pBTreeBlkHdr, m_pCurState->uiBlockSize, + m_pCurState->elmInfo.uiElmNumber, &m_pCurState->elmInfo)) != 0) + { + if( RC_BAD( rc = m_pDbRebuild->reportCorruption( iErrCode, + FSBlkAddress( m_pCurState->uiFileNumber, m_pCurState->uiFileOffset), + m_pCurState->elmInfo.uiElmNumber, m_pCurState->elmInfo.ui64ElmNodeId))) + { + goto Exit; + } + + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( bteFirstElementFlag( m_pCurState->elmInfo.pucElm)) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // This had better not be a LEM element. + + if( !m_pCurState->elmInfo.uiElmKeyLen) + { + m_pDbRebuild->reportCorruption( FLM_BAD_LEM, + m_pCurState->elmInfo.ui32BlkAddr, + m_pCurState->elmInfo.uiElmNumber, + m_pCurState->elmInfo.ui64ElmNodeId); + goto Exit; + } + + if( m_pCurState->elmInfo.ui64ElmNodeId != + m_firstElmState.elmInfo.ui64ElmNodeId) + { + m_pDbRebuild->reportCorruption( FLM_BAD_CONT_ELM_KEY, + m_pCurState->elmInfo.ui32BlkAddr, + m_pCurState->elmInfo.uiElmNumber, + m_pCurState->elmInfo.ui64ElmNodeId); + goto Exit; + } + + m_pCurState->uiCurOffset = 0; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readFirstDataOnlyBlock( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucTmpBuf[ 8]; + F_ELM_INFO * pElmInfo = &m_firstElmState.elmInfo; + + flmAssert( pElmInfo->uiDataOnlyBlkAddr); + + if( RC_BAD( rc = readBlock( NULL, + FSGetFileNumber( pElmInfo->uiDataOnlyBlkAddr), + FSGetFileOffset( pElmInfo->uiDataOnlyBlkAddr), &m_tmpState))) + { + goto Exit; + } + + if( m_tmpState.blkUnion.pBlkHdr->ui64TransID != + m_firstElmState.blkUnion.pBlkHdr->ui64TransID) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + m_pCurState = &m_tmpState; + m_pCurState->uiCurOffset = blkHdrSize( m_tmpState.blkUnion.pBlkHdr); + m_pCurState->elmInfo.uiCollection = pElmInfo->uiCollection; + m_pCurState->elmInfo.ui64ElmNodeId = pElmInfo->ui64ElmNodeId; + m_pCurState->elmInfo.uiOverallDataLen = pElmInfo->uiOverallDataLen; + + // Since this is the first block in the data-only chain, the key + // is stored in the first few bytes after the block header. Make + // sure it matches the key in m_firstElmState. + + if( RC_BAD( rc = read( ucTmpBuf, 2, NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( FB2UW( ucTmpBuf) != m_firstElmState.elmInfo.uiElmKeyLen) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + if( RC_BAD( rc = read( ucTmpBuf, + m_firstElmState.elmInfo.uiElmKeyLen, NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( f_memcmp( ucTmpBuf, m_firstElmState.elmInfo.pucElmKey, + m_firstElmState.elmInfo.uiElmKeyLen) != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readNextDataOnlyBlock( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT32 ui32NextBlkAddr = m_pCurState->blkUnion.pBlkHdr->ui32NextBlkInChain; + + if( !ui32NextBlkAddr) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = readBlock( NULL, + FSGetFileNumber( ui32NextBlkAddr), + FSGetFileOffset( ui32NextBlkAddr), &m_tmpState))) + { + goto Exit; + } + + if( m_tmpState.blkUnion.pBlkHdr->ui64TransID != + m_firstElmState.blkUnion.pBlkHdr->ui64TransID) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + m_pCurState = &m_tmpState; + m_pCurState->uiCurOffset = blkHdrSize( m_tmpState.blkUnion.pBlkHdr); + m_pCurState->elmInfo.uiCollection = m_firstElmState.elmInfo.uiCollection; + m_pCurState->elmInfo.ui64ElmNodeId = m_firstElmState.elmInfo.ui64ElmNodeId; + m_pCurState->elmInfo.uiOverallDataLen = m_firstElmState.elmInfo.uiOverallDataLen; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesToCopy; + F_BLK_HDR * pBlkHdr; + F_ELM_INFO * pElmInfo; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + flmAssert( m_bOpen); + flmAssert( m_pCurState); + + while( uiBytesRead < uiBytesToRead) + { + pBlkHdr = m_pCurState->blkUnion.pBlkHdr; + pElmInfo = &m_pCurState->elmInfo; + + if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) + { + uiBytesToCopy = pElmInfo->uiElmDataLen - m_pCurState->uiCurOffset; + } + else + { + uiBytesToCopy = m_pCurState->uiBlockBytes - m_pCurState->uiCurOffset; + } + + uiBytesToCopy = (uiBytesToRead - uiBytesRead) < uiBytesToCopy + ? (uiBytesToRead - uiBytesRead) + : uiBytesToCopy; + + if( !uiBytesToCopy) + { + if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) + { + if( RC_BAD( rc = readContinuationElm())) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = readNextDataOnlyBlock())) + { + goto Exit; + } + } + + continue; + } + + if( pucBuffer) + { + if( pBlkHdr->ui8BlkType != BT_DATA_ONLY) + { + f_memcpy( pucBuffer, + &pElmInfo->pucElmData[ m_pCurState->uiCurOffset], + uiBytesToCopy); + } + else + { + f_memcpy( pucBuffer, + &m_pCurState->blkUnion.pucBlk[ m_pCurState->uiCurOffset], + uiBytesToCopy); + } + + pucBuffer += uiBytesToCopy; + } + + m_pCurState->uiCurOffset += uiBytesToCopy; + uiBytesRead += uiBytesToCopy; + } + + if( uiBytesRead < uiBytesToRead) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::getNextNode( + F_CachedNode ** ppCachedNode, + F_ELM_INFO * pElmInfo, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + F_CachedNode * pCachedNode = NULL; + + if( ppCachedNode) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( *ppCachedNode) + { + pCachedNode = *ppCachedNode; + *ppCachedNode = NULL; + pCachedNode->decrNodeUseCount(); + pCachedNode->resetNode(); + pCachedNode->incrNodeUseCount(); + } + else + { + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocNode( + &pCachedNode, TRUE))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pCachedNode->incrNodeUseCount(); + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + // Read the node + + for( ;;) + { + if( RC_BAD( rc = readNextFirstElm())) + { + goto Exit; + } + + if( pElmInfo) + { + f_memcpy( pElmInfo, &m_firstElmState.elmInfo, sizeof( F_ELM_INFO)); + } + + if( !ppCachedNode) + { + break; + } + + if( RC_OK( rc = pCachedNode->readNode( m_pDbRebuild->m_pDb, + m_firstElmState.elmInfo.uiCollection, + m_firstElmState.elmInfo.ui64ElmNodeId, this, + m_firstElmState.elmInfo.uiOverallDataLen, pucIV))) + { + break; + } + + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_MEM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pCachedNode->decrNodeUseCount(); + pCachedNode->resetNode(); + pCachedNode->incrNodeUseCount(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + if( ppCachedNode) + { + *ppCachedNode = pCachedNode; + pCachedNode = NULL; + } + +Exit: + + if( pCachedNode) + { + bldFreeCachedNode( &pCachedNode); + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_RebuildNodeIStream::getNextNodeInfo( + F_ELM_INFO * pElmInfo, + F_NODE_INFO * pNodeInfo) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTmpFlags; + + // Read the node info + + for( ;;) + { + if( RC_BAD( rc = readNextFirstElm())) + { + goto Exit; + } + + f_memcpy( pElmInfo, &m_firstElmState.elmInfo, sizeof( F_ELM_INFO)); + + if( RC_OK( rc = flmReadNodeInfo( + m_firstElmState.elmInfo.uiCollection, + m_firstElmState.elmInfo.ui64ElmNodeId, + this, m_firstElmState.elmInfo.uiOverallDataLen, FALSE, + pNodeInfo, &uiTmpFlags))) + { + break; + } + + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_MEM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine retrieves one node starting at the current element. +*****************************************************************************/ +RCODE F_RebuildNodeIStream::readNode( + FLMUINT32 ui32BlkAddr, + FLMUINT uiElmNumber, + F_CachedNode ** ppCachedNode, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + F_CachedNode * pCachedNode = NULL; + FLMINT iErrCode = 0; + + m_pCurState = NULL; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + if( *ppCachedNode) + { + pCachedNode = *ppCachedNode; + *ppCachedNode = NULL; + pCachedNode->decrNodeUseCount(); + pCachedNode->resetNode(); + pCachedNode->incrNodeUseCount(); + } + else + { + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->allocNode( + &pCachedNode, TRUE))) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + goto Exit; + } + + pCachedNode->incrNodeUseCount(); + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + if( RC_BAD( rc = readBlock( NULL, FSGetFileNumber( ui32BlkAddr), + FSGetFileOffset( ui32BlkAddr), &m_firstElmState))) + { + goto Exit; + } + + m_pCurState = &m_firstElmState; + + // Extract information about the element + + if( (iErrCode = bldGetElmInfo( m_firstElmState.blkUnion.pBTreeBlkHdr, + m_firstElmState.uiBlockSize, uiElmNumber, &m_firstElmState.elmInfo)) != 0) + { + if( RC_BAD( rc = m_pDbRebuild->reportCorruption( iErrCode, + FSBlkAddress( m_firstElmState.uiFileNumber, m_firstElmState.uiFileOffset), + m_firstElmState.elmInfo.uiElmNumber, + m_firstElmState.elmInfo.ui64ElmNodeId))) + { + goto Exit; + } + + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( !bteFirstElementFlag( m_firstElmState.elmInfo.pucElm)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( m_firstElmState.elmInfo.uiDataOnlyBlkAddr) + { + if( RC_BAD( rc = readFirstDataOnlyBlock())) + { + goto Exit; + } + } + else + { + m_pCurState = &m_firstElmState; + m_pCurState->uiCurOffset = 0; + } + + // Read the node + + if( RC_BAD( rc = pCachedNode->readNode( m_pDbRebuild->m_pDb, + m_pCurState->elmInfo.uiCollection, + m_pCurState->elmInfo.ui64ElmNodeId, this, + m_pCurState->elmInfo.uiOverallDataLen, pucIV))) + { + goto Exit; + } + + *ppCachedNode = pCachedNode; + pCachedNode = NULL; + +Exit: + + if( pCachedNode) + { + bldFreeCachedNode( &pCachedNode); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads through a database and makes a best guess as to + the true block size of the database. +*****************************************************************************/ +RCODE F_DbRebuild::determineBlkSize( + FLMUINT * puiBlkSizeRV) +{ + RCODE rc = NE_XFLM_OK; + F_BLK_HDR blkHdr; + FLMUINT uiBytesRead; + FLMUINT uiBlkAddress; + FLMUINT uiFileNumber = 0; + FLMUINT uiOffset = 0; + FLMUINT uiCount4K = 0; + FLMUINT uiCount8K = 0; + FLMUINT64 ui64BytesDone = 0; + IF_FileHdl * pFileHdl = NULL; + + // Start from byte offset 0 in the first file. + + m_callbackData.iDoingFlag = REBUILD_GET_BLK_SIZ; + m_callbackData.bStartFlag = TRUE; + + for (;;) + { + if( uiOffset >= m_dbHdr.ui32MaxFileSize || !uiFileNumber) + { +TryNextFile: + uiOffset = 0; + uiFileNumber++; + + if( RC_BAD( rc = m_pSFileHdl->GetFileHdl( uiFileNumber, + FALSE, &pFileHdl))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + } + + if( RC_BAD( rc = pFileHdl->Read( uiOffset, + SIZEOF_STD_BLK_HDR, &blkHdr, &uiBytesRead))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + goto TryNextFile; + } + + ui64BytesDone += (FLMUINT64)XFLM_MIN_BLOCK_SIZE; + uiBlkAddress = (FLMUINT)blkHdr.ui32BlkAddr; + + // If the block address does not match up, try converting it. + + if( FSGetFileOffset( uiBlkAddress) != uiOffset) + { + convert32( &blkHdr.ui32BlkAddr); + uiBlkAddress = (FLMUINT)blkHdr.ui32BlkAddr; + } + + if( FSGetFileOffset( uiBlkAddress) == uiOffset) + { + if( uiOffset % 4096 == 0) + { + if( ++uiCount4K >= 1000) + { + break; + } + } + + if( uiOffset % 8192 == 0) + { + if( ++uiCount8K >= 1000) + { + break; + } + } + } + + uiOffset += XFLM_MIN_BLOCK_SIZE; + + if( RC_BAD( rc = reportStatus())) + { + goto Exit; + } + } + + if( uiCount8K > uiCount4K) + { + *puiBlkSizeRV = 8192; + } + else + { + *puiBlkSizeRV = 4096; + } + +Exit: + + return( rc); +} diff --git a/version5/src/flblksum.cpp b/version5/src/flblksum.cpp new file mode 100644 index 0000000..724d97b --- /dev/null +++ b/version5/src/flblksum.cpp @@ -0,0 +1,120 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the routine which calculates a block checksum. +// +// 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: flblksum.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/******************************************************************** +Desc: Calculate the checksum for a block. NOTE: This is ALWAYS done + on the raw image that will be written to disk. This means + that if the block needs to be converted before writing it out, + it should be done before calculating the checksum. +*********************************************************************/ +FLMUINT32 calcBlkCRC( + F_BLK_HDR * pBlkHdr, + FLMUINT uiBlkEnd) +{ + FLMUINT32 ui32SaveCRC; + FLMUINT uiAdds; + FLMUINT uiXORs; + FLMBYTE * pucBlkPtr; + + // Calculate CRC on everything except for the ui32BlkCRC value. + // To do this, we temporarily change it to zero. The saved + // value will be restored after calculating the CRC. + + ui32SaveCRC = pBlkHdr->ui32BlkCRC; + pBlkHdr->ui32BlkCRC = 0; + uiAdds = 0; + uiXORs = 0; + pucBlkPtr = (FLMBYTE *)pBlkHdr; + +#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) + + FastBlockCheckSum( pucBlkPtr, &uiAdds, &uiXORs, + (unsigned long)uiBlkEnd); + +#else + + FLMBYTE * pucCur = pucBlkPtr; + FLMBYTE * pucEnd = pucCur + uiBlkEnd; + + while( pucCur < pucEnd) + { + uiAdds += *pucCur; + uiXORs ^= *pucCur++; + } + + uiAdds &= 0xFF; +#endif + + // Restore the CRC that was in the block. + + pBlkHdr->ui32BlkCRC = ui32SaveCRC; + return( (FLMUINT32)((uiAdds << 16) + uiXORs)); +} + +/******************************************************************** +Desc: Calculate the CRC for the database header. +*********************************************************************/ +FLMUINT32 calcDbHdrCRC( + XFLM_DB_HDR * pDbHdr) +{ + FLMUINT32 ui32SaveCRC; + FLMUINT uiAdds; + FLMUINT uiXORs; + FLMBYTE * pucHdr; + + // Checksum everything except for the ui32HdrCRC value. + + ui32SaveCRC = pDbHdr->ui32HdrCRC; + pDbHdr->ui32HdrCRC = 0; + + uiAdds = 0; + uiXORs = 0; + pucHdr = (FLMBYTE *)pDbHdr; + +#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) + + FastBlockCheckSum( pucHdr, &uiAdds, &uiXORs, + (unsigned long)sizeof( XFLM_DB_HDR)); + +#else + FLMBYTE * pucCur = pucHdr; + FLMBYTE * pucEnd = pucHdr + sizeof( XFLM_DB_HDR); + + while( pucCur < pucEnd) + { + uiAdds += *pucCur; + uiXORs ^= *pucCur++; + } + + uiAdds &= 0xFF; +#endif + + // Restore the checksum that was in the header + + pDbHdr->ui32HdrCRC = ui32SaveCRC; + return( (FLMUINT32)((uiAdds << 16) + uiXORs)); +} diff --git a/version5/src/flchkdb.cpp b/version5/src/flchkdb.cpp new file mode 100644 index 0000000..a72a298 --- /dev/null +++ b/version5/src/flchkdb.cpp @@ -0,0 +1,937 @@ +//------------------------------------------------------------------------------ +// Desc: Check a database for corruptions +// +// 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: flchkdb.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_DbCheck::~F_DbCheck() +{ + if (m_pXRefRS) + { + m_pXRefRS->Release(); + m_pXRefRS = NULL; + } + + // Cleanup any temporary index check files + + if (m_pIxRSet) + { + m_pIxRSet->Release(); + } + f_free( &m_puiIxArray); + + if (m_pDb) + { + m_pDb->Release(); + } + + if (m_pDbInfo) + { + m_pDbInfo->Release(); + } + + (void)closeAndDeleteResultSetDb(); + + if (m_pRandGen) + { + m_pRandGen->Release(); + } + + if (m_pBtPool) + { + m_pBtPool->Release(); + } + if (m_pBlkEntries) + { + f_free( &m_pBlkEntries); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DbCheck::createAndOpenResultSetDb( void) +{ + RCODE rc = NE_XFLM_OK; + XFLM_CREATE_OPTS createOpts; + F_DbSystem dbSystem; + + if (m_pResultSetDb) + { + if (RC_BAD( rc = closeAndDeleteResultSetDb())) + { + goto Exit; + } + } + + f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + for (;;) + { + + // Generate a random file name + + f_sprintf( m_szResultSetDibName, + "%d.db", (int)m_pRandGen->randomChoice( 100, 20000)); + + if (RC_OK( rc = dbSystem.dbCreate( m_szResultSetDibName, NULL, NULL, + NULL, NULL, &createOpts, TRUE, + (IF_Db **)&m_pResultSetDb))) + { + break; + } + if (rc == NE_XFLM_FILE_EXISTS || rc == NE_XFLM_IO_ACCESS_DENIED) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // Shouldn't have an RFL object - don't want anything + // logged by this database. + + flmAssert( !m_pResultSetDb->getDatabase()->m_pRfl); + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Close the database file and delete it. +****************************************************************************/ +RCODE F_DbCheck::closeAndDeleteResultSetDb( void) +{ + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + + if (m_pResultSetDb) + { + if (m_pResultSetDb->getTransType() != XFLM_NO_TRANS) + { + m_pResultSetDb->transAbort(); + } + m_pResultSetDb->Release(); + m_pResultSetDb = NULL; + } + + if (RC_BAD( rc = dbSystem.dbRemove( m_szResultSetDibName, NULL, NULL, TRUE))) + { + goto Exit; + } + + f_memset( m_szResultSetDibName, 0, sizeof( m_szResultSetDibName)); + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Method to get a new F_BtResultSet object. This method will create a + new collection for the result set to use before handing it off. +****************************************************************************/ +RCODE F_DbCheck::getBtResultSet( + F_BtResultSet ** ppBtRSet + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + F_BtResultSet * pBtRSet = NULL; + F_Database * pDatabase = NULL; + + // If there is already a BtResultSet, let's get rid of it. + + if (*ppBtRSet) + { + (*ppBtRSet)->Release(); + *ppBtRSet = NULL; + } + + // Create the new BtResultSet object first. + + if ((pBtRSet = f_new F_BtResultSet( m_pResultSetDb, m_pBtPool)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pDatabase = m_pResultSetDb->m_pDatabase; + + for (;;) + { + + // Now create a new collection. Randomly select a collection number to use. + + uiCollection = m_pRandGen->randomChoice( 100, XFLM_MAX_COLLECTION_NUM); + + // Check to see if it already exists. + + if (RC_OK( rc = pDatabase->lFileCreate( m_pResultSetDb, + &pBtRSet->m_Collection.lfInfo, &pBtRSet->m_Collection, uiCollection, + XFLM_LF_COLLECTION, FALSE, TRUE))) + { + break; + } + + if (rc != NE_XFLM_EXISTS) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + + *ppBtRSet = pBtRSet; + pBtRSet = NULL; + +Exit: + + if (pBtRSet) + { + pBtRSet->Release(); + } + + return rc; +} + +/**************************************************************************** +Desc : Checks for physical corruption in a FLAIM database. +DNote: The routine verifies the database by first reading through + the database to count certain block types which are in linked lists. + It then verifies the linked lists. It also verifies the B-TREEs + in the database. The reason for the first pass is so that when we + verify the linked lists, we can keep ourselves from getting into + an infinite loop if there is a loop in the lists. +****************************************************************************/ +RCODE F_DbCheck::dbCheck( + const char * pszDbFileName, + // [IN] Full path and file name of the database which + // is to be checked. NULL can be passed as the value of + // this parameter if pDb is non-NULL. + const char * pszDataDir, + // [IN] Directory for data files. + const char * pszRflDir, + // [IN] RFL directory. NULL can be passed as the value of + // this parameter to indicate that the log files are located + // in the same directory as the database or if pDb is non-NULL. + const char * pszPassword, + // [IN] Database password. Needed to open the database if the database + // key has been wrapped in a password. NULL by default. + FLMUINT uiFlags, + // [IN] Check flags. Possible flags include: + // + // XFLM_ONLINE. This flag instructs the check to repair any + // index corruptions it finds. The database must have been + // opened in read/write mode in order for the check to + // successfully repair corruptions. An update transaction + // will be started whenever a corruption is repaired. + // + // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to + // perform a logical check of the databases's indexes + // in addition to the structural check. + // + // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip + // verifying the DOM links. This check can take quite a long time + // to execute. + // + // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow + // the database to be opened in limited mode if the database key is + // wrapped in a password and the password we pass is incorrect + // (or non-existent). + IF_DbInfo ** ppDbInfo, + // [IN] Pointer to a DB_INFO structure which is used to store + // statistics collected during the database check. + IF_DbCheckStatus * pDbCheckStatus + // [IN] Status interface. Functions in this interface are called + // periodically to iform the calling application of the progress + // being made. This allows the application to monitor and/or display + // the progress of the database check. NULL may be passed as the + // value of this parameter if the callback feature is not needed. + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pBlk = NULL; + FLMUINT uiFileEnd; + FLMUINT uiBlockSize; + FLMUINT uiLoop; + FLMUINT64 ui64TmpSize; + FLMBOOL bStartOver; + FLMBOOL bOkToCloseTrans = FALSE; + FLMBOOL bAllowLimitedMode = ( uiFlags & XFLM_ALLOW_LIMITED_MODE) + ? TRUE + : FALSE; + F_DbSystem dbSystem; + + if (RC_BAD( rc = dbSystem.dbOpen( pszDbFileName, pszDataDir, + pszRflDir, pszPassword, bAllowLimitedMode, (IF_Db **)&m_pDb))) + { + goto Exit; + } + + if ((m_pDbInfo = f_new F_DbInfo) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (ppDbInfo) + { + *ppDbInfo = m_pDbInfo; + (*ppDbInfo)->AddRef(); + } + + m_pDbCheckStatus = pDbCheckStatus; + m_LastStatusRc = NE_XFLM_OK; + + // Get the file size... + + if (uiFlags & XFLM_SKIP_DOM_LINK_CHECK) + { + m_bSkipDOMLinkCheck = TRUE; + } + + // Initialize the information block and Progress structure. + + // Since we know that the check will start read transactions + // during its processing, set the flag to indicate that the KRef table + // should be cleaned up on exit if we are still in a read transaction. + + bOkToCloseTrans = TRUE; + uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); + + // Allocate memory to use for reading through the data blocks. + + if( RC_BAD( rc = f_alloc( uiBlockSize, &pBlk))) + { + goto Exit; + } + + if ((m_pBtPool = f_new F_BtPool) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = m_pBtPool->btpInit())) + { + goto Exit; + } + + // Setup the result set database. + + if ((m_pRandGen = f_new F_RandomGenerator) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + m_pRandGen->randomSetSeed( 9768); + + if (RC_BAD( rc = createAndOpenResultSetDb())) + { + goto Exit; + } + +Begin_Check: + + // Initialize all statistics in the DB_INFO structure. + + rc = NE_XFLM_OK; + bStartOver = FALSE; + + m_pDbInfo->m_ui64FileSize = 0; + m_pDbInfo->freeLogicalFiles(); + m_bPhysicalCorrupt = FALSE; + m_bIndexCorrupt = FALSE; + m_uiFlags = uiFlags; + m_bStartedUpdateTrans = FALSE; + f_memset( &m_pDbInfo->m_AvailBlocks, 0, sizeof( BLOCK_INFO)); + f_memset( &m_pDbInfo->m_LFHBlocks, 0, sizeof( BLOCK_INFO)); + + f_memset( &m_Progress, 0, sizeof( XFLM_PROGRESS_CHECK_INFO)); + + /* Get the dictionary information for the file. */ + + if (RC_BAD( rc = getDictInfo())) + { + goto Exit; + } + + m_Progress.ui64BytesExamined = 0; + + for (uiLoop = 1; + uiLoop <= MAX_DATA_BLOCK_FILE_NUMBER; + uiLoop++) + { + if (RC_BAD( m_pDb->m_pSFileHdl->GetFileSize( uiLoop, &ui64TmpSize))) + { + break; + } + + m_Progress.ui64FileSize += ui64TmpSize; + } + + // See if we have a valid end of file + + uiFileEnd = m_pDb->m_uiLogicalEOF; + if (FSGetFileOffset( uiFileEnd) % uiBlockSize != 0) + { + if (RC_BAD( rc = chkReportError( FLM_BAD_FILE_SIZE, XFLM_LOCALE_NONE, + 0, 0, 0xFF, uiFileEnd, 0, 0, 0))) + { + goto Exit; + } + } + else if (m_Progress.ui64FileSize < + FSGetSizeInBytes( m_pDb->m_pDatabase-> getMaxFileSize(), + uiFileEnd)) + { + m_Progress.ui64FileSize = + FSGetSizeInBytes( m_pDb->m_pDatabase->getMaxFileSize(), + uiFileEnd); + } + + m_pDbInfo->m_ui64FileSize = m_Progress.ui64FileSize; + + // Verify the LFH blocks, B-Trees, and the AVAIL list. + + if( RC_BAD( rc = verifyLFHBlocks( &bStartOver))) + { + goto Exit; + } + if (bStartOver) + { + goto Begin_Check; + } + + // Check the b-trees. + + if (RC_BAD( rc = verifyBTrees( &bStartOver))) + { + goto Exit; + } + if (bStartOver) + { + goto Begin_Check; + } + + // Check the avail list. + + if (RC_BAD( rc = verifyAvailList( &bStartOver))) + { + goto Exit; + } + if (bStartOver) + { + goto Begin_Check; + } + +Exit: + + if ((m_bPhysicalCorrupt || m_bIndexCorrupt) && + !F_DbSystem::_errorIsFileCorrupt( rc)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + if (RC_OK( rc) && RC_BAD( m_LastStatusRc)) + { + rc = m_LastStatusRc; + } + + if (m_pDb) + { + // Close down the transaction, if one is going + + if( bOkToCloseTrans && + m_pDb->getTransType( ) == XFLM_READ_TRANS) + { + m_pDb->krefCntrlFree(); + m_pDb->transAbort(); + } + } + + // Free memory, if allocated + + if (pBlk) + { + f_free( &pBlk); + } + + // Close the FLAIM database we opened. + + if (m_pDb) + { + m_pDb->Release(); + m_pDb = NULL; + } + + return( rc); +} + + +/*************************************************************************** +Desc: This routine opens a file and reads its dictionary into memory. +*****************************************************************************/ +RCODE F_DbCheck::getDictInfo() +{ + RCODE rc = NE_XFLM_OK; + + // Close down the transaction, if one is going. + + if (m_pDb->getTransType() != XFLM_UPDATE_TRANS) + { + if (m_pDb->getTransType() == XFLM_READ_TRANS) + { + (void)m_pDb->transAbort(); + } + + // Start a read transaction on the file to ensure we are connected + // to the file's dictionary structures. + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS, + XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE, &m_pDbInfo->m_dbHdr))) + { + goto Exit; + } + } + else + { + f_memcpy( &m_pDbInfo->m_dbHdr, m_pDb->m_pDatabase->getUncommittedDbHdr(), + sizeof( XFLM_DB_HDR)); + } + +Exit: + return( rc); +} + + +/******************************************************************** +Desc: This routine follows all of the blocks in a chain, verifying + that they are properly linked. It also verifies each block's + header. +*********************************************************************/ +RCODE F_DbCheck::verifyBlkChain( + BLOCK_INFO * pBlkInfo, + FLMUINT uiLocale, + FLMUINT uiFirstBlkAddr, + FLMUINT uiBlkType, + FLMBOOL * pbStartOverRV + ) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iVerifyCode = 0; + F_CachedBlock * pSCache = NULL; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT uiPrevBlkAddress; + FLMUINT uiBlkCount = 0; + STATE_INFO StateInfo; + FLMBOOL bStateInitialized = FALSE; + FLMUINT64 ui64SaveBytesExamined; + FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); + FLMUINT uiMaxBlocks = (FLMUINT)(FSGetSizeInBytes( + m_pDb->m_pDatabase->getMaxFileSize(), + m_pDb->m_uiLogicalEOF) / + (FLMUINT64)uiBlockSize); + + uiPrevBlkAddress = 0; + + /* There must be at least ONE block if it is the LFH chain. */ + + if ((uiBlkType == BT_LFH_BLK) && (uiFirstBlkAddr == 0)) + { + iVerifyCode = FLM_BAD_LFH_LIST_PTR; + (void)chkReportError( iVerifyCode, + uiLocale, + 0, + 0, + 0xFF, + 0, + 0, + 0, + 0); + goto Exit; + } + + /* Read through all of the blocks, verifying them as we go. */ + +Restart_Chain: + uiBlkCount = 0; + flmInitReadState( &StateInfo, + &bStateInitialized, + (FLMUINT)m_pDb->m_pDatabase-> + m_lastCommittedDbHdr.ui32DbVersion, + m_pDb, + NULL, + (FLMUINT)((uiBlkType == BT_FREE) + ? (FLMUINT)0xFF + : (FLMUINT)0), + uiBlkType, + NULL); + + ui64SaveBytesExamined = m_Progress.ui64BytesExamined; + StateInfo.ui32BlkAddress = (FLMUINT32)uiFirstBlkAddr; + + while ((StateInfo.ui32BlkAddress != 0) && (uiBlkCount < uiMaxBlocks)) + { + StateInfo.pBlkHdr = NULL; + if( RC_BAD( rc = blkRead( StateInfo.ui32BlkAddress, &pBlkHdr, + &pSCache, &iVerifyCode))) + { + if (rc == NE_XFLM_OLD_VIEW) + { + FLMUINT uiSaveDictSeq = m_pDb->m_pDict->getDictSeq(); + + if (RC_BAD( rc = getDictInfo())) + goto Exit; + + // If the dictionary ID changed, start over. + + if (m_pDb->m_pDict->getDictSeq() != uiSaveDictSeq) + { + *pbStartOverRV = TRUE; + goto Exit; + } + + m_Progress.ui64BytesExamined = ui64SaveBytesExamined; + goto Restart_Chain; + } + pBlkInfo->iErrCode = iVerifyCode; + pBlkInfo->uiNumErrors++; + rc = chkReportError( iVerifyCode, + uiLocale, + 0, + 0, + 0xFF, + StateInfo.ui32BlkAddress, + 0, + 0, + 0); + } + StateInfo.pBlkHdr = pBlkHdr; + uiBlkCount++; + m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; + if (RC_BAD( rc = chkCallProgFunc())) + { + goto Exit; + } + + f_yieldCPU(); + + if ((iVerifyCode = flmVerifyBlockHeader( &StateInfo, + pBlkInfo, + uiBlockSize, + 0xFFFFFFFF, + uiPrevBlkAddress, + TRUE)) != 0) + { + pBlkInfo->iErrCode = iVerifyCode; + pBlkInfo->uiNumErrors++; + chkReportError( iVerifyCode, + uiLocale, + 0, + 0, + 0xFF, + StateInfo.ui32BlkAddress, + 0, + 0, + 0); + goto Exit; + } + uiPrevBlkAddress = StateInfo.ui32BlkAddress; + StateInfo.ui32BlkAddress = pBlkHdr->ui32NextBlkInChain; + } + if (StateInfo.ui32BlkAddress != 0 && RC_OK( m_LastStatusRc)) + { + switch (uiBlkType) + { + case BT_LFH_BLK: + iVerifyCode = FLM_BAD_LFH_LIST_END; + break; + case BT_FREE: + iVerifyCode = FLM_BAD_AVAIL_LIST_END; + break; + } + pBlkInfo->iErrCode = iVerifyCode; + pBlkInfo->uiNumErrors++; + chkReportError( iVerifyCode, + uiLocale, + 0, + 0, + 0xFF, + uiPrevBlkAddress, + 0, + 0, + 0); + goto Exit; + } + +Exit: + + if( pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if( pBlkHdr) + { + f_free( &pBlkHdr); + } + + if (RC_OK(rc) && (iVerifyCode != 0)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + return( rc); +} + + +/*************************************************************************** +Desc: This routine verifies the LFH blocks. +*****************************************************************************/ +RCODE F_DbCheck::verifyLFHBlocks( + FLMBOOL * pbStartOverRV) +{ + RCODE rc = NE_XFLM_OK; + + m_Progress.uiLfNumber = 0; + m_Progress.uiLfType = 0; + m_Progress.iCheckPhase = XFLM_CHECK_LFH_BLOCKS; + m_Progress.bStartFlag = TRUE; + if (RC_BAD( rc = chkCallProgFunc())) + { + goto Exit; + } + m_Progress.bStartFlag = FALSE; + + f_yieldCPU(); + + // Go through the LFH blocks. + + if (RC_BAD( rc = verifyBlkChain( + &m_pDbInfo->m_LFHBlocks, XFLM_LOCALE_LFH_LIST, + (FLMUINT)m_pDb->m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr, + BT_LFH_BLK, pbStartOverRV)) || + *pbStartOverRV) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads through the blocks in the AVAIL list and verifies + that we don't have a loop or some other corruption in the list. +*****************************************************************************/ +RCODE F_DbCheck::verifyAvailList( + FLMBOOL * pbStartOverRV) +{ + RCODE rc = NE_XFLM_OK; + + m_Progress.uiLfNumber = 0; + m_Progress.uiLfType = 0; + m_Progress.iCheckPhase = XFLM_CHECK_AVAIL_BLOCKS; + m_Progress.bStartFlag = TRUE; + if (RC_BAD( rc = chkCallProgFunc())) + { + goto Exit; + } + m_Progress.bStartFlag = FALSE; + + f_yieldCPU(); + + if (RC_BAD( rc = verifyBlkChain( &m_pDbInfo->m_AvailBlocks, + XFLM_LOCALE_AVAIL_LIST, + m_pDb->m_uiFirstAvailBlkAddr, + BT_FREE, pbStartOverRV)) || + *pbStartOverRV) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next B-Tree information that was collected during the + database check. +****************************************************************************/ +void XFLMAPI F_DbInfo::getBTreeInfo( + FLMUINT uiNthLogicalFile, + FLMUINT * puiLfNum, + eLFileType * peLfType, + FLMUINT * puiRootBlkAddress, + FLMUINT * puiNumLevels + ) +{ + LF_HDR * pLfHdr; + + if (uiNthLogicalFile < m_uiNumLogicalFiles) + { + pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile]; + *puiLfNum = pLfHdr->uiLfNum; + *peLfType = pLfHdr->eLfType; + *puiRootBlkAddress = pLfHdr->uiRootBlk; + *puiNumLevels = pLfHdr->uiNumLevels; + } + else + { + flmAssert( 0); + *puiLfNum = 0; + *puiRootBlkAddress = 0; + *puiNumLevels = 0; + } +} + +/**************************************************************************** +Desc: Returns block information on the specified b-tree and level within + that b-tree. +****************************************************************************/ +void XFLMAPI F_DbInfo::getBTreeBlockStats( + FLMUINT uiNthLogicalFile, + FLMUINT uiLevel, + FLMUINT64 * pui64KeyCount, + FLMUINT64 * pui64BytesUsed, + FLMUINT64 * pui64ElementCount, + FLMUINT64 * pui64ContElementCount, + FLMUINT64 * pui64ContElmBytes, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors + ) +{ + LF_HDR * pLfHdr; + + if (uiNthLogicalFile < m_uiNumLogicalFiles && + uiLevel < m_pLogicalFiles [uiNthLogicalFile].uiNumLevels) + { + pLfHdr = &m_pLogicalFiles[ uiNthLogicalFile]; + *pui64KeyCount = pLfHdr->pLevelInfo [uiLevel].ui64KeyCount; + *pui64BytesUsed = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64BytesUsed; + *pui64ElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ElementCount; + *pui64ContElementCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElementCount; + *pui64ContElmBytes = pLfHdr->pLevelInfo [uiLevel].BlockInfo.ui64ContElmBytes; + *puiBlockCount = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiBlockCount; + *piLastError = pLfHdr->pLevelInfo [uiLevel].BlockInfo.iErrCode; + *puiNumErrors = pLfHdr->pLevelInfo [uiLevel].BlockInfo.uiNumErrors; + } + else + { + flmAssert( 0); + *pui64KeyCount = 0; + *pui64BytesUsed = 0; + *pui64ElementCount = 0; + *pui64ContElementCount = 0; + *pui64ContElmBytes = 0; + *puiBlockCount = 0; + *piLastError = 0; + *puiNumErrors = 0; + } +} + +/**************************************************************************** +Desc: Checks for physical corruption in a FLAIM database. +Note: The routine verifies the database by first reading through + the database to count certain block types which are in linked lists. + It then verifies the linked lists. It also verifies the B-TREEs + in the database. The reason for the first pass is so that when we + verify the linked lists, we can keep ourselves from getting into + an infinite loop if there is a loop in the lists. +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::dbCheck( + const char * pszDbFileName, + // [IN] Full path and file name of the database which + // is to be checked. NULL can be passed as the value of + // this parameter if pDb is non-NULL. + const char * pszDataDir, + // [IN] Directory for data files. + const char * pszRflDir, + // [IN] RFL directory. NULL can be passed as the value of + // this parameter to indicate that the log files are located + // in the same directory as the database or if pDb is non-NULL. + const char * pszPassword, + // [IN] Database password. This is necessary to open the database if + // the key has been wrapped in a password. NULL by default. + FLMUINT uiFlags, + // [IN] Check flags. Possible flags include: + // + // XFLM_ONLINE. This flag instructs the check to repair any + // index corruptions it finds. The database must have been + // opened in read/write mode in order for the check to + // successfully repair corruptions. An update transaction + // will be started whenever a corruption is repaired. + // + // XFLM_DO_LOGICAL_CHECK. This flag instructs the check to + // perform a logical check of the databases's indexes + // in addition to the structural check. + // + // XFLM_SKIP_DOM_LINK_CHECK. This flag instructs the check to skip + // verifying the DOM links. This check can take quite a long time + // to execute. + // + // XFLM_ALLOW_LIMITED_MODE. This flag instructs the check to allow + // the database to be opened in limited mode if the database key is + // wrapped in a password and the password we pass is incorrect + // (or non-existent). + IF_DbInfo ** ppDbInfo, + // [IN] Pointer to a DB_INFO structure which is used to store + // statistics collected during the database check. + IF_DbCheckStatus * pDbCheckStatus + // [IN] Status interface. Functions in this interface are called + // periodically to iform the calling application of the progress + // being made. This allows the application to monitor and/or display + // the progress of the database check. NULL may be passed as the + // value of this parameter if the callback feature is not needed. + ) +{ + RCODE rc = NE_XFLM_OK; + F_DbCheck * pCheckObj = NULL; + + if ((pCheckObj = f_new F_DbCheck) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + rc = pCheckObj->dbCheck( pszDbFileName, pszDataDir, pszRflDir, pszPassword, + uiFlags, ppDbInfo, pDbCheckStatus); + +Exit: + + if (pCheckObj) + { + pCheckObj->Release(); + } + return( rc); +} + diff --git a/version5/src/flchkix.cpp b/version5/src/flchkix.cpp new file mode 100644 index 0000000..917862f --- /dev/null +++ b/version5/src/flchkix.cpp @@ -0,0 +1,1028 @@ +//------------------------------------------------------------------------------ +// Desc: Check logical integrity of indexes. +// +// 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: flchkix.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/******************************************************************** +Desc: This routine builds a key tree from a collated key +********************************************************************/ +RCODE F_DbCheck::keyToVector( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + IF_DataVector ** ppKeyRV + ) +{ + RCODE rc = NE_XFLM_OK; + + // Generate the key tree + + if ((*ppKeyRV = f_new F_DataVector) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + (*ppKeyRV)->reset(); + + rc = (*ppKeyRV)->inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Retrieves the next key from the sorted result set +*********************************************************************/ +RCODE F_DbCheck::chkGetNextRSKey( void) +{ + RCODE rc = NE_XFLM_OK; + RS_IX_KEY * pCurrRSKey; + + // Swap current and last key pointers - this allows us to always keep + // the last key without having to memcpy the keys. + + pCurrRSKey = m_pCurrRSKey; + m_pCurrRSKey = m_pPrevRSKey; + m_pPrevRSKey = pCurrRSKey; + pCurrRSKey = m_pCurrRSKey; + + if (pCurrRSKey == NULL) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // Get the next key - call getFirst because we are deleting the + // entry after we process it. + + if (RC_BAD( rc = m_pIxRSet->getFirst( m_pDb, m_pIxd, NULL, + pCurrRSKey->pucRSKeyBuf, + MAX_KEY_SIZ, + &pCurrRSKey->uiRSKeyLen, + pCurrRSKey->pucRSDataBuf, + MAX_KEY_SIZ, + &pCurrRSKey->uiRSDataLen))) + { + goto Exit; + } + + // Verify that the key meets the minimum length requirements + + flmAssert( pCurrRSKey->uiRSKeyLen); + +Exit: + + return( rc); + +} + +/******************************************************************** +Desc: Verifies the current index key against the result set. +*********************************************************************/ +RCODE F_DbCheck::verifyIXRSet( + STATE_INFO * pStateInfo) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iCmpVal = 0; + FLMUINT uiIteration = 0; + FLMBOOL bRSetEmpty = FALSE; + RS_IX_KEY * pCurrRSKey; + RS_IX_KEY * pPrevRSKey; + + if (!m_pCurrRSKey) + { + m_pCurrRSKey = &m_IxKey1; + m_pPrevRSKey = &m_IxKey2; + } + + // Compare index and result set keys + + while (!bRSetEmpty) + { + if (m_bGetNextRSKey) + { + + // Get the next key from the result set. If the result set + // is empty, then m_uiRSKeyLen will be set to + // zero, forcing the problem to be resolved below. + + if (RC_BAD( rc = chkGetNextRSKey())) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + + // Set bRSetEmpty to TRUE so that the loop will exit after the + // current key is resolved. Otherwise, conflict resolution on + // the current key will be repeated forever (infinite loop). + + bRSetEmpty = TRUE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + + // Update statistics + + m_Progress.ui64NumKeysExamined++; + } + } + pCurrRSKey = m_pCurrRSKey; + pPrevRSKey = m_pPrevRSKey; + + if (pCurrRSKey->uiRSKeyLen == 0 || bRSetEmpty) + { + + // We don't have a key because we got an EOF when + // reading the result set. Need to resolve the + // fact that the result set does not have a key + // that is found in the index. Set iCmpVal to + // 1 to force this resolution. + + iCmpVal = 1; + } + else + { + + // Compare the index key and result set key. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, NULL, NULL, NULL, + TRUE, TRUE, + pCurrRSKey->pucRSKeyBuf, + pCurrRSKey->uiRSKeyLen, + pStateInfo->pucElmKey, + pStateInfo->uiElmKeyLen, &iCmpVal))) + { + goto Exit; + } + } + + if (iCmpVal < 0) + { + + // The result set key is less than the index key. This could mean + // that the result set key needs to be added to the index. + + if ( RC_BAD( rc = resolveIXMissingKey( pStateInfo))) + { + + // If the key was added to the index (bReposition == TRUE) + // or we got some other error, we don't want to get the next + // result set key. + + m_bGetNextRSKey = (bRSetEmpty ? TRUE : FALSE); + goto Exit; + } + else + { + + // False alarm. The index is missing the key because of + // a concurrent update. We want to get the next RS key. + + m_bGetNextRSKey = TRUE; + + // Delete the current key in the result set so we don't hit it again. + + if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd, + pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen))) + { + goto Exit; + } + } + } + else if (iCmpVal > 0) + { + + // The result set key is greater than the index key. This could mean + // that the index key needs to be deleted from the index. Whether we + // delete the index key or not, we don't need to get the next result + // set key, but we do want to reposition and get the next index key. + + m_bGetNextRSKey = (bRSetEmpty ? TRUE : FALSE); + if ( RC_BAD( rc = resolveRSetMissingKey( pStateInfo))) + { + goto Exit; + } + break; + } + else + { + + // The index and result set keys are equal. We want to get + // the next result set key. + + m_bGetNextRSKey = TRUE; + + // Delete the current key in the result set so we don't hit it again. + + if (RC_BAD( rc = m_pIxRSet->deleteEntry( m_pDb, m_pIxd, + pCurrRSKey->pucRSKeyBuf, pCurrRSKey->uiRSKeyLen))) + { + goto Exit; + } + + break; + } + + // Call the yield function periodically + + uiIteration++; + if (!(uiIteration & 0x1F) ) + { + f_yieldCPU(); + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Resolves the case of a key found in the result set but not in + the current index. +*********************************************************************/ +RCODE F_DbCheck::resolveIXMissingKey( + STATE_INFO * pStateInfo) +{ + FLMBOOL bKeyInDoc; + FLMBOOL bKeyInIndex; + RCODE rc = NE_XFLM_OK; + FLMBOOL bFixCorruption = FALSE; + RS_IX_KEY * pCurrRSKey = m_pCurrRSKey; + XFLM_INDEX_STATUS ixStatus; + + // Determine if the record generates the key and if the + // key is found in the index. + + if (RC_BAD( rc = getKeySource( pCurrRSKey->pucRSKeyBuf, + pCurrRSKey->uiRSKeyLen, + &bKeyInDoc, &bKeyInIndex))) + { + if (rc == NE_XFLM_INDEX_OFFLINE) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // If the record does not generate the key or the key+ref is in the index, + // the index is not corrupt. + + if (!bKeyInDoc || bKeyInIndex) + { + m_Progress.ui64NumConflicts++; + goto Exit; + } + + // Otherwise, the index is corrupt. + + // Update statistics + + m_Progress.ui64NumDocKeysNotFound++; + m_pDbInfo->m_uiLogicalCorruptions++; + + // Report the error + + if (RC_BAD( rc = reportIxError( pStateInfo, + FLM_KEY_NOT_IN_KEY_REFSET, + pCurrRSKey->pucRSKeyBuf, + pCurrRSKey->uiRSKeyLen, + &bFixCorruption))) + { + goto Exit; + } + + // Exit if the application does not want to repair the corruption. + + if (!bFixCorruption) + { + + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + goto Exit; + } + + // Fix the corruption + + if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus))) + { + goto Exit; + } + + if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 && + ixStatus.eState != XFLM_INDEX_SUSPENDED) + { + + // Update statistics + + m_pDbInfo->m_uiLogicalRepairs++; + + // Add the key + + if (RC_OK( rc = addDelKeyRef( pCurrRSKey->pucRSKeyBuf, + pCurrRSKey->uiRSKeyLen, + FALSE))) + { + goto Exit; + } + else + { + + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + } + } + else + { + + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Resolves the case of a key found in the current index but not + in the result set. +*********************************************************************/ +RCODE F_DbCheck::resolveRSetMissingKey( + STATE_INFO * pStateInfo) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bKeyInDoc; + FLMBOOL bKeyInIndex; + FLMBOOL bFixCorruption = FALSE; + XFLM_INDEX_STATUS ixStatus; + + // See if the key is found in the index and/or generated + // by the document. + + if (RC_BAD( rc = getKeySource( pStateInfo->pucElmKey, + pStateInfo->uiElmKeyLen, + &bKeyInDoc, &bKeyInIndex))) + { + if (rc == NE_XFLM_INDEX_OFFLINE) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // If the key is generated by the record or the key is not found + // in the index, the index is not corrupt. + + if (bKeyInDoc || !bKeyInIndex) + { + m_Progress.ui64NumConflicts++; + goto Exit; + } + + // Otherwise, the index is corrupt. + + // Update statistics + + m_Progress.ui64NumKeysNotFound++; + m_pDbInfo->m_uiLogicalCorruptions++; + + // Report the error + + if (RC_BAD( rc = reportIxError( pStateInfo, + FLM_IX_KEY_NOT_FOUND_IN_REC, + pStateInfo->pucElmKey, + pStateInfo->uiElmKeyLen, + &bFixCorruption))) + { + goto Exit; + } + + // Exit if the application does not want to repair the corruption. + + if (!bFixCorruption) + { + + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + goto Exit; + } + + // Fix the corruption + + if (RC_BAD( rc = m_pDb->indexStatus( m_pIxd->uiIndexNum, &ixStatus))) + { + goto Exit; + } + + if (ixStatus.ui64LastDocumentIndexed == (FLMUINT64)~0 && + ixStatus.eState != XFLM_INDEX_SUSPENDED) + { + + // Update statistics + + m_pDbInfo->m_uiLogicalRepairs++; + + // Delete the reference from the index + + if (RC_BAD( rc = addDelKeyRef( pStateInfo->pucElmKey, + pStateInfo->uiElmKeyLen, + TRUE))) + { + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + } + goto Exit; + } + else + { + + // Set the logical corruption flag + + m_bIndexCorrupt = TRUE; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Verifies that a key component is actually in the document. +*********************************************************************/ +RCODE F_DbCheck::verifyComponentInDoc( + ICD * pIcd, + FLMUINT uiComponent, + F_DataVector * pKey, + FLMBOOL * pbInDoc + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDOMNode = NULL; + FLMUINT64 ui64NodeId; + FLMUINT uiNameId; + + // Get the nodeId. + + if ((ui64NodeId = pKey->getID( uiComponent)) != 0) + { + if (RC_BAD( rc = m_pDb->getNode( m_pIxd->uiCollectionNum, ui64NodeId, + &pDOMNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + *pbInDoc = FALSE; + goto Exit; + } + + // No need to verify the name ID if it is an element root tag. + + if( pIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + if( RC_BAD( rc = pDOMNode->hasAttribute( m_pDb, pIcd->uiDictNum))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + + *pbInDoc = FALSE; + goto Exit; + } + } + else if( pIcd->uiDictNum != ELM_ROOT_TAG) + { + if (RC_BAD( rc = pDOMNode->getNameId( m_pDb, &uiNameId))) + { + goto Exit; + } + + if (uiNameId != pIcd->uiDictNum) + { + *pbInDoc = FALSE; + goto Exit; + } + } + + // Make sure these are the same type. + + if ((pKey->isAttr( uiComponent) && !(pIcd->uiFlags & ICD_IS_ATTRIBUTE)) || + (!pKey->isAttr( uiComponent) && (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + goto Exit; + } + + // Verify that the node belongs to the document. + + if (pKey->getDocumentID() != pDOMNode->getDocumentId()) + { + *pbInDoc = FALSE; + goto Exit; + } + } + +Exit: + + if (pDOMNode) + { + pDOMNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: Determines if a key is generated by the current document + and/or if the key is found in the current index +*********************************************************************/ +RCODE F_DbCheck::getKeySource( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbKeyInDoc, + FLMBOOL * pbKeyInIndex + ) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd; + FLMUINT uiComponent; + F_DataVector key; + + // Initialize return values. + + *pbKeyInDoc = FALSE; + *pbKeyInIndex = FALSE; + + if (m_pIxd->uiFlags & IXD_OFFLINE) + { + rc = RC_SET( NE_XFLM_INDEX_OFFLINE); + goto Exit; + } + + // See if the key is in the index. + + if (RC_BAD( rc = chkVerifyKeyExists( pucKey, uiKeyLen, pbKeyInIndex))) + { + goto Exit; + } + + // Put the key into a data vector structure. + + if (RC_BAD( rc = key.inputKey( m_pDb, m_pIxd->uiIndexNum, pucKey, uiKeyLen))) + { + goto Exit; + } + + // See if all of the nodes referenced from the key actually are in the + // document. This includes data nodes and context nodes. + + *pbKeyInDoc = TRUE; + uiComponent = 0; + pIcd = m_pIxd->pFirstKey; + while (pIcd) + { + if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) + { + goto Exit; + } + if (!(*pbKeyInDoc)) + { + goto Exit; + } + uiComponent++; + pIcd = pIcd->pNextKeyComponent; + } + + // Go through data components. + + pIcd = m_pIxd->pFirstData; + while (pIcd) + { + if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) + { + goto Exit; + } + if (!(*pbKeyInDoc)) + { + goto Exit; + } + uiComponent++; + pIcd = pIcd->pNextDataComponent; + } + + // Go through context components + + pIcd = m_pIxd->pFirstContext; + while (pIcd) + { + if (RC_BAD( rc = verifyComponentInDoc( pIcd, uiComponent, &key, pbKeyInDoc))) + { + goto Exit; + } + if (!(*pbKeyInDoc)) + { + goto Exit; + } + uiComponent++; + pIcd = pIcd->pNextKeyComponent; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Verify that a key is (or is not) found in an index. +*********************************************************************/ +RCODE F_DbCheck::chkVerifyKeyExists( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL * pbFoundRV) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pbtree = NULL; + IXKeyCompare compareObject; + + compareObject.setIxInfo( m_pDb, m_pIxd); + compareObject.setCompareNodeIds( TRUE); + compareObject.setCompareDocId( TRUE); + + *pbFoundRV = FALSE; + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + + if( RC_BAD( rc = pbtree->btOpen( m_pDb, &m_pIxd->lfInfo, + (m_pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, FALSE, + &compareObject))) + { + goto Exit; + } + + if( RC_BAD( rc = pbtree->btLocateEntry( + pucKey, uiKeyLen, &uiKeyLen, XFLM_EXACT))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + *pbFoundRV = TRUE; + +Exit: + + if (pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + return( rc ); +} + +/*************************************************************************** +Desc: This routine adds or deletes an index key and/or reference. +*****************************************************************************/ +RCODE F_DbCheck::addDelKeyRef( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bDelete) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKeyBuf[ sizeof( KREF_ENTRY) + MAX_KEY_SIZ]; + KREF_ENTRY * pKrefEntry = (KREF_ENTRY *)(&ucKeyBuf[ 0]); + FLMBOOL bStartedUpdate = FALSE; + FLMBOOL bKeyInDoc; + FLMBOOL bKeyInIndex; + + // Start an update transaction, if necessary + + if (RC_BAD( rc = startUpdate())) + { + goto Exit; + } + bStartedUpdate = TRUE; + + // Verify that the state has not changed + + if (RC_BAD( rc = getKeySource( pucKey, uiKeyLen, + &bKeyInDoc, &bKeyInIndex))) + { + goto Exit; + } + + if ((bKeyInIndex && bDelete) || (!bKeyInIndex && !bDelete)) + { + // Setup the KrefEntry structure + + f_memcpy( &(ucKeyBuf[ sizeof( KREF_ENTRY)]), pucKey, uiKeyLen); + pKrefEntry->ui16KeyLen = (FLMUINT16)uiKeyLen; + pKrefEntry->uiDataLen = 0; + pKrefEntry->ui16IxNum = (FLMUINT16)m_pIxd->uiIndexNum; + pKrefEntry->uiSequence = 1; + pKrefEntry->bDelete = bDelete; + + // Add or delete the key/reference. + + if (RC_BAD( rc = m_pDb->refUpdate( &m_pIxd->lfInfo, m_pIxd, pKrefEntry, + FALSE))) + { + goto Exit; + } + + // Update statistics + + m_Progress.uiNumProblemsFixed++; + } + +Exit: + + // End the update + + if (bStartedUpdate) + { + RCODE rc2; + + if (RC_BAD( rc2 = chkEndUpdate())) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + + return( rc); +} + +/******************************************************************** +Desc: Populates the XFLM_CORRUPT_INFO structure and calls the user's + callback routine. +*********************************************************************/ +RCODE F_DbCheck::reportIxError( + STATE_INFO * pStateInfo, + FLMINT iErrCode, + FLMBYTE * pucErrKey, + FLMUINT uiErrKeyLen, + FLMBOOL * pbFixErrRV + ) +{ + RCODE rc = NE_XFLM_OK; + void * pDbPoolMark = NULL; + FLMBOOL bResetKRef = FALSE; + XFLM_CORRUPT_INFO CorruptInfo; + + f_memset( &CorruptInfo, 0, sizeof( XFLM_CORRUPT_INFO)); + + // Need to mark the DB's temporary pool. The index code allocates + // memory for new CDL entries from the DB pool. If the pool is not + // reset, it grows during the check and becomes VERY large. + + pDbPoolMark = m_pDb->m_TempPool.poolMark(); + + // Set up the KRef so that flmGetRecKeys will work + + if (RC_BAD( rc = m_pDb->krefCntrlCheck())) + { + goto Exit; + } + bResetKRef = TRUE; + + // Fix corruptions by default unless the app says not to. + + CorruptInfo.uiErrLocale = XFLM_LOCALE_INDEX; + CorruptInfo.iErrCode = iErrCode; + CorruptInfo.uiErrLfNumber = m_pIxd->uiIndexNum; + CorruptInfo.uiErrElmOffset = pStateInfo->uiElmOffset; + + // Generate the key tree using the key that caused the error + + if (RC_BAD( rc = keyToVector( pucErrKey, uiErrKeyLen, &CorruptInfo.ifpErrIxKey))) + { + goto Exit; + } + + // Report the error + + *pbFixErrRV = FALSE; + if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) + { + m_LastStatusRc = m_pDbCheckStatus->reportCheckErr( &CorruptInfo, pbFixErrRV); + } + +Exit: + + if (CorruptInfo.ifpErrIxKey) + { + CorruptInfo.ifpErrIxKey->Release(); + CorruptInfo.ifpErrIxKey = NULL; + } + + // Remove any keys added to the KRef + + if (bResetKRef) + { + m_pDb->krefCntrlFree(); // VISIT: Is this correct? + } + + // Reset the index check pool + + m_pDb->m_TempPool.poolReset(pDbPoolMark); + + return( rc); +} + +/*************************************************************************** +Desc: Start an update transaction +*****************************************************************************/ +RCODE F_DbCheck::startUpdate( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bAbortedReadTrans = FALSE; + FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum; + + // This routine should never be called unless + // XFLM_ONLINE flag was passed in to the check. + + flmAssert( m_uiFlags & XFLM_ONLINE); + + if (m_pDb->getTransType() == XFLM_READ_TRANS) + { + + // Free the KrefCntrl + + m_pDb->krefCntrlCheck(); + + // Abort the read transaction + + m_pIxd = NULL; + if (RC_BAD( rc = m_pDb->transAbort())) + { + goto Exit; + } + bAbortedReadTrans = TRUE; + + // Try to start an update transaction + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT, + XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + m_bStartedUpdateTrans = TRUE; + + // Must reget the IXD. + + if (RC_BAD( rc = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, + &m_pIxd, TRUE))) + { + goto Exit; + } + } + + if (RC_BAD( m_LastStatusRc)) + { + rc = m_LastStatusRc; + goto Exit; + } + +Exit: + + // If something went wrong after the update transaction was started, + // abort the transaction. + + if (RC_BAD( rc)) + { + if (m_bStartedUpdateTrans) + { + m_pDb->transAbort(); + m_bStartedUpdateTrans = FALSE; + } + } + + // Re-start the read transaction. + + if (bAbortedReadTrans && !m_bStartedUpdateTrans) + { + RCODE rc2; + + m_pIxd = NULL; + if (RC_BAD( rc2 = m_pDb->transBegin( XFLM_READ_TRANS, XFLM_NO_TIMEOUT, + XFLM_DONT_POISON_CACHE))) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + else + { + if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, + &m_pIxd, TRUE))) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + } + + return( rc); +} + +/*************************************************************************** +Desc: End an update transaction. +*****************************************************************************/ +RCODE F_DbCheck::chkEndUpdate( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSaveIndexNum = m_pIxd->uiIndexNum; + + if (m_bStartedUpdateTrans) + { + + // Commit the update transaction that was started. If the transaction + // started by the application, do not commit it. + + m_pIxd = NULL; + m_bStartedUpdateTrans = FALSE; + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + // Re-start read transaction + + if (m_pDb->getTransType() == XFLM_NO_TRANS) + { + RCODE rc2; + + if (RC_BAD( rc2 = m_pDb->transBegin( + XFLM_READ_TRANS, XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + else + { + if (RC_BAD( rc2 = m_pDb->getDict()->getIndex( uiSaveIndexNum, &m_pLFile, + &m_pIxd, TRUE))) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + } + + return( rc); +} + diff --git a/version5/src/flchktr.cpp b/version5/src/flchktr.cpp new file mode 100644 index 0000000..06b7cec --- /dev/null +++ b/version5/src/flchktr.cpp @@ -0,0 +1,277 @@ +//------------------------------------------------------------------------------ +// Desc: Check b-trees for physical integrity. +// +// Tabs: 3 +// +// Copyright (c) 1991-1992, 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: flchktr.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE chkReadBlkFromDisk( + F_Db * pDb, + F_Database * pDatabase, + XFLM_DB_HDR * pDbHdr, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiFilePos, + F_BLK_HDR * pBlkHdr); + +/******************************************************************** +Desc: Read a block - try cache first, then disk if not in cache. +*********************************************************************/ +RCODE F_DbCheck::blkRead( + FLMUINT uiBlkAddress, + F_BLK_HDR ** ppBlkHdr, + F_CachedBlock ** ppSCache, + FLMINT * piBlkErrCodeRV) +{ + RCODE rc = NE_XFLM_OK; + + if (*ppSCache) + { + ScaReleaseCache( *ppSCache, FALSE); + *ppSCache = NULL; + *ppBlkHdr = NULL; + } + else if (*ppBlkHdr) + { + f_free( ppBlkHdr); + *ppBlkHdr = NULL; + } + + if (m_pDb->m_uiKilledTime) + { + rc = RC_SET( NE_XFLM_OLD_VIEW); + goto Exit; + } + + // Get the block from cache. + + if (RC_OK( rc = m_pDb->m_pDatabase->getBlock( m_pDb, NULL, + uiBlkAddress, NULL, ppSCache))) + { + *ppBlkHdr = (*ppSCache)->getBlockPtr(); + } + else + { + // Try to read the block directly from disk. + + FLMUINT uiBlkSize = m_pDb->m_pDatabase->m_uiBlockSize; + FLMUINT64 ui64TransID; + F_BLK_HDR * pBlkHdr; + FLMUINT64 ui64LastReadTransID; + FLMUINT uiPrevBlkAddr; + FLMUINT uiFilePos; + + // If we didn't get a corruption error, jump to exit. + + if( !F_DbSystem::_errorIsFileCorrupt( rc)) + { + goto Exit; + } + + // Allocate memory for the block. + + if( RC_BAD( rc = f_calloc( uiBlkSize, ppBlkHdr))) + { + goto Exit; + } + + pBlkHdr = *ppBlkHdr; + uiFilePos = uiBlkAddress; + ui64TransID = m_pDb->m_ui64CurrTransID; + ui64LastReadTransID = ~((FLMUINT64)0); + + // Follow version chain until we find version we need. + + for (;;) + { + if (RC_BAD( rc = chkReadBlkFromDisk( + m_pDb, + m_pDb->m_pDatabase, + &m_pDb->m_pDatabase->m_lastCommittedDbHdr, + m_pDb->m_pSFileHdl, + uiFilePos, pBlkHdr))) + { + goto Exit; + } + + // See if we can use the current version of the block, or if we + // must go get a previous version. + + if (pBlkHdr->ui64TransID <= ui64TransID) + { + break; + } + + // If the transaction ID is greater than or equal to the last + // one we read, we have a corruption. This test will keep us + // from looping around forever. + + if (pBlkHdr->ui64TransID >= ui64LastReadTransID) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + ui64LastReadTransID = pBlkHdr->ui64TransID; + + // Block is too new, go for next older version. + + // If previous block address is same as current file position or + // zero, we have a problem. + + uiPrevBlkAddr = (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr; + if (uiPrevBlkAddr == uiFilePos || !uiPrevBlkAddr) + { + rc = (m_pDb->m_uiKilledTime) + ? RC_SET( NE_XFLM_OLD_VIEW) + : RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + uiFilePos = uiPrevBlkAddr; + } + + // See if we even got the block we thought we wanted. + + if ((FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddress) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + +Exit: + + *piBlkErrCodeRV = 0; + if (RC_BAD( rc)) + { + switch (rc) + { + case NE_XFLM_DATA_ERROR: + *piBlkErrCodeRV = FLM_COULD_NOT_SYNC_BLK; + break; + case NE_XFLM_BLOCK_CRC: + *piBlkErrCodeRV = FLM_BAD_BLK_CHECKSUM; + break; + } + } + return( rc); +} + +/************************************************************************ +Desc: Read a block from disk +*************************************************************************/ +FSTATIC RCODE chkReadBlkFromDisk( + F_Db * pDb, + F_Database * pDatabase, + XFLM_DB_HDR * pDbHdr, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiFilePos, + F_BLK_HDR * pBlkHdr + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + FLMUINT uiBlkSize = (FLMUINT)pDbHdr->ui16BlockSize; + F_Dict * pDict; + + if (RC_BAD( rc = pSFileHdl->ReadBlock( uiFilePos, uiBlkSize, + (FLMBYTE *)pBlkHdr, &uiBytesRead))) + { + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (uiBytesRead < uiBlkSize) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if (RC_BAD( rc = flmPrepareBlockForUse( uiBlkSize, pBlkHdr))) + { + goto Exit; + } + + // Decrypt the block if encrypted + + if (RC_BAD( rc = pDb->getDictionary( &pDict))) + { + goto Exit; + } + + if (RC_BAD( rc = pDatabase->decryptBlock( pDict, (FLMBYTE *)pBlkHdr))) + { + goto Exit; + } + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Report an error +*********************************************************************/ +RCODE F_DbCheck::chkReportError( + FLMINT iErrCode, + FLMUINT uiErrLocale, + FLMUINT uiErrLfNumber, + FLMUINT uiErrLfType, + FLMUINT uiErrBTreeLevel, + FLMUINT uiErrBlkAddress, + FLMUINT uiErrParentBlkAddress, + FLMUINT uiErrElmOffset, + FLMUINT64 ui64ErrNodeId) +{ + XFLM_CORRUPT_INFO CorruptInfo; + FLMBOOL bFixErr; + + CorruptInfo.iErrCode = iErrCode; + CorruptInfo.uiErrLocale = uiErrLocale; + CorruptInfo.uiErrLfNumber = uiErrLfNumber; + CorruptInfo.uiErrLfType = uiErrLfType; + CorruptInfo.uiErrBTreeLevel = uiErrBTreeLevel; + CorruptInfo.uiErrBlkAddress = uiErrBlkAddress; + CorruptInfo.uiErrParentBlkAddress = uiErrParentBlkAddress; + CorruptInfo.uiErrElmOffset = uiErrElmOffset; + CorruptInfo.ui64ErrNodeId = ui64ErrNodeId; + CorruptInfo.ifpErrIxKey = NULL; + + if (m_pDbCheckStatus && RC_OK( m_LastStatusRc)) + { + bFixErr = FALSE; + m_LastStatusRc = m_pDbCheckStatus->reportCheckErr( &CorruptInfo, &bFixErr); + } + + if (iErrCode != FLM_OLD_VIEW) + { + m_bPhysicalCorrupt = TRUE; + m_uiFlags &= ~XFLM_DO_LOGICAL_CHECK; + } + + flmAssert( 0); + return( m_LastStatusRc); +} + diff --git a/version5/src/flclose.cpp b/version5/src/flclose.cpp new file mode 100644 index 0000000..d2e88bd --- /dev/null +++ b/version5/src/flclose.cpp @@ -0,0 +1,226 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the destructor for the F_Db object. +// +// Tabs: 3 +// +// Copyright (c) 1990-1992, 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: flclose.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Destructor for F_Db object +****************************************************************************/ +F_Db::~F_Db() +{ + if( m_eTransType != XFLM_NO_TRANS) + { + // Someone forgot to close their transaction! + + RC_UNEXPECTED_ASSERT( NE_XFLM_TRANS_ACTIVE); + transAbort(); + } + + // Free the super file. + + if (m_pSFileHdl) + { + // Opened files will be released back to the + // file handle manager + + m_pSFileHdl->Release(); + } + + // Free up statistics. + + if (m_bStatsInitialized) + { + flmStatFree( &m_Stats); + } + + // Return the cached B-Tree (if any) to the + // global pool + + if( m_pCachedBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pCachedBTree); + } + + if( m_pKrefTbl) + { + f_free( &m_pKrefTbl); + m_uiKrefTblSize = 0; + } + + if( m_pucKrefKeyBuf) + { + f_free( &m_pucKrefKeyBuf); + } + + if (m_pKeyColl) + { + m_pKeyColl->Release(); + } + + if (m_pIxClient) + { + m_pIxClient->Release(); + } + + if (m_pIxStatus) + { + m_pIxStatus->Release(); + } + + if (m_pDeleteStatus) + { + m_pDeleteStatus->Release(); + } + + if (m_pCommitClient) + { + m_pCommitClient->Release(); + } + + if (m_pOldNodeList) + { + m_pOldNodeList->Release(); + } + + if (m_hWaitSem != F_SEM_NULL) + { + f_semDestroy( &m_hWaitSem); + } + + // Unlink the F_Db from the F_Database and F_Dict structures. + // IMPORTANT NOTE: The call to unlinkFromDatabase needs to + // be the last thing that is done, because it may unlock + // and relock the mutex. + + if (m_pDatabase) + { + m_pDatabase->lockMutex(); + unlinkFromDict(); + m_pDatabase->unlockMutex(); + + f_mutexLock( gv_XFlmSysData.hShareMutex); + unlinkFromDatabase(); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: Wait for a specific database to close +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::waitToClose( + const char * pszDbPath) +{ + RCODE rc = NE_XFLM_OK; + FBUCKET * pBucket; + FLMUINT uiBucket; + F_Database * pDatabase = NULL; + char szDbPathStr1[ F_PATH_MAX_SIZE]; + FLMBOOL bMutexLocked = FALSE; + F_SEM hWaitSem = F_SEM_NULL; + + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + if( RC_BAD( rc = gv_pFileSystem->pathToStorageString( + pszDbPath, szDbPathStr1))) + { + goto Exit; + } + +Retry: + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + pBucket = gv_XFlmSysData.pDatabaseHashTbl; + uiBucket = flmStrHashBucket( szDbPathStr1, pBucket, FILE_HASH_ENTRIES); + pDatabase = (F_Database *)pBucket [uiBucket].pFirstInBucket; + + while( pDatabase) + { + // Compare the strings. On non-Unix platforms we must use + // f_stricmp, because case does not matter for file names + // on those platforms. + +#ifdef FLM_UNIX + if( f_strcmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) +#else + if( f_stricmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) +#endif + { + break; + } + + pDatabase = pDatabase->m_pNext; + } + + if( !pDatabase) + { + // Didn't find a matching database. We are done. + + goto Exit; + } + + // If the file is in the process of being opened by another + // thread, wait for the open to complete. + + if( pDatabase->m_uiFlags & DBF_BEING_OPENED) + { + flmWaitNotifyReq( gv_XFlmSysData.hShareMutex, hWaitSem, + &pDatabase->m_pOpenNotifies, NULL); + goto Retry; + } + else + { + // The database is open. Put ourselves into the close notify list + // so that we will wake up when the database has been closed. + + if( RC_BAD( rc = flmWaitNotifyReq( + gv_XFlmSysData.hShareMutex, hWaitSem, + &pDatabase->m_pCloseNotifies, NULL))) + { + goto Exit; + } + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + return( rc); +} diff --git a/version5/src/flconvrt.cpp b/version5/src/flconvrt.cpp new file mode 100644 index 0000000..b14a2a4 --- /dev/null +++ b/version5/src/flconvrt.cpp @@ -0,0 +1,722 @@ +//------------------------------------------------------------------------------ +// Desc: Database upgrade routines. +// +// 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: flconvrt.cpp 3112 2006-01-19 13:12:40 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc : Upgrades a database to the latest FLAIM version. +****************************************************************************/ +RCODE XFLMAPI F_Db::upgrade( + IF_UpgradeClient * // pUpgradeClient + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bLockedDatabase = FALSE; + FLMUINT uiOldVersion = 0; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + XFLM_DB_HDR * pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; + FLMUINT64 ui64SaveTransId; + FLMUINT uiRflToken = 0; + FLMBOOL bUpgradeNeeded = FALSE; + + // Lock the database if not already locked. + // Cannot lose exclusive access between the checkpoint and + // the update transaction that does the conversion. + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if (RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, 15))) + { + goto Exit; + } + bLockedDatabase = TRUE; + } + + // Cannot have any transaction already going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // NOTE: Don't get the current version number until AFTER obtaining + // the exclusive lock - to make sure nobody else can or will do + // an upgrade while we are in here. + + uiOldVersion = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion; + + // Verify that we can do the upgrade + + switch (uiOldVersion) + { + case XFLM_CURRENT_VERSION_NUM: + break; + + default: + rc = RC_SET( NE_XFLM_UNALLOWED_UPGRADE); + goto Exit; + } + + if( !bUpgradeNeeded) + { + goto Exit; + } + + // Change state of logging OFF to TRUE - don't want anything + // logged during conversion except for the upgrade packet. + + pRfl->disableLogging( &uiRflToken); + m_uiFlags |= FDB_UPGRADING; + + ui64SaveTransId = m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID; + + // Flush everything do disk so that the roll forward log is empty. + // The upgrade doesn't put any special data in the roll forward log + // so if the roll forward log had stuff in it, it would roll forward + // on data that was a newer version - and never work! + // Start an update transaction and commit it, forcing it to be + // checkpointed. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Don't want this transaction to change the transaction ID because + // we are only trying to force a checkpoint. We don't want to change + // the transaction ID until we have actually done the convert. + + m_pDatabase->m_uncommittedDbHdr.ui64CurrTransID = ui64SaveTransId; + m_ui64CurrTransID = ui64SaveTransId; + + // Set up things in the FDB to indicate where we should move the + // checkpoint file number and offset to. If we are in the middle + // of a recovery or restore operation, move the pointer forward + // to just BEFORE the upgrade packet. Down below when we do the + // checkpoint at the end of the upgrade, we will move the pointer + // forward to just AFTER the upgrade packet. + + if (m_uiFlags & FDB_REPLAYING_RFL) + { + m_uiUpgradeCPFileNum = pRfl->getCurrFileNum(); + m_uiUpgradeCPOffset = pRfl->getCurrPacketAddress(); + } + + // Commit the transaction, forcing it to be checkpointed. + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, TRUE))) + { + goto Exit; + } + + // Start an update transaction for the conversion. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Make sure that commit does something. + + m_bHadUpdOper = TRUE; + + // NOTE: By this point, all conversions should be complete, except for + // committing and changing the version number. + + // Log the upgrade packet to the RFL + + pRfl->enableLogging( &uiRflToken); + + // Log the upgrade packet. + + if( RC_BAD( rc = pRfl->logUpgrade( this, uiOldVersion))) + { + goto Exit; + } + + // Turn logging off again + + pRfl->disableLogging( &uiRflToken); + + // Change the FLAIM version number to the new version number. + + pUncommittedDbHdr->ui32DbVersion = XFLM_CURRENT_VERSION_NUM; + + // Commit and force a checkpoint by passing TRUE. + // Set up things in the FDB to indicate where we should move the + // checkpoint file number and offset to. If we are in the middle + // of a recovery or restore operation, move the pointer forward + // to just AFTER the upgrade packet. + + if (m_uiFlags & FDB_REPLAYING_RFL) + { + m_uiUpgradeCPFileNum = pRfl->getCurrFileNum(); + m_uiUpgradeCPOffset = pRfl->getCurrReadOffset(); + } + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, TRUE))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + // Failure condition, we jumped to exit + + pUncommittedDbHdr->ui32DbVersion = (FLMUINT32)uiOldVersion; + m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion = + (FLMUINT32)uiOldVersion; + + (void)abortTrans(); + } + + if (uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + // Turn off the upgrade flag, in case it was turned on above. + + m_uiFlags &= (~(FDB_UPGRADING)); + + if (bLockedDatabase) + { + (void)dbUnlock(); + } + + return( rc ); +} + +/************************************************************************ +Desc : Enable encryption on the database. +*************************************************************************/ +RCODE XFLMAPI F_Db::enableEncryption( void) +{ + RCODE rc = NE_XFLM_OK; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen = 0; + XFLM_DB_HDR * pucUncommittedLogHdr = &m_pDatabase->m_uncommittedDbHdr; + FLMBOOL bLocked = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + + // We must will start our own transaction + + if( m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if ( RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + + bLocked = TRUE; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Begin an update transaction. + + if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + bStartedTrans = TRUE; + + // If we don't have a wrapping key, then create one. Normally + // this would be the case, since we are enabling encryption, + // but the test is "just to be sure" we don't + // overwrite an existing key. + + if (!m_pDatabase->m_pWrappingKey) + { + if ( RC_BAD( rc = createDbKey())) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( + &pucWrappingKey, &ui32KeyLen, + m_pDatabase->m_pszDbPasswd, NULL))) + { + goto Exit; + } + + f_memcpy( pucUncommittedLogHdr->DbKey, pucWrappingKey, ui32KeyLen); + pucUncommittedLogHdr->ui32DbKeyLen = ui32KeyLen; + + m_pDatabase->m_rcLimitedCode = NE_XFLM_OK; + m_pDatabase->m_bInLimitedMode = FALSE; + m_pDatabase->m_bHaveEncKey = TRUE; + + // If we have a dictionary, update its limited mode flag too. + + if (m_pDict) + { + m_pDict->m_bInLimitedMode = FALSE; + } + + // Log the upgrade packet + + pRfl->enableLogging( &uiRflToken); + + if (RC_BAD( rc = pRfl->logEncryptionKey( this, + RFL_ENABLE_ENCRYPTION_PACKET, pucWrappingKey, ui32KeyLen))) + { + goto Exit; + } + + // Turn logging off again + + pRfl->disableLogging( &uiRflToken); + + // Commit the transaction and force a checkpoint + + if( RC_BAD( rc = commitTrans( 0, TRUE, NULL))) + { + goto Exit; + } + + bStartedTrans = FALSE; + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bLocked) + { + dbUnlock(); + } + + if( pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + return( rc); +} + +/**************************************************************************** +Desc : Change the database key from wrapped in the NICI server + key to shrouded in a password and vice-versa. + + If no password is specified, the key will be wrapped in the + NICI server key. If a password is specified, the key will be + shrouded in it. +****************************************************************************/ +RCODE XFLMAPI F_Db::wrapKey( + const char * pszPassword) +{ + RCODE rc = NE_XFLM_OK; + XFLM_DB_HDR * pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; + FLMBOOL bStartedTrans = FALSE; + FLMBYTE * pucTmp = NULL; + FLMUINT32 ui32KeyLen = XFLM_MAX_ENC_KEY_SIZE; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMBOOL bLocked = FALSE; + FLMUINT uiRflToken = 0; + + if( getTransType() != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + if( !(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if ( RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bLocked = TRUE; + } + + // Turn off logging. We only want to log the wrap key packet. + + pRfl->disableLogging( &uiRflToken); + + // Start the transaction + + if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Wrap or shroud the key + + if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( + &pucTmp, &ui32KeyLen, + (FLMBYTE *)pszPassword, NULL))) + { + goto Exit; + } + + f_memcpy( pUncommittedDbHdr->DbKey, pucTmp, ui32KeyLen); + pUncommittedDbHdr->ui32DbKeyLen = ui32KeyLen; + + // Turn on logging. We only want to log the wrap key packet. + + pRfl->enableLogging( &uiRflToken); + + // Log a wrapped key packet to record that the key + // has been wrapped/encrypted. + + if (RC_BAD( rc = pRfl->logEncryptionKey( this, + RFL_WRAP_KEY_PACKET, pucTmp, ui32KeyLen))) + { + goto Exit; + } + + // Turn logging off again + + pRfl->disableLogging( &uiRflToken); + + // Make sure the log header gets written out... + + m_bHadUpdOper = TRUE; + + // Commit the transaction and force a checkpoint + + if (RC_BAD( rc = transCommit())) + { + goto Exit; + } + + bStartedTrans = FALSE; + + // Delete the old password + + if (m_pDatabase->m_pszDbPasswd) + { + f_free( &m_pDatabase->m_pszDbPasswd); + } + + // Store the new password + + if( pszPassword) + { + if (RC_BAD( rc = f_calloc( f_strlen( pszPassword) + 1, + &m_pDatabase->m_pszDbPasswd))) + { + goto Exit; + } + + f_memcpy( m_pDatabase->m_pszDbPasswd, pszPassword, + (FLMSIZET)f_strlen( pszPassword)); + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bLocked) + { + dbUnlock(); + } + + if( pucTmp) + { + f_free( &pucTmp); + } + + return( rc); +} + +/*************************************************************************** + * A private function that goes through all the potential trial-and-error + * involved in getting the strongest possible key we can use for the + * database key. + * NOTE: If an update transaction is needed, it is the responsibility of + * the caller to start the transaction! We can't check for this in the + * function because sometimes a transaction is required, and other times + * (such as during a db create) it's impossible. + ***************************************************************************/ +RCODE F_Db::createDbKey( void) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pDatabase->m_pWrappingKey) + { + m_pDatabase->m_pWrappingKey->Release(); + m_pDatabase->m_pWrappingKey = NULL; + } + + if( (m_pDatabase->m_pWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->init( + TRUE, FLM_NICI_AES))) + { + goto Exit; + } + + // Try to get the strongest encryption supported on this platform. + + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( + XFLM_NICI_AES256))) + { + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( + XFLM_NICI_AES192))) + { + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( + XFLM_NICI_AES128))) + { + // Try using DES3 + m_pDatabase->m_pWrappingKey->Release(); + + if ((m_pDatabase->m_pWrappingKey = f_new F_CCS) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->init( + TRUE, FLM_NICI_DES3))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->generateWrappingKey( + XFLM_NICI_DES3X))) + { + // No more choices... + goto Exit; + } + } + } + } + +Exit: + return rc; +} +/**************************************************************************** +Desc : Generate a new database key and re-wrap all existing keys in it + NOTE: New database key will be wrapped in NICI server key, + even if the previous key was wrapped in a password. +****************************************************************************/ +RCODE XFLMAPI F_Db::rollOverDbKey( void) +{ + RCODE rc = NE_XFLM_OK; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMBYTE * pucWrappingKey = NULL; + FLMUINT32 ui32KeyLen = 0; + FLMUINT uiEncDefNum; + F_ENCDEF * pEncDef; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttrNode = NULL; + FLMBYTE * pucBuf; + FLMBOOL bLocked = FALSE; + FLMBOOL bStartedTrans = FALSE; + FLMUINT32 ui32BufLen = 0; + FLMUINT uiRflToken = 0; + + if( getTransType() != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if ( RC_BAD( rc = dbLock( XFLM_LOCK_EXCLUSIVE, 0, XFLM_NO_TIMEOUT))) + { + goto Exit; + } + + bLocked = TRUE; + } + + // Turn off logging + + pRfl->disableLogging( &uiRflToken); + + // Start the transaction + + if (RC_BAD( rc = transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Update the database header with the new key + + if( RC_BAD( rc = createDbKey())) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( + &pucWrappingKey, &ui32KeyLen, + m_pDatabase->m_pszDbPasswd, NULL))) + { + goto Exit; + } + + f_memcpy( &m_pDatabase->m_uncommittedDbHdr.DbKey, + pucWrappingKey, ui32KeyLen); + m_pDatabase->m_uncommittedDbHdr.ui32DbKeyLen = ui32KeyLen; + + // Loop through all the keys in the dictionary + + for( uiEncDefNum = m_pDict->m_uiLowestEncDefNum; + uiEncDefNum <= m_pDict->m_uiHighestEncDefNum; uiEncDefNum++) + { + // If we can't retrieve the encdef, it's no big deal. Just means + // there was a gap in the enc def nums. + + if( RC_OK( rc = m_pDict->getEncDef( uiEncDefNum, &pEncDef))) + { + if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, + pEncDef->ui64DocumentId, &pNode))) + { + goto Exit; + } + + // Get the attribute holding the actual key + + if( RC_BAD( rc = pNode->getAttribute( this, + ATTR_ENCRYPTION_KEY_TAG, (IF_DOMNode **)&pAttrNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pEncDef->pCcs->getKeyToStore( &pucBuf, &ui32BufLen, + NULL, m_pDatabase->m_pWrappingKey))) + { + goto Exit; + } + + pAttrNode->removeModeFlags( this, FDOM_READ_ONLY); + if( RC_BAD( rc = pAttrNode->setBinary( this, pucBuf, ui32BufLen))) + { + goto Exit; + } + pAttrNode->addModeFlags( this, FDOM_READ_ONLY); + + f_free( &pucBuf); + ui32BufLen = 0; + + if( RC_BAD( rc = documentDone( XFLM_DICT_COLLECTION, + pEncDef->ui64DocumentId))) + { + goto Exit; + } + } + else + { + rc = NE_XFLM_OK; + } + } + + if( RC_BAD( rc = transCommit())) + { + goto Exit; + } + bStartedTrans = FALSE; + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = m_pDatabase->m_pRfl->logRollOverDbKey( this))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + transAbort(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bLocked) + { + dbUnlock(); + } + + if( pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + if( pucBuf) + { + f_free( &pucBuf); + } + + if( pNode) + { + pNode->Release(); + } + + if( pAttrNode) + { + pAttrNode->Release(); + } + + return( rc); +} diff --git a/version5/src/flcreate.cpp b/version5/src/flcreate.cpp new file mode 100644 index 0000000..d034569 --- /dev/null +++ b/version5/src/flcreate.cpp @@ -0,0 +1,509 @@ +//------------------------------------------------------------------------------ +// Desc: Create a database. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: flcreate.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/*API~*********************************************************************** +Desc: Creates a new FLAIM database. +*END************************************************************************/ +RCODE XFLMAPI F_DbSystem::dbCreate( + const char * pszFilePath, + // [IN] Full path file name of the database which is to be created. + const char * pszDataDir, + // [IN] Directory for data files. + const char * pszRflDir, + // [IN] RFL directory. NULL indicates that the RFL files should + // be put in the same directory as the database. + const char * pszDictFileName, + // [IN] Full path of a file containing dictionary definitions to be + // imported into the dictionary collection during database + // creation. This is only used if pszDictBuf is NULL. If + // both pszDictFileName and pszDictBuf are NULL, the database + // will be created with an empty dictionary. + const char * pszDictBuf, + // [IN] Buffer containing dictionary definitions in external XML + // format. If the value of this parameter is NULL, + // pszDictFileName will be used. + XFLM_CREATE_OPTS * pCreateOpts, + // [IN] Create options for the database. All members of the + // structure should be initialized through a call to f_memset: + // + // f_memset( pCreateOpts, 0, sizeof( XFLM_CREATE_OPTS)); + // + // Once initialized, the values of specific members can be set to + // reflect the options desired when the database is created (such + // as block size and various roll-forward logging options). If + // NULL is passed as the value of this parameter, default options + // will be used. + FLMBOOL bTempDb, + // [IN] Flag indicating whether this is a temporary database. + // Should try to minimize writing to disk if this is the case. + IF_Db ** ppDb + // [OUT] Pointer to a database object. If the creation is + // successful, the database object will be initialized. + ) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = NULL; + F_Database * pDatabase; + FLMBOOL bDatabaseCreated = FALSE; + FLMBOOL bNewDatabase = FALSE; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiRflToken = 0; + + // Make sure the path looks valid + + if (!pszFilePath || !pszFilePath [0]) + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + + // Allocate and initialize an F_Db structure. + + if (RC_BAD( rc = allocDb( &pDb, FALSE))) + { + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Free any unused structures that have been unused for the maximum + // amount of time. May unlock and re-lock the global mutex. + + checkNotUsedObjects(); + + for( ;;) + { + + // See if we already have the file open. + // May unlock and re-lock the global mutex. + + if (RC_BAD( rc = findDatabase( pszFilePath, pszDataDir, &pDatabase))) + { + goto Exit; + } + + // Didn't find the database + + if (!pDatabase) + { + break; + } + + // See if file is open or being opened. + + if (pDatabase->m_uiOpenIFDbCount || (pDatabase->m_uiFlags & DBF_BEING_OPENED)) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + + // Free the F_Database object. May temporarily unlock the global mutex. + // For this reason, we must call findDatabase again (see above) after + // freeing the object. + + pDatabase->freeDatabase(); + pDatabase = NULL; + } + + // Allocate a new F_Database object + + if (RC_BAD( rc = allocDatabase( pszFilePath, pszDataDir, bTempDb, &pDatabase))) + { + goto Exit; + } + bNewDatabase = TRUE; + + // Link the F_Db object to the F_Database object. + + rc = pDb->linkToDatabase( pDatabase); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc)) + { + goto Exit; + } + + // If the database has not already been created, do so now. + + // Determine what to set file block size to. + + if (pCreateOpts != NULL) + { + pDb->m_pSFileHdl->SetBlockSize( + flmAdjustBlkSize( pCreateOpts->uiBlockSize)); + } + else + { + pDb->m_pSFileHdl->SetBlockSize( XFLM_DEFAULT_BLKSIZ); + } + + if (RC_OK( gv_pFileSystem->Exists( pszFilePath))) + { + rc = RC_SET( NE_XFLM_FILE_EXISTS); + goto Exit; + } + + // Create the .db file. + + pDb->m_pSFileHdl->setMaxAutoExtendSize( gv_XFlmSysData.uiMaxFileSize); + pDb->m_pSFileHdl->setExtendSize( pDb->m_pDatabase->m_uiFileExtendSize); + if (RC_BAD( rc = pDb->m_pSFileHdl->CreateFile( 0))) + { + goto Exit; + } + bDatabaseCreated = TRUE; + + (void)flmStatGetDb( &pDb->m_Stats, pDatabase, + 0, &pDb->m_pDbStats, NULL, NULL); + + // We must have exclusive access. Create a lock file for that + // purpose, if there is not already a lock file. + // NOTE: No need for a lock file if this is a temporary database. + + if (!bTempDb) + { + if (RC_BAD( rc = pDatabase->getExclAccess( pszFilePath))) + { + goto Exit; + } + } + + if (RC_BAD( rc = pDb->initDbFiles( pszRflDir, pszDictFileName, + pszDictBuf, pCreateOpts))) + { + goto Exit; + } + + // Disable RFL logging (m_pRfl was initialized in initDbFiles) + + if( pDatabase->m_pRfl) + { + pDatabase->m_pRfl->disableLogging( &uiRflToken); + } + + // Set FFILE stuff to same state as a completed checkpoint. + + pDatabase->m_uiFirstLogCPBlkAddress = 0; + pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Create a checkpoint thread - no need if this is a temporary + // database. + + if (!bTempDb) + { + if (RC_BAD( rc = pDatabase->startCPThread())) + { + goto Exit; + } + + if( RC_BAD( rc = pDatabase->startMaintThread())) + { + goto Exit; + } + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + if (pDb) + { + pDb->completeOpenOrCreate( rc, bNewDatabase); + + // completeOpenOrCreate will delete pDb if RC_BAD( rc) + + if (RC_BAD( rc)) + { + pDb = NULL; + } + else + { + *ppDb = (IF_Db *)pDb; + pDb = NULL; // This isn't strictly necessary, but it makes it + // obvious that we are no longer using the object. + } + } + + if (RC_BAD( rc)) + { + if (bDatabaseCreated) + { + F_DbSystem dbSystem; + + dbSystem.dbRemove( pszFilePath, pszDataDir, pszRflDir, TRUE); + } + } + else if( uiRflToken) + { + pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + return( rc); +} + +/**************************************************************************** +Desc: Create a database - initialize all physical areas & data dictionary. +****************************************************************************/ +RCODE F_Db::initDbFiles( + const char * pszRflDir, + const char * pszDictFileName, // Name of dictionary file. This + // is only used if pszDictBuf is + // NULL. If both pszDictFileName + // and pszDictBuf are NULL, the + // file will be craeted with an empty + // dictionary. + const char * pszDictBuf, // Buffer containing dictionary in + // XML format. If NULL, + // pszDictFileName will be used. + XFLM_CREATE_OPTS * pCreateOpts) // Create options for the database. +{ + RCODE rc = NE_XFLM_OK; + FLMUINT bTransStarted = FALSE; + FLMBYTE * pucBuf = NULL; + FLMUINT uiBlkSize; + FLMUINT uiWriteBytes; + FLMUINT uiRflToken = 0; + XFLM_DB_HDR * pDbHdr; + F_BLK_HDR * pBlkHdr; + F_CachedBlock * pSCache = NULL; + FLMBYTE * pucWrappingKey = NULL; +#ifdef FLM_USE_NICI + FLMUINT32 ui32KeyLen = 0; +#endif + + // Determine what size of buffer to allocate. + + uiBlkSize = (FLMUINT)(pCreateOpts + ? flmAdjustBlkSize( pCreateOpts->uiBlockSize) + : (FLMUINT)XFLM_DEFAULT_BLKSIZ); + + // Allocate a buffer for writing. + + if (RC_BAD( rc = f_alloc( (FLMUINT)uiBlkSize, &pucBuf))) + { + goto Exit; + } + + // Initialize the database header structure. + + pDbHdr = (XFLM_DB_HDR *)pucBuf; + flmInitDbHdr( pCreateOpts, TRUE, m_pDatabase->m_bTempDb, pDbHdr); + m_pDatabase->m_uiBlockSize = (FLMUINT)pDbHdr->ui16BlockSize; + m_pDatabase->m_uiDefaultLanguage = (FLMUINT)pDbHdr->ui8DefaultLanguage; + m_pDatabase->m_uiMaxFileSize = (FLMUINT)pDbHdr->ui32MaxFileSize; + m_pDatabase->m_uiSigBitsInBlkSize = calcSigBits( uiBlkSize); + + f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); + + // Create the first block file. + + if (!m_pDatabase->m_bTempDb) + { + if (RC_BAD( rc = m_pSFileHdl->CreateFile( 1))) + { + goto Exit; + } + } + +#ifdef FLM_USE_NICI + if (RC_BAD( rc = createDbKey())) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDatabase->m_pWrappingKey->getKeyToStore( + &pucWrappingKey, + &ui32KeyLen, + m_pDatabase->m_pszDbPasswd, + NULL))) + { + goto Exit; + } + + f_memcpy( m_pDatabase->m_lastCommittedDbHdr.DbKey, + pucWrappingKey, + ui32KeyLen); + m_pDatabase->m_lastCommittedDbHdr.ui32DbKeyLen = ui32KeyLen; + + m_pDatabase->m_rcLimitedCode = NE_XFLM_OK; + m_pDatabase->m_bInLimitedMode = FALSE; + m_pDatabase->m_bHaveEncKey = TRUE; + +#else + m_pDatabase->m_rcLimitedCode = NE_XFLM_ENCRYPTION_UNAVAILABLE; + m_pDatabase->m_bInLimitedMode = TRUE; + m_pDatabase->m_bHaveEncKey = FALSE; +#endif + + // Write out the log header + + if (RC_BAD( rc = m_pDatabase->writeDbHdr( m_pDbStats, m_pSFileHdl, + &m_pDatabase->m_lastCommittedDbHdr, + NULL, TRUE))) + { + goto Exit; + } + + // Initialize and output the first LFH block + + if (m_pDatabase->m_bTempDb) + { + getDbHdrInfo( &m_pDatabase->m_lastCommittedDbHdr); + if (RC_BAD( rc = m_pDatabase->createBlock( this, &pSCache))) + { + goto Exit; + } + pBlkHdr = (F_BLK_HDR *)pSCache->m_pBlkHdr; + m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr = pBlkHdr->ui32BlkAddr; + } + else + { + // Copy the Db header to the checkpointDbHdr buffer. + // This is now the first official checkpoint version of the log + // header. It must be copied to the checkpointDbHdr buffer so that + // it will not be lost in subsequent calls to flmWriteDbHdr. + + f_memcpy( &m_pDatabase->m_checkpointDbHdr, &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + + f_memset( pucBuf, 0, uiBlkSize); + pBlkHdr = (F_BLK_HDR *)pucBuf; + pBlkHdr->ui32BlkAddr = m_pDatabase->m_lastCommittedDbHdr.ui32FirstLFBlkAddr; + pBlkHdr->ui64TransID = 0; + } + pBlkHdr->ui8BlkType = BT_LFH_BLK; + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(uiBlkSize - SIZEOF_STD_BLK_HDR); + blkSetNativeFormat( pBlkHdr); + + if (!m_pDatabase->m_bTempDb) + { + pBlkHdr->ui32BlkCRC = calcBlkCRC( pBlkHdr, SIZEOF_STD_BLK_HDR); + if (RC_BAD( rc = m_pSFileHdl->WriteBlock( + (FLMUINT)pBlkHdr->ui32BlkAddr, + uiBlkSize, pucBuf, uiBlkSize, NULL, + &uiWriteBytes))) + { + goto Exit; + } + + // Force things to disk. + + if (RC_BAD( rc = m_pSFileHdl->Flush())) + { + goto Exit; + } + } + + // Allocate the pRfl object. Could not do this until this point + // because we need to have the version number, block size, etc. + // setup in the database header + + flmAssert( !m_pDatabase->m_pRfl); + + if (!m_pDatabase->m_bTempDb) + { + if ((m_pDatabase->m_pRfl = f_new F_Rfl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDatabase->m_pRfl->setup( m_pDatabase, pszRflDir))) + { + goto Exit; + } + + // Disable RFL logging + + m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + + // Start an update transaction and populate the dictionary. + // This also creates the default collections and indexes. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bTransStarted = TRUE; + + if (RC_BAD( rc = dictCreate( pszDictFileName, pszDictBuf))) + { + goto Exit; + } + + // Because the checkpoint thread has not yet been created, + // flmCommitDbTrans will force a checkpoint when it completes, + // ensuring a consistent database state. + + bTransStarted = FALSE; + if (RC_BAD( rc = commitTrans( 0, TRUE))) + { + goto Exit; + } + } + else + { + // The uncommitted header must have all of the stuff from the committed header + // in order for this to be able to work as if an update transaction was in + // progress. + + f_memcpy( &m_pDatabase->m_uncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + } + +Exit: + + // Free the temporary buffer, if it was allocated. + + f_free( &pucBuf); + + if (pucWrappingKey) + { + f_free( &pucWrappingKey); + } + + if (bTransStarted) + { + abortTrans(); + } + + if( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc); +} diff --git a/version5/src/fldbglog.cpp b/version5/src/fldbglog.cpp new file mode 100644 index 0000000..e93938a --- /dev/null +++ b/version5/src/fldbglog.cpp @@ -0,0 +1,325 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the functions for debug logging. +// +// 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: fldbglog.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#ifdef FLM_DBG_LOG + +// Local prototypes + +FSTATIC void _flmDbgLogFlush( void); + +FSTATIC void _flmDbgOutputMsg( + char * pszMsg); + +// Global data + +F_MUTEX g_hDbgLogMutex = F_MUTEX_NULL; +IF_FileHdl * g_pLogFile = NULL; +char * g_pszLogBuf = NULL; +FLMUINT g_uiLogBufOffset = 0; +FLMUINT g_uiLogFileOffset = 0; +FLMBOOL g_bDbgLogEnabled = TRUE; + +#define DBG_LOG_BUFFER_SIZE ((FLMUINT)512000) + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogInit( void) +{ + char szLogPath[ 256]; + RCODE rc = NE_XFLM_OK; + + flmAssert( g_hDbgLogMutex == F_MUTEX_NULL); + + // Allocate a buffer for the log + + if (RC_BAD( rc = f_alloc( DBG_LOG_BUFFER_SIZE + 1024, &g_pszLogBuf))) + { + goto Exit; + } + + // Create the mutex + + if (RC_BAD( rc = f_mutexCreate( &g_hDbgLogMutex))) + { + goto Exit; + } + + // Build the file path + +#ifdef FLM_NLM + f_strcpy( szLogPath, "SYS:\\FLMDBG.LOG"); +#else + f_sprintf( szLogPath, "FLMDBG.LOG"); +#endif + + // Create the file - truncate if it exists already. + + if( RC_BAD( rc = gv_pFileSystem->Create( szLogPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, &g_pLogFile))) + { + goto Exit; + } + +Exit: + + flmAssert( RC_OK( rc)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogExit( void) +{ + if( g_bDbgLogEnabled) + { + // Output "Log End" message + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( "--- LOG END ---"); + f_mutexUnlock( g_hDbgLogMutex); + + // Flush the log + flmDbgLogFlush(); + } + + // Free all resources + + if( g_hDbgLogMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &g_hDbgLogMutex); + } + + if( g_pszLogBuf) + { + f_free( &g_pszLogBuf); + } + + if( g_pLogFile) + { + g_pLogFile->Truncate( g_uiLogFileOffset + g_uiLogBufOffset); + g_pLogFile->Close(); + g_pLogFile->Release(); + g_pLogFile = NULL; + } + + g_bDbgLogEnabled = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogMsg( + char * pszMsg) +{ + if (!g_bDbgLogEnabled) + return; + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszMsg); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogFlush( void) +{ + f_mutexLock( g_hDbgLogMutex); + _flmDbgLogFlush(); + f_mutexUnlock( g_hDbgLogMutex); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC void _flmDbgLogFlush( void) +{ + FLMUINT uiBytesToWrite; + FLMUINT uiBytesWritten; + char * pszBufPtr = g_pszLogBuf; + FLMUINT uiTotalToWrite = g_uiLogBufOffset; + RCODE rc = NE_XFLM_OK; + FLMUINT uiBufferSize = DBG_LOG_BUFFER_SIZE + 1024; + + while( uiTotalToWrite) + { + if( uiTotalToWrite > 0xFE00) + { + uiBytesToWrite = 0xFE00; + } + else + { + uiBytesToWrite = uiTotalToWrite; + } + + if( RC_BAD( rc = g_pLogFile->SectorWrite( + g_uiLogFileOffset, uiBytesToWrite, + pszBufPtr, uiBufferSize, NULL, &uiBytesWritten, FALSE))) + { + goto Exit; + } + + flmAssert( uiBytesToWrite == uiBytesWritten); + g_uiLogFileOffset += uiBytesWritten; + pszBufPtr += uiBytesWritten; + uiBufferSize -= uiBytesWritten; + uiTotalToWrite -= uiBytesWritten; + } + + if (g_uiLogBufOffset & 0x1FF) + { + if (g_uiLogBufOffset > 512) + { + f_memcpy( g_pszLogBuf, + &g_pszLogBuf [g_uiLogBufOffset & 0xFFFFFE00], + 512); + g_uiLogBufOffset &= 0x1FF; + } + g_uiLogFileOffset -= g_uiLogBufOffset; + } + else + { + g_uiLogBufOffset = 0; + } + +Exit: + + flmAssert( RC_OK( rc)); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +void _flmDbgOutputMsg( + char * pszMsg) +{ + char * pszBufPtr = (char *)(&(g_pszLogBuf[ g_uiLogBufOffset])); + + f_sprintf( (char *)pszBufPtr, "%s\n", pszMsg); + g_uiLogBufOffset += f_strlen( pszBufPtr); + + if( g_uiLogBufOffset >= DBG_LOG_BUFFER_SIZE) + { + _flmDbgLogFlush(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogWrite( + F_Database * pDatabase, + FLMUINT uiBlkAddress, + FLMUINT uiWriteAddress, + FLMUINT64 ui64TransId, + char * pszEvent) +{ + char pszTmpBuf[ 256]; + + if( !g_bDbgLogEnabled) + return; + + if( !uiWriteAddress) + { + f_sprintf( (char *)pszTmpBuf, "d%X b=%X t%I64u %s", + (unsigned)((FLMUINT)pDatabase), + (unsigned)uiBlkAddress, ui64TransId, pszEvent); + } + else + { + f_sprintf( (char *)pszTmpBuf, "d%X b=%X a=%X t%I64u %s", + (unsigned)((FLMUINT)pDatabase), + (unsigned)uiBlkAddress, (unsigned)uiWriteAddress, + ui64TransId, pszEvent); + } + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszTmpBuf); + f_mutexUnlock( g_hDbgLogMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void flmDbgLogUpdate( + F_Database * pDatabase, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + RCODE rc, + char * pszEvent) +{ + char pszTmpBuf[ 256]; + char szErr [12]; + + if (!g_bDbgLogEnabled) + { + return; + } + if (RC_BAD( rc)) + { + f_sprintf( szErr, " RC=%04X", (unsigned)rc); + } + else + { + szErr [0] = 0; + } + + if (uiCollection) + { + f_sprintf( (char *)pszTmpBuf, "d%X t%I64u c%u n%I64u %s%s", + (unsigned)((FLMUINT)pDatabase), + ui64TransId, (unsigned)uiCollection, + ui64NodeId, pszEvent, szErr); + } + else + { + f_sprintf( (char *)pszTmpBuf, "d%X t%I64u %s%s", + (unsigned)((FLMUINT)pDatabase), + ui64TransId, pszEvent, + szErr); + } + + f_mutexLock( g_hDbgLogMutex); + _flmDbgOutputMsg( pszTmpBuf); + f_mutexUnlock( g_hDbgLogMutex); +} + +#else + +// Must have something here for the Netware platform, or it won't build. + +#if defined( FLM_WATCOM_NLM) + void gv_fldbglog() + { + } +#endif + + +#endif // #ifdef FLM_DBG_LOG diff --git a/version5/src/flerror.cpp b/version5/src/flerror.cpp new file mode 100644 index 0000000..1a5a7db --- /dev/null +++ b/version5/src/flerror.cpp @@ -0,0 +1,126 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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. +Note: Some of the most common errors will be coded so the use can set a + break point. +****************************************************************************/ +#ifdef FLM_DEBUG +RCODE flmMakeErr( + RCODE rc, + const char * pszFile, + int iLine, + FLMBOOL bAssert) +{ + if( rc == NE_XFLM_OK) + { + return NE_XFLM_OK; + } + + // Switch on warning type return codes + if( rc <= NE_XFLM_NOT_FOUND) + { + switch(rc) + { + case NE_XFLM_BOF_HIT: + break; + case NE_XFLM_EOF_HIT: + break; + case NE_XFLM_END: + break; + case NE_XFLM_EXISTS: + break; + case NE_XFLM_NOT_FOUND: + break; + } + + goto Exit; + } + + switch(rc) + { + case NE_XFLM_IO_BAD_FILE_HANDLE: + break; + case NE_XFLM_DATA_ERROR: + flmLogError( rc, "", pszFile, iLine); + break; + case NE_XFLM_BTREE_ERROR: + flmLogError( rc, "", pszFile, iLine); + break; + case NE_XFLM_MEM: + break; + case NE_XFLM_OLD_VIEW: + break; + case NE_XFLM_SYNTAX: + break; + case NE_XFLM_BLOCK_CRC: + flmLogError( rc, "", pszFile, iLine); + break; + case NE_XFLM_CACHE_ERROR: + flmLogError( rc, "", pszFile, iLine); + break; + case NE_XFLM_NOT_IMPLEMENTED: + break; + case NE_XFLM_CONV_DEST_OVERFLOW: + break; + case NE_XFLM_KEY_OVERFLOW: + break; + case NE_XFLM_FAILURE: + break; + case NE_XFLM_ILLEGAL_OP: + break; + case NE_XFLM_BAD_COLLECTION: + break; + default: + rc = rc; + break; + } + +Exit: + +#if defined( FLM_DEBUG) + if( bAssert) + { + flmAssert( 0); + } +#else + F_UNREFERENCED_PARM( bAssert); +#endif + + return( rc); +} +#endif + +#if defined( FLM_WATCOM_NLM) + int gv_iFlerrorDummy(void) + { + return( 0); + } +#endif diff --git a/version5/src/flerrstr.cpp b/version5/src/flerrstr.cpp new file mode 100644 index 0000000..13c19b2 --- /dev/null +++ b/version5/src/flerrstr.cpp @@ -0,0 +1,176 @@ +//------------------------------------------------------------------------------ +// Desc: This module contains the checkErrorToStr method - which returns +// a pointer to a string which corresponds to a corruption code. +// +// Tabs: 3 +// +// Copyright (c) 1992-1997,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: flerrstr.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +// WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE +// REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h + +char * FlmCorruptStrings[ FLM_NUM_CORRUPT_ERRORS] += { + "BAD_CHAR", /*1*/ + "BAD_ASIAN_CHAR", /*2*/ + "BAD_CHAR_SET", /*3*/ + "BAD_TEXT_FIELD", /*4*/ + "BAD_NUMBER_FIELD", /*5*/ + "BAD_FIELD_TYPE", /*6*/ + "BAD_IX_DEF", /*7*/ + "MISSING_REQ_KEY_FIELD", /*8*/ + "BAD_TEXT_KEY_COLL_CHAR", /*9*/ + "BAD_TEXT_KEY_CASE_MARKER", /*10*/ + "BAD_NUMBER_KEY", /*11*/ + "BAD_BINARY_KEY", /*12*/ + "BAD_CONTEXT_KEY", /*13*/ + "BAD_KEY_FIELD_TYPE", /*14*/ + "Not_Used_15", /*15*/ + "Not_Used_16", /*16*/ + "Not_Used_17", /*17*/ + "BAD_KEY_LEN", /*18*/ + "BAD_LFH_LIST_PTR", /*19*/ + "BAD_LFH_LIST_END", /*20*/ + "INCOMPLETE_NODE", /*21*/ + "BAD_BLK_END", /*22*/ + "KEY_COUNT_MISMATCH", /*23*/ + "REF_COUNT_MISMATCH", /*24*/ + "BAD_CONTAINER_IN_KEY", /*25*/ + "BAD_BLK_HDR_ADDR", /*26*/ + "BAD_BLK_HDR_LEVEL", /*27*/ + "BAD_BLK_HDR_PREV", /*28*/ + +// WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE +// REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h + + "BAD_BLK_HDR_NEXT", /*29*/ + "BAD_BLK_HDR_TYPE", /*30*/ + "BAD_BLK_HDR_ROOT_BIT", /*31*/ + "BAD_BLK_HDR_BLK_END", /*32*/ + "BAD_BLK_HDR_LF_NUM", /*33*/ + "BAD_AVAIL_LIST_END", /*34*/ + "BAD_PREV_BLK_NEXT", /*35*/ + "BAD_FIRST_LAST_ELM_FLAG", /*36*/ + "nu", /*37*/ + "BAD_LEM", /*38*/ + "BAD_ELM_LEN", /*39*/ + "BAD_ELM_KEY_SIZE", /*40*/ + "BAD_ELM_KEY", /*41*/ + "BAD_ELM_KEY_ORDER", /*42*/ + "nu", /*43*/ + "BAD_CONT_ELM_KEY", /*44*/ + "NON_UNIQUE_FIRST_ELM_KEY", /*45*/ + "BAD_ELM_OFFSET", /*46*/ + "BAD_ELM_INVALID_LEVEL", /*47*/ + "BAD_ELM_FLD_NUM", /*48*/ + "BAD_ELM_FLD_LEN", /*49*/ + "BAD_ELM_FLD_TYPE", /*50*/ + "BAD_ELM_END", /*51*/ + "BAD_PARENT_KEY", /*52*/ + "BAD_ELM_DOMAIN_SEN", /*53*/ + "BAD_ELM_BASE_SEN", /*54*/ + "BAD_ELM_IX_REF", /*55*/ + "BAD_ELM_ONE_RUN_SEN", /*56*/ + "BAD_ELM_DELTA_SEN", /*57*/ + "BAD_ELM_DOMAIN", /*58*/ + +// WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE +// REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h + + "BAD_LAST_BLK_NEXT", /*59*/ + "BAD_FIELD_PTR", /*60*/ + "REBUILD_REC_EXISTS", /*61*/ + "REBUILD_KEY_NOT_UNIQUE", /*62*/ + "NON_UNIQUE_ELM_KEY_REF", /*63*/ + "OLD_VIEW", /*64*/ + "COULD_NOT_SYNC_BLK", /*65*/ + "IX_REF_REC_NOT_FOUND", /*66*/ + "IX_KEY_NOT_FOUND_IN_REC", /*67*/ + "KEY_NOT_IN_KEY_REFSET", /*68*/ + "BAD_BLK_CHECKSUM", /*69*/ + "BAD_LAST_DRN", /*70*/ + "BAD_FILE_SIZE", /*71*/ + "nu", /*72*/ + "BAD_DATE_FIELD", /*73*/ + "BAD_TIME_FIELD", /*74*/ + "BAD_TMSTAMP_FIELD", /*75*/ + "BAD_DATE_KEY", /*76*/ + "BAD_TIME_KEY", /*77*/ + "BAD_TMSTAMP_KEY", /*78*/ + "BAD_BLOB_FIELD", /*79*/ + +// WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE +// REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h + + "BAD_PCODE_IXD_TBL", /*80*/ + "NODE_QUARANTINED", /*81*/ + "BAD_BLK_TYPE", /*82*/ + "BAD_ELEMENT_CHAIN", /*83*/ + "BAD_ELM_EXTR_DATA", /*84*/ + "BAD_BLOCK_STRUCTURE", /*85*/ + "BAD_ROOT_PARENT", /*86*/ + "BAD_ROOT_LINK", /*87*/ + "BAD_PARENT_LINK", /*88*/ + "BAD_INVALID_ROOT", /*89*/ + "BAD_FIRST_CHILD_LINK", /*90*/ + "BAD_LAST_CHILD_LINK", /*91*/ + "BAD_PREV_SIBLING_LINK", /*92*/ + "BAD_NEXT_SIBLING_LINK", /*93*/ + "BAD_ANNOTATION_LINK", /*95*/ + "UNSUPPORTED_NODE_TYPE", /*96*/ + "BAD_INVALID_NAME_ID", /*97*/ + "BAD_INVALID_PREFIX_ID", /*98*/ + "BAD_DATA_BLOCK_COUNT", /*99*/ + "FLM_BAD_AVAIL_SIZE", /*100*/ + "BAD_NODE_TYPE", /*101*/ + "BAD_CHILD_ELM_COUNT", /*102*/ + }; + +// WARNING: ANY CHANGES MADE TO THE FlmCorruptStrings TABLE MUST BE +// REFLECTED IN THE CHECK CODE DEFINES FOUND IN flaimsys.h + + + +/*API~*********************************************************************** +Area : INFORMATION +Desc : Returns a pointer to the string representation of a corruption + error code. +Notes: Linking in this routine will bring in static string arrays. +*END************************************************************************/ +const char * XFLMAPI F_DbSystem::checkErrorToStr( + FLMINT iCheckErrorCode) +{ + if( (iCheckErrorCode >= 1) && (iCheckErrorCode <= FLM_NUM_CORRUPT_ERRORS)) + { + return( FlmCorruptStrings [iCheckErrorCode - 1]); + } + else if( iCheckErrorCode == 0) + { + return( "OK"); + } + else + { + return( "Unknown Error"); + } +} + diff --git a/version5/src/flfixed.cpp b/version5/src/flfixed.cpp new file mode 100644 index 0000000..2351055 --- /dev/null +++ b/version5/src/flfixed.cpp @@ -0,0 +1,2502 @@ +//------------------------------------------------------------------------------ +// Desc: Special allocators for making many fixed-size allocations. +// +// 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: flfixed.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#ifdef FLM_SOLARIS + #include +#endif + +#ifdef FLM_NLM + extern "C" + { + extern LONG gv_lAllocRTag; + } +#endif + +/**************************************************************************** +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 F_SlabManager::setup( + FLMUINT uiPreallocSize) +{ + RCODE rc = NE_XFLM_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_XFLM_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 F_SlabManager::resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_XFLM_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_XFLM_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 F_SlabManager::allocSlab( + void ** ppSlab, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_XFLM_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_XFLM_MEM); + goto Exit; + } + + m_uiTotalSlabs++; + m_uiInUseSlabs++; + } + +Exit: + + if( bUnlockMutex) + { + unlockMutex(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void 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_CACHE_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_CACHE_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 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 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_XFLM_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_CACHE_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, + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT uiCellSize, + XFLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_XFLM_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_CACHE_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_CACHE_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_CACHE_PROTECT + unprotectSlab( pSlabToUnlink->pNextSlabWithAvailCells, TRUE); +#endif + + pSlabToUnlink-> + pNextSlabWithAvailCells->pPrevSlabWithAvailCells = + pSlabToUnlink->pPrevSlabWithAvailCells; + +#ifdef FLM_CACHE_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_CACHE_PROTECT + unprotectSlab( pNewSlab, TRUE); +#endif + pNewSlab->pNext = m_pFirstSlab; +#ifdef FLM_CACHE_PROTECT + protectSlab( pNewSlab, TRUE); +#endif + +#ifdef FLM_CACHE_PROTECT + unprotectSlab( m_pFirstSlab, TRUE); +#endif + m_pFirstSlab->pPrev = pNewSlab; +#ifdef FLM_CACHE_PROTECT + protectSlab( m_pFirstSlab, TRUE); +#endif + } + else + { + m_pLastSlab = pNewSlab; + } + + m_pFirstSlab = pNewSlab; + } + + pSlab = m_pFirstSlab; + +#ifdef FLM_CACHE_PROTECT + unprotectSlab( pSlab, TRUE); +#endif + pSlab->ui16AllocatedCells++; + +#ifdef FLM_CACHE_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_XFlmSysData.bTrackLeaks && gv_XFlmSysData.bStackWalk) + { + pHeader->puiStack = memWalkStack(); + } + else + { + pHeader->puiStack = NULL; + } +#endif + if (!m_pRelocator) + { + ((CELLHEADER2 *)pHeader)->pRelocator = pRelocator; + } + +#ifdef FLM_CACHE_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_CACHE_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_CACHE_PROTECT + unprotectSlab( pSlab, TRUE); + bProtectSlab = TRUE; +#endif + + pHeader->pContainingSlab = NULL; +#ifdef FLM_DEBUG + if( pHeader->puiStack) + { + os_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( pCellContents->szDebugPattern, (FLMBYTE*)"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_CACHE_PROTECT + unprotectSlab( m_pFirstSlabWithAvailCells, TRUE); +#endif + m_pFirstSlabWithAvailCells->pPrevSlabWithAvailCells = pSlab; +#ifdef FLM_CACHE_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_CACHE_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_CACHE_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_CACHE_PROTECT + if( m_bMemProtectionEnabled) + { + m_pSlabManager->protectSlab( pSlab); + } +#endif + +Exit: + + return( pSlab); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_CACHE_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_CACHE_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_CACHE_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_CACHE_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_CACHE_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_CACHE_PROTECT + unprotectSlab( pSlab->pNext, TRUE); +#endif + pSlab->pNext->pPrev = pSlab->pPrev; +#ifdef FLM_CACHE_PROTECT + protectSlab( pSlab->pNext, TRUE); +#endif + } + else + { + m_pLastSlab = pSlab->pPrev; + } + + if( pSlab->pPrev) + { +#ifdef FLM_CACHE_PROTECT + unprotectSlab( pSlab->pPrev, TRUE); +#endif + pSlab->pPrev->pNext = pSlab->pNext; +#ifdef FLM_CACHE_PROTECT + protectSlab( pSlab->pPrev, TRUE); +#endif + } + else + { + m_pFirstSlab = pSlab->pNext; + } + + // Unlink from slabs-with-avail-cells list + + if( pSlab->pNextSlabWithAvailCells) + { +#ifdef FLM_CACHE_PROTECT + unprotectSlab( pSlab->pNextSlabWithAvailCells, TRUE); +#endif + pSlab->pNextSlabWithAvailCells->pPrevSlabWithAvailCells = + pSlab->pPrevSlabWithAvailCells; +#ifdef FLM_CACHE_PROTECT + protectSlab( pSlab->pNextSlabWithAvailCells, TRUE); +#endif + } + else + { + m_pLastSlabWithAvailCells = pSlab->pPrevSlabWithAvailCells; + } + + if( pSlab->pPrevSlabWithAvailCells) + { +#ifdef FLM_CACHE_PROTECT + unprotectSlab( pSlab->pPrevSlabWithAvailCells, TRUE); +#endif + pSlab->pPrevSlabWithAvailCells->pNextSlabWithAvailCells = + pSlab->pNextSlabWithAvailCells; +#ifdef FLM_CACHE_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_CACHE_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( XFLM_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_CACHE_PROTECT + unprotectSlab( pCurSlab, TRUE); +#endif + + pCurSlab->pNextSlabWithAvailCells = NULL; + pCurSlab->pPrevSlabWithAvailCells = NULL; + + if( pPrevSib) + { + pCurSlab->pPrevSlabWithAvailCells = pPrevSib; +#ifdef FLM_CACHE_PROTECT + unprotectSlab( pPrevSib, TRUE); +#endif + pPrevSib->pNextSlabWithAvailCells = pCurSlab; +#ifdef FLM_CACHE_PROTECT + protectSlab( pPrevSib, TRUE); +#endif + } + else + { + m_pFirstSlabWithAvailCells = pCurSlab; + } + +#ifdef FLM_CACHE_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_CACHE_PROTECT + unprotectSlab( ((CELLHEADER *)(pucReloc - + m_uiCellHeaderSize))->pContainingSlab, TRUE); +#endif + + f_memcpy( pucReloc, pucOriginal, m_uiCellSize); + pRelocator->relocate( pucOriginal, pucReloc); + +#ifdef FLM_CACHE_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( + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + XFLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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_XFLM_OK; + F_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_XFLM_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_XFLM_OK; + FLMBYTE * pucTmp; + F_FixedAlloc * pOldAllocator; + F_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_XFLM_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_XFLM_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) +{ + F_FixedAlloc * pAllocator = getAllocator( uiSize); + + if( pAllocator) + { + pAllocator->freeCell( *ppucBuffer, FALSE, TRUE, NULL); + *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; + F_FixedAlloc * pAllocator; + + if( !uiSize) + { + uiTrueSize = 0; + } + else if( (pAllocator = getAllocator( uiSize)) != NULL) + { + uiTrueSize = pAllocator->getCellSize(); + } + else + { + uiTrueSize = f_msize( pucBuffer); + } + + return( uiTrueSize); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FixedAlloc * F_BufferAlloc::getAllocator( + FLMUINT uiSize) +{ + F_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 = (F_FixedAlloc *)m_ppAllocators [0]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_1 + ? m_ppAllocators [1] + : m_ppAllocators [2]); + } + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_3 + ? m_ppAllocators [3] + : m_ppAllocators [4]); + } + } + else if( uiSize <= CELL_SIZE_7) + { + if( uiSize <= CELL_SIZE_5) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [5]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_6 + ? m_ppAllocators [6] + : m_ppAllocators [7]); + } + } + else + { + if( uiSize <= CELL_SIZE_8) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [8]; + } + else + { + pAllocator = (F_FixedAlloc *)(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 = (F_FixedAlloc *)m_ppAllocators [11]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_12 + ? m_ppAllocators [12] + : m_ppAllocators [13]); + } + } + else + { + if( uiSize <= CELL_SIZE_14) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [14]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_15 + ? m_ppAllocators [15] + : m_ppAllocators [16]); + } + } + } + else if( uiSize <= CELL_SIZE_19) + { + if( uiSize <= CELL_SIZE_17) + { + pAllocator = (F_FixedAlloc *)m_ppAllocators [17]; + } + else + { + pAllocator = (F_FixedAlloc *)(uiSize <= CELL_SIZE_18 + ? m_ppAllocators [18] + : m_ppAllocators [19]); + } + } + else if( uiSize <= CELL_SIZE_21) + { + pAllocator = (F_FixedAlloc *)(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, + XFLM_SLAB_USAGE * pUsageStats) +{ + RCODE rc = NE_XFLM_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_XFLM_INVALID_PARM); + goto Exit; + } + + f_qsort( puiCellSizes, 0, uiCellCount - 1, + flmQSortUINTCompare, flmQSortUINTSwap); + + 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_XFLM_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_XFLM_OK; + F_FixedAlloc * pAllocator = getAllocator( uiSize); + + flmAssert( pAllocator); + flmAssert( pAllocator->getCellSize() >= uiSize); + + if( (*ppucBuffer = (FLMBYTE *)pAllocator->allocCell( pRelocator, + NULL, 0, bMutexLocked)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_MultiAlloc::reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiNewSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucTmp; + F_FixedAlloc * pOldAllocator; + F_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->m_uiCellSize), TRUE)) == NULL) + { + rc = RC_SET( NE_XFLM_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: +****************************************************************************/ +F_FixedAlloc * F_MultiAlloc::getAllocator( + FLMUINT uiSize) +{ + F_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: +****************************************************************************/ +F_FixedAlloc * F_MultiAlloc::getAllocator( + FLMBYTE * pucBuffer) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + F_FixedAlloc * pAllocator = NULL; + + m_pSlabManager->lockMutex(); + + pHeader = (F_FixedAlloc::CELLHEADER *)(pucBuffer - + F_FixedAlloc::getAllocAlignedSize( + sizeof( F_FixedAlloc::CELLHEADER2))); + pSlab = pHeader->pContainingSlab; + pAllocator = (F_FixedAlloc *)pSlab->pvAllocator; + + m_pSlabManager->unlockMutex(); + return( pAllocator); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_CACHE_PROTECT +void F_MultiAlloc::protectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + F_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 = (F_FixedAlloc *)pSlab->pvAllocator; + pAllocator->protectSlab( pSlab, TRUE); + + if( !bMutexLocked) + { + m_pSlabManager->unlockMutex(); + } +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_CACHE_PROTECT +void F_MultiAlloc::unprotectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked) +{ + F_FixedAlloc::CELLHEADER * pHeader; + F_FixedAlloc::SLAB * pSlab; + F_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 = (F_FixedAlloc *)pSlab->pvAllocator; + pAllocator->unprotectSlab( pSlab, TRUE); + + if( !bMutexLocked) + { + m_pSlabManager->unlockMutex(); + } +} +#endif diff --git a/version5/src/flfixed.h b/version5/src/flfixed.h new file mode 100644 index 0000000..8668c48 --- /dev/null +++ b/version5/src/flfixed.h @@ -0,0 +1,580 @@ +//------------------------------------------------------------------------------ +// Desc: Special allocators for making many fixed-size allocations. +// +// 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: flfixed.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FLFIXED_H +#define FLFIXED_H + +// 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: +****************************************************************************/ +class F_SlabManager : public XF_RefCount, public XF_Base +{ +public: + + F_SlabManager(); + + virtual ~F_SlabManager(); + + RCODE setup( + FLMUINT uiPreallocSize); + + RCODE allocSlab( + void ** ppSlab, + FLMBOOL bMutexLocked); + + void freeSlab( + void ** ppSlab, + FLMBOOL bMutexLocked); + + RCODE resize( + FLMUINT uiNumBytes, + FLMUINT * puiActualSize = NULL, + FLMBOOL bMutexLocked = FALSE); + + FINLINE void incrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) + { + if( !bMutexLocked) + { + lockMutex(); + } + + m_uiTotalBytesAllocated += uiCount; + + if( !bMutexLocked) + { + unlockMutex(); + } + } + + FINLINE void decrementTotalBytesAllocated( + FLMUINT uiCount, + FLMBOOL bMutexLocked) + { + if( !bMutexLocked) + { + lockMutex(); + } + + flmAssert( m_uiTotalBytesAllocated >= uiCount); + m_uiTotalBytesAllocated -= uiCount; + + if( !bMutexLocked) + { + unlockMutex(); + } + } + + FINLINE FLMUINT getSlabSize( void) + { + return( m_uiSlabSize); + } + + FINLINE FLMUINT getTotalSlabs( void) + { + return( m_uiTotalSlabs); + } + + FINLINE void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + FINLINE void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + + FINLINE FLMUINT totalBytesAllocated( void) + { + return( m_uiTotalBytesAllocated); + } + + FINLINE FLMUINT availSlabs( void) + { + return( m_uiAvailSlabs); + } + +#ifdef FLM_CACHE_PROTECT + void protectSlab( + void * pSlab); + + void unprotectSlab( + void * pSlab); +#endif + +private: + + void freeAllSlabs( void); + + void * allocSlabFromSystem( void); + + void releaseSlabToSystem( + void * pSlab); + + RCODE sortSlabList( void); + + typedef struct + { + void * pPrev; + void * pNext; + } SLABHEADER; + + static FLMINT slabAddrCompareFunc( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + static void 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 with two virtual functions - canRelocate and relocate. +****************************************************************************/ +class IF_Relocator +{ +public: + virtual void relocate( + void * pvOldAlloc, + void * pvNewAlloc) = 0; + + virtual FLMBOOL canRelocate( + void * pvOldAlloc) = 0; +}; + +/**************************************************************************** +Desc: Class to provide an efficient means of providing many allocations + of a fixed size. +****************************************************************************/ +class F_FixedAlloc : public XF_RefCount, public XF_Base +{ +public: + + F_FixedAlloc(); + + virtual ~F_FixedAlloc(); + + RCODE setup( + IF_Relocator * pRelocator, + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT uiCellSize, + XFLM_SLAB_USAGE * pUsageStats); + + FINLINE void * 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 freeCell( + void * ptr, + FLMBOOL bMutexLocked) + { + freeCell( ptr, bMutexLocked, FALSE, NULL); + } + + void freeUnused( void); + + void freeAll( void); + + FINLINE FLMUINT getCellSize( void) + { + return( m_uiCellSize); + } + + void defragmentMemory( void); + +#ifdef FLM_CACHE_PROTECT + void protectCell( + void * pvCell); + + void unprotectCell( + void * pvCell); +#endif + + 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; + +private: + +#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 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 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; + } + + F_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; + + // Members specifically for stats + + XFLM_SLAB_USAGE * m_pUsageStats; + + // Memory protection + +#ifdef FLM_CACHE_PROTECT + FLMBOOL m_bMemProtectionEnabled; +#endif + +friend class F_BufferAlloc; +friend class F_MultiAlloc; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class F_BufferAlloc : public XF_RefCount, public XF_Base +{ +public: + + F_BufferAlloc() + { + f_memset( m_ppAllocators, 0, sizeof( m_ppAllocators)); + m_pSlabManager = NULL; + } + + virtual ~F_BufferAlloc(); + + RCODE setup( + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + XFLM_SLAB_USAGE * pUsageStats); + + RCODE allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL); + + RCODE reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiOldSize, + FLMUINT uiNewSize, + void * pvInitialData, + FLMUINT uiDataSize, + FLMBYTE ** ppucBuffer, + FLMBOOL * pbAllocatedOnHeap = NULL); + + void freeBuf( + FLMUINT uiSize, + FLMBYTE ** ppucBuffer); + + FLMUINT getTrueSize( + FLMUINT uiSize, + FLMBYTE * pucBuffer); + + void defragmentMemory( void); + +private: + + F_FixedAlloc * getAllocator( + FLMUINT uiSize); + + F_SlabManager * m_pSlabManager; + F_FixedAlloc * m_ppAllocators[ NUM_BUF_ALLOCATORS]; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class F_MultiAlloc : public XF_RefCount, public XF_Base +{ +public: + + F_MultiAlloc() + { + m_pSlabManager = NULL; + m_puiCellSizes = NULL; + m_ppAllocators = NULL; + } + + ~F_MultiAlloc() + { + cleanup(); + } + + RCODE setup( + F_SlabManager * pSlabManager, + FLMBOOL bMemProtect, + FLMUINT * puiCellSizes, + XFLM_SLAB_USAGE * pUsageStats); + + RCODE allocBuf( + IF_Relocator * pRelocator, + FLMUINT uiSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE); + + RCODE reallocBuf( + IF_Relocator * pRelocator, + FLMUINT uiNewSize, + FLMBYTE ** ppucBuffer, + FLMBOOL bMutexLocked = FALSE); + + FINLINE void freeBuf( + FLMBYTE ** ppucBuffer) + { + if( ppucBuffer && *ppucBuffer) + { + getAllocator( *ppucBuffer)->freeCell( *ppucBuffer, FALSE); + *ppucBuffer = NULL; + } + } + + void defragmentMemory( void); + + FINLINE FLMUINT getTrueSize( + FLMBYTE * pucBuffer) + { + return( getAllocator( pucBuffer)->getCellSize()); + } + +#ifdef FLM_CACHE_PROTECT + void protectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE); + + void unprotectBuffer( + void * pvBuffer, + FLMBOOL bMutexLocked = FALSE); +#endif + + FINLINE void lockMutex( void) + { + m_pSlabManager->lockMutex(); + } + + FINLINE void unlockMutex( void) + { + m_pSlabManager->unlockMutex(); + } + +private: + + F_FixedAlloc * getAllocator( + FLMUINT uiSize); + + F_FixedAlloc * getAllocator( + FLMBYTE * pucBuffer); + + void cleanup( void); + + F_SlabManager * m_pSlabManager; + FLMUINT * m_puiCellSizes; + F_FixedAlloc ** m_ppAllocators; +}; + +#endif // FLFIXED_H diff --git a/version5/src/flgethdr.cpp b/version5/src/flgethdr.cpp new file mode 100644 index 0000000..cbba4a9 --- /dev/null +++ b/version5/src/flgethdr.cpp @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the flmGetHdrInfo routine -- a routine which +// reads the header information from a FLAIM database, verifies it +// and returns the header information in a structure. +// +// 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: flgethdr.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/*************************************************************************** +Desc: This routine reads the header information in a FLAIM database, + verifies the password, and returns the file header and log + header information. +*****************************************************************************/ +RCODE flmGetHdrInfo( + F_SuperFileHdl * pSFileHdl, + XFLM_DB_HDR * pDbHdr, + FLMUINT32 * pui32CalcCRC) +{ + RCODE rc = NE_XFLM_OK; + IF_FileHdl * pCFileHdl; + + if( RC_BAD( rc = pSFileHdl->GetFileHdl( 0, FALSE, &pCFileHdl))) + { + goto Exit; + } + + rc = flmReadAndVerifyHdrInfo( NULL, pCFileHdl, pDbHdr, pui32CalcCRC); + +Exit: + + return( rc); +} diff --git a/version5/src/flindex.cpp b/version5/src/flindex.cpp new file mode 100644 index 0000000..2ac5581 --- /dev/null +++ b/version5/src/flindex.cpp @@ -0,0 +1,1194 @@ +//------------------------------------------------------------------------------ +// Desc: Contains FLAIM API routines that aid the user in managing indexes. +// +// 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: flindex.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE flmBackgroundIndexBuildThrd( + F_Thread * pThread); + +/**************************************************************************** +Desc : Return the status of the index. +Notes: +****************************************************************************/ +RCODE XFLMAPI F_Db::indexStatus( + FLMUINT uiIndexNum, + XFLM_INDEX_STATUS * pIndexStatus) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + F_BKGND_IX * pBackgroundIx; + FLMBOOL bMutexLocked = FALSE; + + flmAssert( pIndexStatus); + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if( m_eTransType != XFLM_NO_TRANS) + { + if( !okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + // Need to have at least a read transaction going. + + if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + pBackgroundIx = flmBackgroundIndexGet( m_pDatabase, uiIndexNum, TRUE); + if (pBackgroundIx) + { + f_memcpy( pIndexStatus, &pBackgroundIx->indexStatus, + sizeof( XFLM_INDEX_STATUS)); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + flmAssert( pIndexStatus->uiIndexNum == uiIndexNum); + } + else + { + IXD * pIxd; + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + // Populate the index status structure. + + f_memset( pIndexStatus, 0, sizeof( XFLM_INDEX_STATUS)); + pIndexStatus->uiIndexNum = uiIndexNum; + + if( !(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))) + { + pIndexStatus->ui64LastDocumentIndexed = ~((FLMUINT64)0); + pIndexStatus->eState = XFLM_INDEX_ONLINE; + } + else + { + // NOTE: If we are in a read transaction, the last node indexed + // value may not be read-consistent. It is only guaranteed to + // be correct for update transactions. + + pIndexStatus->ui64LastDocumentIndexed = pIxd->ui64LastDocIndexed; + pIndexStatus->eState = (pIxd->uiFlags & IXD_SUSPENDED) + ? XFLM_INDEX_SUSPENDED + : XFLM_INDEX_BRINGING_ONLINE; + } + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + if( bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Return the number of the next index. Pass in zero to get the + first index. +****************************************************************************/ +RCODE XFLMAPI F_Db::indexGetNext( + FLMUINT * puiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + IXD * pIxd; + + flmAssert( puiIndexNum != NULL); + + if( RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if( m_eTransType != XFLM_NO_TRANS) + { + if( !okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + + // Need to have at least a read transaction going. + + if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + if( (pIxd = m_pDict->getNextIndex( *puiIndexNum, FALSE)) == NULL) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + *puiIndexNum = pIxd->uiIndexNum; + +Exit: + + if (bStartedTrans) + { + abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc : Suspend the selected index from doing any key updates on records + that are equal or higher than the next record ID value + in the container that the index references. If the index is offline + then the background process will be suspended. If the index is + online then it will be suspended. If the index is already + suspended NE_XFLM_OK will be returned. A suspended index is not + persistent if the database goes down. +Notes: An update transaction will be started if necessary. +****************************************************************************/ +RCODE F_Db::indexSuspend( + FLMUINT uiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + FLMUINT64 ui64HighestDocId; + FLMBOOL bStartedTrans = FALSE; + F_COLLECTION * pCollection; + FLMBOOL bMustAbortOnError = FALSE; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + else if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + } + else + { + // Need to have an update transaction going. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // See if the index is valid + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (pIxd->uiFlags & IXD_SUSPENDED) + { + // Index is already suspended. + + goto Exit; + } + + // If the index is not currently offline, the highest node indexed + // is the collection's last document ID. Otherwise, it is + // simply the value that is stored in the IXD. + + if (!(pIxd->uiFlags & IXD_OFFLINE)) + { + if (RC_BAD( rc = m_pDict->getCollection( pIxd->uiCollectionNum, + &pCollection))) + { + goto Exit; + } + + ui64HighestDocId = pCollection->ui64LastDocId; + } + else + { + ui64HighestDocId = pIxd->ui64LastDocIndexed; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Must abort on error + + bMustAbortOnError = TRUE; + + // There may be a background thread still assigned to the + // index even though the index may be "on-line." This is because + // the background thread may have just commited a transaction that + // transitioned the index from off-line to on-line, but the thread + // has not yet exited (even though it will not do any more work + // to update the index). We want to wait for the thread to terminate + // before our transaction is allowed to commit. This is so that if + // we immediately call resume, it won't find the yet-to-terminate + // thread still running in the background. + + if (!(m_uiFlags & FDB_REPLAYING_RFL)) + { + if( RC_BAD( rc = addToStopList( uiIndexNum))) + { + goto Exit; + } + } + + if (RC_BAD( rc = setIxStateInfo( uiIndexNum, ui64HighestDocId, + IXD_SUSPENDED))) + { + goto Exit; + } + + // setIxStateInfo may have changed to a new dictionary, so pIxd is no + // good after this point + + pIxd = NULL; + + // Log the suspend packet to the RFL + + pRfl->enableLogging( &uiRflToken); + + if (RC_BAD( rc = m_pDatabase->m_pRfl->logIndexSuspendOrResume( + this, uiIndexNum, RFL_INDEX_SUSPEND_PACKET))) + { + goto Exit; + } + +Exit: + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc)) + { + if( bStartedTrans) + { + abortTrans(); + } + else if( bMustAbortOnError) + { + setMustAbortTrans( rc); + } + } + else if( bStartedTrans) + { + rc = commitTrans( 0, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc : If the index was suspended, restart the background process that + will get the index up to date so that it will eventually be online. + Returns NE_XFLM_OK with no change if the index is already online. +Notes: An update transaction will be started if necessary. +****************************************************************************/ +RCODE F_Db::indexResume( + FLMUINT uiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bMustAbortOnError = FALSE; + FLMUINT uiRflToken = 0; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + else if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + } + else + { + // Need to have an update transaction going. + + if (RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + // See if the index is valid + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (!(pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE))) + { + // Index is already on-line + + goto Exit; + } + + if (!(pIxd->uiFlags & IXD_SUSPENDED)) + { + // Index is not suspended. It is offline (see test + // above), but a thread should already be building the + // index. + + flmAssert( flmBackgroundIndexGet( m_pDatabase, + uiIndexNum, FALSE) != NULL); + + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Point of no return -- must abort on error + + bMustAbortOnError = TRUE; + + // Better not have a background thread running + + flmAssert( flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) == NULL); + + // Update the index state info to show "offline" + + if (RC_BAD( rc = setIxStateInfo( uiIndexNum, pIxd->ui64LastDocIndexed, + IXD_OFFLINE))) + { + goto Exit; + } + + // setIxStateInfo may have changed to a new dictionary, so pIxd is no + // good after this point + + pIxd = NULL; + + // Add an entry to the start list so that an indexing thread + // will be started when this transaction commits. + + if (!(m_uiFlags & FDB_REPLAYING_RFL)) + { + if (RC_BAD( rc = addToStartList( uiIndexNum))) + { + goto Exit; + } + } + + // Log the resume packet to the RFL + + pRfl->enableLogging( &uiRflToken); + + if (RC_BAD( rc = pRfl->logIndexSuspendOrResume( + this, uiIndexNum, RFL_INDEX_RESUME_PACKET))) + { + goto Exit; + } + +Exit: + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc)) + { + if( bStartedTrans) + { + abortTrans(); + } + else if( bMustAbortOnError) + { + setMustAbortTrans( rc); + } + } + else if( bStartedTrans) + { + rc = commitTrans( 0, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Add the index to the stop list of background threads. +****************************************************************************/ +RCODE F_Db::addToStopList( + FLMUINT uiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + F_BKGND_IX * pBackgroundIx; + F_BKGND_IX * pNextBackgroundIx; + + // We'd better not be replaying the RFL + + flmAssert( !(m_uiFlags & FDB_REPLAYING_RFL)); + + // First look in the start list and remove any index matches. + // This is need if you add an index and drop + // it within the same transaction. + + for( pBackgroundIx = m_pIxStartList; + pBackgroundIx; pBackgroundIx = pNextBackgroundIx) + { + pNextBackgroundIx = pBackgroundIx->pNext; + + if (pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) + { + if (pNextBackgroundIx) + { + pNextBackgroundIx->pPrev = pBackgroundIx->pPrev; + } + + if (pBackgroundIx->pPrev) + { + pBackgroundIx->pPrev->pNext = pNextBackgroundIx; + } + else + { + m_pIxStartList = pNextBackgroundIx; + } + + f_free( &pBackgroundIx); + } + } + + // See if we already have an entry in the stop list for the index. There + // is no reason to have the index in the list more than once. + + for (pBackgroundIx = m_pIxStopList; + pBackgroundIx; pBackgroundIx = pNextBackgroundIx) + { + pNextBackgroundIx = pBackgroundIx->pNext; + + if (pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) + { + goto Exit; // Should return NE_XFLM_OK + } + } + + // Allocate and add the thread structure to the thread list. + + if (RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), + &pBackgroundIx))) + { + goto Exit; + } + + pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; + pBackgroundIx->pPrev = NULL; + if ((pBackgroundIx->pNext = m_pIxStopList) != NULL) + { + m_pIxStopList->pPrev = pBackgroundIx; + } + m_pIxStopList = pBackgroundIx; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Add the index to the start list of background threads. +****************************************************************************/ +RCODE F_Db::addToStartList( + FLMUINT uiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + F_BKGND_IX * pBackgroundIx; + F_BKGND_IX * pNextBackgroundIx; + + // We'd better not be replaying the RFL + + flmAssert( !(m_uiFlags & FDB_REPLAYING_RFL)); + + // Look in the start list to make sure we don't already + // have an entry for this index. We don't want to + // start more than one thread per index. The background + // indexing code is not structured to handle multiple build + // threads on the same index. + + // NOTE: We don't want to remove any entries in the stop + // list corresponding to this index. The reason for this + // is the index may have been deleted, re-added, deleted, + // modified, etc. several times during the transaction. + // We want to make sure that an existing background indexing + // thread is terminated and a new one is started. The stop + // list is always processed first at transaction commit time. + // Then new indexing threads (in the start list) are started. + + for( pBackgroundIx = m_pIxStartList; + pBackgroundIx; pBackgroundIx = pNextBackgroundIx) + { + pNextBackgroundIx = pBackgroundIx->pNext; + + if (pBackgroundIx->indexStatus.uiIndexNum == uiIndexNum) + { + goto Exit; // Should return NE_XFLM_OK + } + } + + // Allocate and add the thread structure to the pDb thread list. + + if (RC_BAD( rc = f_calloc( (FLMUINT)( sizeof( F_BKGND_IX)), + &pBackgroundIx))) + { + goto Exit; + } + + pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; + pBackgroundIx->pPrev = NULL; + if ((pBackgroundIx->pNext = m_pIxStartList) != NULL) + { + m_pIxStartList->pPrev = pBackgroundIx; + } + m_pIxStartList = pBackgroundIx; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: After Abort and before we unlock, stop and start all indexing. +****************************************************************************/ +void F_Db::indexingAfterAbort( void) +{ + F_BKGND_IX * pStartIx; + F_BKGND_IX * pStopIx; + F_BKGND_IX * pNextIx; + + pStopIx = m_pIxStopList; + m_pIxStopList = NULL; + for( ; pStopIx; pStopIx = pNextIx) + { + pNextIx = pStopIx->pNext; + f_free( &pStopIx); + } + + pStartIx = m_pIxStartList; + m_pIxStartList = NULL; + for( ; pStartIx; pStartIx = pNextIx) + { + pNextIx = pStartIx->pNext; + f_free( &pStartIx); + } +} + +/**************************************************************************** +Desc: Stops a background indexing thread +Notes: This routine DOES NOT assume that the global mutex is locked. It + will lock and unlock the mutex as needed. +****************************************************************************/ +void F_Db::stopBackgroundIndexThread( + FLMUINT uiIndexNum, + FLMBOOL bWait, + FLMBOOL * pbStopped) +{ + F_BKGND_IX * pBackgroundIx; + FLMUINT uiThreadId; + FLMBOOL bMutexLocked = FALSE; + + if (pbStopped) + { + *pbStopped = FALSE; + } + + for (;;) + { + // Lock the global mutex + + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + // Get the background index + + if ((pBackgroundIx = flmBackgroundIndexGet( m_pDatabase, + uiIndexNum, TRUE, &uiThreadId)) == NULL) + { + if (pbStopped) + { + *pbStopped = TRUE; + } + goto Exit; + } + + // Set the thread's shutdown flag first. + + gv_XFlmSysData.pThreadMgr->setThreadShutdownFlag( uiThreadId); + + // Unlock the global mutex + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // The thread may be waiting to start a transaction. + + gv_XFlmSysData.pServerLockMgr->SignalLockWaiter( uiThreadId); + + if( !bWait) + { + break; + } + + // Wait for the thread to terminate + + f_sleep( 50); + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: After commit and before we unlock, stop and start all indexing. +****************************************************************************/ +void F_Db::indexingAfterCommit( void) +{ + F_BKGND_IX * pStartIx; + F_BKGND_IX * pStopIx; + F_BKGND_IX * pNextIx; + FLMBOOL bThreadsActive; + FLMBOOL bStopped; + + // Signal all background indexing threads in the stop list + // to shutdown. Poll until all have terminated. + + for( ;;) + { + bThreadsActive = FALSE; + for( pStopIx = m_pIxStopList; pStopIx; pStopIx = pStopIx->pNext) + { + stopBackgroundIndexThread( pStopIx->indexStatus.uiIndexNum, + FALSE, &bStopped); + if( !bStopped) + { + bThreadsActive = TRUE; + } + } + + if( !bThreadsActive) + { + break; + } + + f_sleep( 50); + } + + // Now that all of the threads have been stopped, discard the stop list + + pStopIx = m_pIxStopList; + m_pIxStopList = NULL; + for (; pStopIx; pStopIx = pNextIx) + { + pNextIx = pStopIx->pNext; + f_free( &pStopIx); + } + + // Start threads listed in the index start list. + + pStartIx = m_pIxStartList; + m_pIxStartList = NULL; + for (; pStartIx; pStartIx = pNextIx) + { + pNextIx = pStartIx->pNext; + (void)startIndexBuild( pStartIx->indexStatus.uiIndexNum); + f_free( &pStartIx); + } +} + +/**************************************************************************** +Desc: Thread that will build an index in the background. + Caller will create a pDb to use. This pDb must be + freed at the conclusion of the routine. +****************************************************************************/ +RCODE F_Db::startIndexBuild( + FLMUINT uiIndexNum) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiGMT; + IXD * pIxd; + F_BKGND_IX * pBackgroundIx = NULL; + char szThreadName[ F_PATH_MAX_SIZE]; + char szBaseName[ F_FILENAME_SIZE]; + + f_timeGetSeconds( &uiGMT ); + + if (flmBackgroundIndexGet( m_pDatabase, uiIndexNum, FALSE) != NULL) + { + // There is already a background thread running on this index. + + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + // Allocate the background thread and index status strucutures. + + if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_BKGND_IX), &pBackgroundIx))) + { + goto Exit; + } + + pBackgroundIx->pDatabase = m_pDatabase; + pBackgroundIx->indexStatus.eState = XFLM_INDEX_BRINGING_ONLINE; + pBackgroundIx->indexStatus.uiIndexNum = uiIndexNum; + pBackgroundIx->indexStatus.uiStartTime = uiGMT; + pBackgroundIx->indexStatus.ui64LastDocumentIndexed = + pIxd->ui64LastDocIndexed; + pBackgroundIx->indexStatus.ui64KeysProcessed = 0; + pBackgroundIx->indexStatus.ui64DocumentsProcessed = 0; + pBackgroundIx->indexStatus.ui64Transactions = 0; + + pBackgroundIx->uiIndexingAction = FTHREAD_ACTION_INDEX_OFFLINE; + pBackgroundIx->pPrev = NULL; + pBackgroundIx->pNext = NULL; + + // Generate the thread name + + if (RC_BAD( rc = gv_pFileSystem->pathReduce( m_pDatabase->m_pszDbPath, + szThreadName, szBaseName))) + { + goto Exit; + } + + f_sprintf( (char *)szThreadName, "BldIX %u (%s)", + (unsigned)uiIndexNum, szBaseName); + + // Start the thread in the background indexing thread group. + // The new thread will cleanup pBackgroundIx on termination. + + if (RC_BAD( rc = f_threadCreate( NULL, + flmBackgroundIndexBuildThrd, szThreadName, + FLM_BACKGROUND_INDEXING_THREAD_GROUP, uiIndexNum, + (void *)pBackgroundIx, NULL, 24000))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc) && pBackgroundIx) + { + f_free( &pBackgroundIx); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called by the thread that performs background + indexing. +****************************************************************************/ +RCODE F_Db::backgroundIndexBuild( + F_Thread * pThread, + FLMBOOL * pbShutdown, + FLMINT * piErrorLine + ) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); + FLMBOOL bStartedTrans = FALSE; + FLMUINT64 ui64FirstDocumentId; + FLMUINT uiIndexNum; + FLMBOOL bHitEnd; + XFLM_INDEX_STATUS savedIxStatus; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + *piErrorLine = (FLMINT)__LINE__; + goto Exit; + } + + flmAssert( m_eTransType == XFLM_NO_TRANS); + + m_uiFlags |= FDB_BACKGROUND_INDEXING; + uiIndexNum = pBackgroundIx->indexStatus.uiIndexNum; + m_pSFileHdl->enableFlushMinimize(); + + for (;;) + { + + // Set the thread's status + + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + + // See if we should shut down. + + if (pThread->getShutdownFlag()) + { + *pbShutdown = TRUE; + break; + } + + // Start a transaction + + if( RC_BAD( rc = beginBackgroundTrans( pThread))) + { + if (rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) + { + // This would only happen if we were signaled to shut down. + // So, it's ok to exit + + flmAssert( pThread->getShutdownFlag()); + *pbShutdown = TRUE; + rc = NE_XFLM_OK; + } + else + { + *piErrorLine = (FLMINT)__LINE__; + } + goto Exit; + } + bStartedTrans = TRUE; + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + + // Index may have been deleted by another transaction, or + // there may have been some other error. + + *piErrorLine = (FLMINT)__LINE__; + goto Exit; + } + + // If the index is suspended, this thread should have been + // shut down. The suspending thread will keep the database + // locked until we exit. So, if we have the database locked, + // the index better not be suspended. + + flmAssert( !(pIxd->uiFlags & IXD_SUSPENDED)); + pBackgroundIx->indexStatus.eState = XFLM_INDEX_BRINGING_ONLINE; + + if ((ui64FirstDocumentId = pIxd->ui64LastDocIndexed) == ~((FLMUINT64)0)) + { + goto Exit; + } + + // Setup the NODE range we want to index. + + ui64FirstDocumentId++; + + // Set the thread's status + + pThread->setThreadStatus( "Indexing %u:%I64u", + (unsigned)pIxd->uiCollectionNum, ui64FirstDocumentId); + + // Read and index up to the highest node ID (or node higher than ui64EndNodeId) + // or until time runs out. The 500 is millisecs to take for the transaction. + + f_memcpy( &savedIxStatus, + &pBackgroundIx->indexStatus, sizeof( XFLM_INDEX_STATUS)); + + if (RC_BAD( rc = indexSetOfDocuments( uiIndexNum, + ui64FirstDocumentId, ~((FLMUINT64)0), + NULL, NULL, + &pBackgroundIx->indexStatus, &bHitEnd, pThread))) + { + // Lock the mutex while copying the saved index status back to + // the main index status so that someone requesting the index status + // won't see the status while the memcpy is in progress. + + f_mutexLock( gv_XFlmSysData.hShareMutex); + f_memcpy( &pBackgroundIx->indexStatus, + &savedIxStatus, sizeof( XFLM_INDEX_STATUS)); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + *piErrorLine = (FLMINT)__LINE__; + goto Exit; + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_COMMITTING_TRANS); + + // Commit the transaction (even if we didn't do any indexing work). + + bStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, FALSE))) + { + *piErrorLine = (FLMINT)__LINE__; + goto Exit; + } + pBackgroundIx->indexStatus.ui64Transactions++; + + if (bHitEnd) + { + break; + } + } + +Exit: + + if (bStartedTrans) + { + (void)abortTrans(); + bStartedTrans = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Thread that will build an index in the background. + Caller will create a pDb to use. This pDb must be + freed at the conclusion of the routine. +****************************************************************************/ +FSTATIC RCODE flmBackgroundIndexBuildThrd( + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + F_BKGND_IX * pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); + F_Db * pDb = NULL; + FLMBOOL bForcedShutdown = FALSE; + FLMUINT uiIndexNum; + char szMsg [128]; + FLMINT iErrorLine = 0; + F_DbSystem dbSystem; + + pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); + +Loop_Again: + + rc = NE_XFLM_OK; + uiIndexNum = pBackgroundIx->indexStatus.uiIndexNum; + flmAssert( pThread->getThreadAppId() == uiIndexNum); + pDb = NULL; + + // We could loop forever on internalDbOpen errors, + // check if we should exit. + + if( pThread->getShutdownFlag()) + { + bForcedShutdown = TRUE; + goto Exit; + } + + if( RC_BAD( rc = dbSystem.internalDbOpen( + pBackgroundIx->pDatabase, &pDb))) + { + + // If the file is being closed, this is not an error. + + if (pBackgroundIx->pDatabase->getFlags() & DBF_BEING_CLOSED) + { + bForcedShutdown = TRUE; + rc = NE_XFLM_OK; + } + else + { + iErrorLine = (FLMINT)__LINE__; + } + goto Exit; + } + + if (RC_BAD( rc = pDb->backgroundIndexBuild( pThread, &bForcedShutdown, + &iErrorLine))) + { + goto Exit; + } + +Exit: + + pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); + + if (pDb) + { + pDb->Release(); + pDb = NULL; + } + + if (RC_BAD(rc) && !bForcedShutdown) + { + if (rc == NE_XFLM_MEM || rc == NE_XFLM_IO_DISK_FULL || + rc == NE_XFLM_MUST_WAIT_CHECKPOINT) + { + // Log the error + + f_sprintf( szMsg, + "Background indexing thread %u (index %u)", + (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); + flmLogError( rc, szMsg, __FILE__, iErrorLine); + + // Sleep a half second and try again. + + f_sleep( 500); + goto Loop_Again; + } + else + { + f_sprintf( szMsg, + "Background indexing thread %u (index %u) -- unrecoverable error.", + (unsigned)pThread->getThreadId(), (unsigned)uiIndexNum); + flmLogError( rc, szMsg, __FILE__, iErrorLine); + } + } + + // Set the thread's app ID to 0, so that it will not + // be found now that the thread is terminating (we don't + // want flmBackgroundIndexGet to find the thread). + + pThread->setThreadAppId( 0); + + // Free the background index structure + + f_mutexLock( gv_XFlmSysData.hShareMutex); + pThread->setParm1( NULL); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + f_free( &pBackgroundIx); + + return( rc); +} + +/**************************************************************************** +Desc: Looks for a background indexing thread that is running with + a matching action and value. +Note: The shared semaphore must be locked on the outside while + calling this routine and accessing anything within the F_BKGND_IX + structure. +****************************************************************************/ +F_BKGND_IX * flmBackgroundIndexGet( + F_Database * pDatabase, + FLMUINT uiIndexNum, + FLMBOOL bMutexLocked, + FLMUINT * puiThreadId) +{ + RCODE rc = NE_XFLM_OK; + F_Thread * pThread; + FLMUINT uiThreadId; + F_BKGND_IX * pBackgroundIx = NULL; + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + } + + uiThreadId = 0; + for( ;;) + { + if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( + &pThread, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &uiThreadId))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + RC_UNEXPECTED_ASSERT( rc); + } + } + + if (pThread->getThreadAppId()) + { + F_BKGND_IX * pTmpIx = NULL; + + pTmpIx = (F_BKGND_IX *)pThread->getParm1(); + if (pTmpIx->indexStatus.uiIndexNum == uiIndexNum && + pTmpIx->pDatabase == pDatabase) + { + flmAssert( pThread->getThreadAppId() == uiIndexNum); + pBackgroundIx = pTmpIx; + pThread->Release(); + if( puiThreadId) + { + *puiThreadId = uiThreadId; + } + break; + } + } + pThread->Release(); + } + + if (!bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + return( pBackgroundIx); +} diff --git a/version5/src/flkeyret.cpp b/version5/src/flkeyret.cpp new file mode 100644 index 0000000..7791a46 --- /dev/null +++ b/version5/src/flkeyret.cpp @@ -0,0 +1,331 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the F_Db::keyRetrieve method. +// +// 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: flkeyret.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/*API~*********************************************************************** +Area : RETRIEVAL +Desc: Retrieves a key from an index based on a passed-in GEDCOM tree and DRN. +*END************************************************************************/ +RCODE F_Db::keyRetrieve( + FLMUINT uiIndex, + // [IN] Index number. + IF_DataVector * ifpSearchKey, + // [IN] Pointer to the search key. + FLMUINT uiFlags, + // [IN] Flag (XFLM_FIRST, XFLM_LAST, XFLM_EXCL, XFLM_INCL, + // XFLM_EXACT, XFLM_KEY_EXACT + IF_DataVector * ifpFoundKey + // [OUT] Returns key that was found - may also have data components. + ) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd = NULL; + LFILE * pLFile; + FLMBYTE * pucSearchKey = NULL; + FLMBYTE * pucFoundKey = NULL; + void * pvMark = m_TempPool.poolMark(); + FLMUINT uiSearchKeyLen = 0; + FLMUINT uiFoundKeyLen; + FLMUINT uiOriginalFlags; + F_Btree * pbtree = NULL; + FLMUINT uiDataLen; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiIdMatchFlags = uiFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); + IXKeyCompare compareObject; + FLMBOOL bCompareDocId = FALSE; + FLMBOOL bCompareNodeIds = FALSE; + F_DataVector * pSearchKey = (F_DataVector *)ifpSearchKey; + F_DataVector * pFoundKey = (F_DataVector *)ifpFoundKey; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_eTransType == XFLM_NO_TRANS) + { + if( RC_BAD( rc = transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + + bStartedTrans = TRUE; + } + + // See if we have a transaction going which should be aborted. + + if( RC_BAD( m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + // Allocate key buffers. + + if (pSearchKey) + { + if (RC_BAD( rc = m_TempPool.poolAlloc( MAX_KEY_SIZ, + (void **)&pucSearchKey))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_TempPool.poolAlloc( MAX_KEY_SIZ, (void **)&pucFoundKey))) + { + goto Exit; + } + + // Make sure it is a valid index definition + + if (RC_BAD( rc = m_pDict->getIndex( uiIndex, &pLFile, &pIxd))) + { + goto Exit; + } + + if (RC_BAD( rc = flushKeys())) + { + goto Exit; + } + + if (uiFlags & XFLM_FIRST || (!pSearchKey && + !(uiFlags & XFLM_FIRST) && !(uiFlags & XFLM_LAST))) + { + uiOriginalFlags = uiFlags = XFLM_FIRST; + } + else if (uiFlags & XFLM_LAST) + { + uiOriginalFlags = uiFlags = XFLM_LAST; + } + else + { + uiOriginalFlags = uiFlags; + if( !(uiIdMatchFlags & XFLM_MATCH_IDS)) + { + flmAssert( !(uiFlags & XFLM_KEY_EXACT)); + if (uiFlags & XFLM_EXCL) + { + uiFlags = XFLM_EXCL; + } + else if (uiFlags & XFLM_EXACT) + { + uiOriginalFlags = XFLM_EXACT | XFLM_KEY_EXACT; + uiFlags = XFLM_INCL; + } + else + { + uiFlags = XFLM_INCL; + } + } + else + { + if (uiFlags & XFLM_EXACT) + { + flmAssert( !(uiFlags & XFLM_KEY_EXACT)); + uiFlags = XFLM_EXACT; + } + else if (uiFlags & XFLM_EXCL) + { + uiFlags = XFLM_EXCL; + } + else + { + uiFlags = XFLM_INCL; + } + } + + if (RC_BAD( rc = pSearchKey->outputKey( pIxd, uiIdMatchFlags, + pucSearchKey, MAX_KEY_SIZ, &uiSearchKeyLen, SEARCH_KEY_FLAG))) + { + goto Exit; + } + + // If we are not matching on the IDs and this is an XFLM_EXCL + // search, tack on a 0xFF for the IDs, which should get us past + // all keys that match. We need to turn on the match IDs flags + // in this case so that the comparison routine will match on the + // 0xFF. + + if (!uiIdMatchFlags && (uiFlags & XFLM_EXCL)) + { + pucSearchKey [uiSearchKeyLen++] = 0xFF; + bCompareDocId = TRUE; + bCompareNodeIds = TRUE; + } + else + { + if (uiIdMatchFlags & XFLM_MATCH_IDS) + { + bCompareNodeIds = TRUE; + bCompareDocId = TRUE; + } + else if (uiIdMatchFlags & XFLM_MATCH_DOC_ID) + { + bCompareDocId = TRUE; + } + } + } + + compareObject.setIxInfo( this, pIxd); + compareObject.setCompareNodeIds( bCompareNodeIds); + compareObject.setCompareDocId( bCompareDocId); + compareObject.setSearchKey( pSearchKey); + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + + if( RC_BAD( rc = pbtree->btOpen( this, pLFile, + (pIxd->uiFlags & IXD_ABS_POS) + ? TRUE + : FALSE, + (pIxd->pFirstData) + ? TRUE + : FALSE, &compareObject))) + { + goto Exit; + } + + // Search the for the key + + if (uiSearchKeyLen) + { + f_memcpy( pucFoundKey, pucSearchKey, uiSearchKeyLen); + } + uiFoundKeyLen = uiSearchKeyLen; + + if( RC_BAD( rc = pbtree->btLocateEntry( + pucFoundKey, MAX_KEY_SIZ, &uiFoundKeyLen, uiFlags, NULL, + &uiDataLen))) + { + if (rc == NE_XFLM_EOF_HIT && uiOriginalFlags & XFLM_EXACT) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + } + goto Exit; + } + + // See if we are in the same key + + if (uiOriginalFlags & XFLM_KEY_EXACT) + { + FLMINT iTmpCmp; + + if (RC_BAD( rc = ixKeyCompare( this, pIxd, + (F_DataVector *)pSearchKey, NULL, NULL, + (uiIdMatchFlags == XFLM_MATCH_DOC_ID) ? TRUE : FALSE, + FALSE, pucFoundKey, uiFoundKeyLen, + pucSearchKey, uiSearchKeyLen, &iTmpCmp))) + { + goto Exit; + } + + if (iTmpCmp != 0) + { + rc = (uiOriginalFlags & (XFLM_INCL | XFLM_EXCL)) + ? RC_SET( NE_XFLM_EOF_HIT) + : RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + } + + // Parse the found key into its individual components + + if (pFoundKey) + { + pFoundKey->reset(); + if (RC_BAD( rc = pFoundKey->inputKey( pIxd, pucFoundKey, uiFoundKeyLen))) + { + goto Exit; + } + + // See if there is a data part + + if (pIxd->pFirstData) + { + FLMUINT uiDataBufSize; + FLMBYTE * pucData; + + // If the data will fit in the search key buffer, just + // reuse it since we are not going to do anything with + // it after this. Otherwise, allocate a new buffer. + + if (uiDataLen <= MAX_KEY_SIZ && pucSearchKey) + { + uiDataBufSize = MAX_KEY_SIZ; + pucData = pucSearchKey; + } + else + { + uiDataBufSize = uiDataLen; + if (RC_BAD( rc = m_TempPool.poolAlloc( uiDataBufSize, + (void **)&pucData))) + { + goto Exit; + } + } + + // Retrieve the data + + if (RC_BAD( rc = pbtree->btGetEntry( + pucFoundKey, MAX_KEY_SIZ, uiFoundKeyLen, + pucData, uiDataBufSize, &uiDataLen))) + { + goto Exit; + } + + // Parse the data + + if (RC_BAD( rc = pFoundKey->inputData( pIxd, pucData, uiDataLen))) + { + goto Exit; + } + } + } + +Exit: + + m_TempPool.poolReset( pvMark); + + if (pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + if( bStartedTrans) + { + transAbort(); + } + + return( rc); +} diff --git a/version5/src/flmstat.cpp b/version5/src/flmstat.cpp new file mode 100644 index 0000000..f4432d0 --- /dev/null +++ b/version5/src/flmstat.cpp @@ -0,0 +1,1450 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains routines for updating FLAIM statistics. +// +// Tabs: 3 +// +// Copyright (c) 1997-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: flmstat.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE flmStatGetDbByName( + XFLM_STATS * pFlmStats, + char * pszDbName, + FLMUINT uiLowStart, + XFLM_DB_STATS ** ppDbStatsRV, + FLMUINT * puiDBAllocSeqRV, + FLMUINT * puiDbTblPosRV); + +FSTATIC void flmUpdateLFileStats( + XFLM_LFILE_STATS * pDest, + XFLM_LFILE_STATS * pSrc); + +FSTATIC RCODE flmUpdateDbStats( + XFLM_DB_STATS * pDest, + XFLM_DB_STATS * pSrc); + +FSTATIC FLMUINT flmDaysInMonth( + FLMUINT uiYear, + FLMUINT uiMonth); + +FSTATIC void flmAdjustTime( + F_TMSTAMP * pTime, + FLMINT iStartPoint); + +/**************************************************************************** +Desc: This routine returns a pointer to a particular database's + statistics block. +****************************************************************************/ +FSTATIC RCODE flmStatGetDbByName( + XFLM_STATS * pFlmStats, + char * pszDbName, + FLMUINT uiLowStart, + XFLM_DB_STATS ** ppDbStatsRV, + FLMUINT * puiDBAllocSeqRV, + FLMUINT * puiDbTblPosRV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTblSize; + XFLM_DB_STATS * pDbStatTbl; + FLMUINT uiLow; + FLMUINT uiMid = 0; + FLMUINT uiHigh; + FLMINT iCmp = 0; + FLMUINT uiNewSize; + FLMUINT uiElement; + char * pszTmpDbName = NULL; + + // If there is a database array, search it first. + + if (((pDbStatTbl = pFlmStats->pDbStats) != NULL) && + ((uiTblSize = pFlmStats->uiNumDbStats) != 0)) + { + for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + // See if we found a match. + +#ifdef FLM_UNIX + if ((iCmp = f_strcmp( pszDbName, + pDbStatTbl [uiMid].pszDbName)) == 0) +#else + if ((iCmp = f_stricmp( pszDbName, + pDbStatTbl [uiMid].pszDbName)) == 0) +#endif + { + + // Found match. + + *ppDbStatsRV = &pDbStatTbl [uiMid]; + if (puiDBAllocSeqRV) + { + *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; + } + if (puiDbTblPosRV) + { + *puiDbTblPosRV = uiMid; + } + goto Exit; + } + + // Check if we are done - where uiLow equals uiHigh. + + if (uiLow >= uiHigh) + { + + // Item not found. + + break; + } + + if (iCmp < 0) + { + if (uiMid == uiLowStart) + { + break; // Way too high? + } + uiHigh = uiMid - 1; // Too high + } + else + { + if (uiMid == uiTblSize) + { + break; // Done - Hit the top + } + uiLow = uiMid + 1; // Too low + } + } + } + + // If the array is full, or was never allocated, allocate a new one. + + if (pFlmStats->uiDbStatArraySize <= pFlmStats->uiNumDbStats) + { + if (!pFlmStats->pDbStats) + { + uiNewSize = INIT_DB_STAT_ARRAY_SIZE; + } + else + { + uiNewSize = pFlmStats->uiDbStatArraySize + + DB_STAT_ARRAY_INCR_SIZE; + } + if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( XFLM_DB_STATS) * uiNewSize), + &pDbStatTbl))) + { + goto Exit; + } + + // Save whatever was in the old table, if any. + + if (pFlmStats->pDbStats && pFlmStats->uiNumDbStats) + { + f_memcpy( pDbStatTbl, pFlmStats->pDbStats, + (FLMINT)(sizeof( XFLM_DB_STATS) * pFlmStats->uiNumDbStats)); + } + if (pFlmStats->pDbStats) + { + f_free( &pFlmStats->pDbStats); + } + + pFlmStats->uiDBAllocSeq++; + pFlmStats->pDbStats = pDbStatTbl; + pFlmStats->uiDbStatArraySize = uiNewSize; + } + + // Allocate space for the database name + + if (RC_BAD( rc = f_alloc( f_strlen( pszDbName) + 1, &pszTmpDbName))) + { + goto Exit; + } + + // Insert the item into the array. + + if (iCmp != 0) + { + uiElement = pFlmStats->uiNumDbStats; + + // If our new database number is greater than database number of the + // element pointed to by uiMid, increment uiMid so that the new + // database number will be inserted after it instead of before it. + + if (iCmp > 0) + { + uiMid++; + } + + // Move everything up in the array, including the slot pointed to + // by uiMid. + + while (uiElement > uiMid) + { + f_memcpy( &pDbStatTbl [uiElement], &pDbStatTbl [uiElement - 1], + sizeof( XFLM_DB_STATS)); + uiElement--; + } + f_memset( &pDbStatTbl [uiMid], 0, sizeof( XFLM_DB_STATS)); + } + pDbStatTbl [uiMid].pszDbName = pszTmpDbName; + pszTmpDbName = NULL; + f_strcpy( pDbStatTbl [uiMid].pszDbName, pszDbName); + pFlmStats->uiNumDbStats++; + *ppDbStatsRV = &pDbStatTbl [uiMid]; + if (puiDBAllocSeqRV) + { + *puiDBAllocSeqRV = pFlmStats->uiDBAllocSeq; + } + if (puiDbTblPosRV) + { + *puiDbTblPosRV = uiMid; + } +Exit: + if (pszTmpDbName) + { + f_free( &pszTmpDbName); + } + return( rc); +} + +/**************************************************************************** +Desc: This routine returns a pointer to a particular database's + statistics block. +****************************************************************************/ +RCODE flmStatGetDb( + XFLM_STATS * pFlmStats, + F_Database * pDatabase, + FLMUINT uiLowStart, + XFLM_DB_STATS ** ppDbStatsRV, + FLMUINT * puiDBAllocSeqRV, + FLMUINT * puiDbTblPosRV) +{ + if (!pFlmStats) + { + *ppDbStatsRV = NULL; + if (puiDBAllocSeqRV) + { + *puiDBAllocSeqRV = 0; + } + if (puiDbTblPosRV) + { + *puiDbTblPosRV = 0; + } + return( NE_XFLM_OK); + } + + return( flmStatGetDbByName( pFlmStats, pDatabase->getDbNamePtr(), + uiLowStart, ppDbStatsRV, puiDBAllocSeqRV, + puiDbTblPosRV)); +} + +/**************************************************************************** +Desc: This routine returns a pointer to a particular logical file in a + particular database's statistics block. +****************************************************************************/ +RCODE flmStatGetLFile( + XFLM_DB_STATS * pDbStats, + FLMUINT uiLFileNum, + eLFileType eLfType, + FLMUINT uiLowStart, + XFLM_LFILE_STATS ** ppLFileStatsRV, + FLMUINT * puiLFileAllocSeqRV, + FLMUINT * puiLFileTblPosRV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTblSize; + XFLM_LFILE_STATS * pLFileStatTbl; + XFLM_LFILE_STATS * pLFileCurrStat; + FLMUINT uiLow; + FLMUINT uiMid = 0; + FLMUINT uiHigh; + FLMINT iCmp = 0; + FLMUINT uiNewSize; + FLMUINT uiElement; + + if (!pDbStats) + { + *ppLFileStatsRV = NULL; + if (puiLFileAllocSeqRV) + { + *puiLFileAllocSeqRV = 0; + } + if (puiLFileTblPosRV) + { + *puiLFileTblPosRV = 0; + } + goto Exit; + } + + // If there is a database array, search it first. + + if (((pLFileStatTbl = pDbStats->pLFileStats) != NULL) && + ((uiTblSize = pDbStats->uiNumLFileStats) != 0)) + { + for (uiHigh = --uiTblSize, uiLow = uiLowStart ; ; ) // Optimize: reduce uiTblSize + { + uiMid = (uiLow + uiHigh) >> 1; // (uiLow + uiHigh) / 2 + + // See if we found a match. + + pLFileCurrStat = &pLFileStatTbl [uiMid]; + if (uiLFileNum < pLFileCurrStat->uiLFileNum) + { + iCmp = -1; + } + else if (uiLFileNum > pLFileCurrStat->uiLFileNum) + { + iCmp = 1; + } + else if (eLfType < pLFileCurrStat->eLfType) + { + iCmp = -1; + } + else if (eLfType > pLFileCurrStat->eLfType) + { + iCmp = 1; + } + else + { + + // Found match. + + *ppLFileStatsRV = pLFileCurrStat; + if (puiLFileAllocSeqRV) + { + *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; + } + if (puiLFileTblPosRV) + { + *puiLFileTblPosRV = uiMid; + } + goto Exit; + } + + // Check if we are done - where uiLow equals uiHigh. + + if (uiLow >= uiHigh) + { + + // Item not found. + + break; + } + + if (iCmp < 0) + { + if (uiMid == uiLowStart) + { + break; // Way too high? + } + uiHigh = uiMid - 1; // Too high + } + else + { + if (uiMid == uiTblSize) + { + break; // Done - Hit the top + } + uiLow = uiMid + 1; // Too low + } + } + } + + // If the array is full, or was never allocated, allocate a new one. + + if (pDbStats->uiLFileStatArraySize <= pDbStats->uiNumLFileStats) + { + if (!pDbStats->pLFileStats) + { + uiNewSize = INIT_LFILE_STAT_ARRAY_SIZE; + } + else + { + uiNewSize = pDbStats->uiLFileStatArraySize + + LFILE_STAT_ARRAY_INCR_SIZE; + } + if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( XFLM_LFILE_STATS) * uiNewSize), + &pLFileStatTbl))) + { + goto Exit; + } + + // Save whatever was in the old table, if any. + + if ((pDbStats->pLFileStats) && + (pDbStats->uiNumLFileStats)) + { + f_memcpy( pLFileStatTbl, pDbStats->pLFileStats, + (FLMUINT)(sizeof( XFLM_LFILE_STATS) * + pDbStats->uiNumLFileStats)); + } + if (pDbStats->pLFileStats) + { + f_free( &pDbStats->pLFileStats); + } + + pDbStats->uiLFileAllocSeq++; + pDbStats->pLFileStats = pLFileStatTbl; + pDbStats->uiLFileStatArraySize = uiNewSize; + } + + // Insert the item into the array. + + if (iCmp != 0) + { + uiElement = pDbStats->uiNumLFileStats; + + // If our new database number is greater than database number of the + // element pointed to by uiMid, increment uiMid so that the new + // database number will be inserted after it instead of before it. + + if (iCmp > 0) + { + uiMid++; + } + + // Move everything up in the array, including the slot pointed to + // by uiMid. + + while (uiElement > uiMid) + { + f_memcpy( &pLFileStatTbl [uiElement], &pLFileStatTbl [uiElement - 1], + sizeof( XFLM_LFILE_STATS)); + uiElement--; + } + f_memset( &pLFileStatTbl [uiMid], 0, sizeof( XFLM_LFILE_STATS)); + } + pLFileStatTbl [uiMid].uiLFileNum = uiLFileNum; + pLFileStatTbl [uiMid].eLfType = eLfType; + pDbStats->uiNumLFileStats++; + *ppLFileStatsRV = &pLFileStatTbl [uiMid]; + if (puiLFileAllocSeqRV) + { + *puiLFileAllocSeqRV = pDbStats->uiLFileAllocSeq; + } + if (puiLFileTblPosRV) + { + *puiLFileTblPosRV = uiMid; + } +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine returns a pointer to a particular LFILE's + statistics block. +****************************************************************************/ +XFLM_LFILE_STATS * F_Db::getLFileStatPtr( + LFILE * pLFile) +{ + if (!pLFile) + { + return( (XFLM_LFILE_STATS *)NULL); + } + + if ((!m_pLFileStats) || + (m_uiLFileAllocSeq != + m_pDbStats->uiLFileAllocSeq) || + (m_pLFileStats->uiLFileNum != pLFile->uiLfNum)) + { + if (RC_BAD( flmStatGetLFile( m_pDbStats, pLFile->uiLfNum, + pLFile->eLfType, 0, &m_pLFileStats, + &m_uiLFileAllocSeq, NULL))) + { + m_pLFileStats = NULL; + m_uiLFileAllocSeq = 0; + } + } + return( m_pLFileStats); +} + +/**************************************************************************** +Desc: This routine resets the statistics in a FLM_STAT structure. +****************************************************************************/ +void flmStatReset( + XFLM_STATS * pStats, + FLMBOOL bFree) +{ + FLMUINT uiDb; + XFLM_DB_STATS * pDbStats; + FLMUINT uiLFile; + XFLM_LFILE_STATS * pLFile; + + if ((pDbStats = pStats->pDbStats) != NULL) + { + for (uiDb = 0; uiDb < pStats->uiNumDbStats; uiDb++, pDbStats++) + { + if ((pLFile = pDbStats->pLFileStats) != NULL) + { + if (bFree) + { + f_free( &pDbStats->pLFileStats); + } + else + { + for (uiLFile = 0; + uiLFile < pDbStats->uiNumLFileStats; + uiLFile++, pLFile++) + { + FLMUINT uiSaveLFileNum = pLFile->uiLFileNum; + eLFileType eSaveLfType = pLFile->eLfType; + + f_memset( pLFile, 0, sizeof( XFLM_LFILE_STATS)); + pLFile->uiLFileNum = uiSaveLFileNum; + pLFile->eLfType = eSaveLfType; + } + } + } + if (!bFree) + { + char * pszSaveDbName; + FLMUINT uiSaveLFileAllocSeq = pDbStats->uiLFileAllocSeq; + XFLM_LFILE_STATS * pSaveLFileStats = pDbStats->pLFileStats; + FLMUINT uiSaveLFileStatArraySize = + pDbStats->uiLFileStatArraySize; + FLMUINT uiSaveNumLFileStats = pDbStats->uiNumLFileStats; + + pszSaveDbName = pDbStats->pszDbName; + f_memset( pDbStats, 0, sizeof( XFLM_DB_STATS)); + pDbStats->pszDbName = pszSaveDbName; + pDbStats->uiLFileAllocSeq = uiSaveLFileAllocSeq; + pDbStats->pLFileStats = pSaveLFileStats; + pDbStats->uiLFileStatArraySize = uiSaveLFileStatArraySize; + pDbStats->uiNumLFileStats = uiSaveNumLFileStats; + } + else + { + f_free( &pDbStats->pszDbName); + } + } + if (bFree) + { + f_free( &pStats->pDbStats); + } + } + if ((bFree) || (!pDbStats)) + { + pStats->pDbStats = NULL; + pStats->uiDbStatArraySize = 0; + pStats->uiNumDbStats = 0; + } + pStats->uiStartTime = 0; + pStats->uiStopTime = 0; + + if (pStats->bCollectingStats) + { + f_timeGetSeconds( &pStats->uiStartTime); + } +} + +/**************************************************************************** +Desc: This routine updates statistics from one XFLM_RTRANS_STATS structure + into another. +****************************************************************************/ +FINLINE void flmUpdateRTransStats( + XFLM_RTRANS_STATS * pDest, + XFLM_RTRANS_STATS * pSrc) +{ + flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); + flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); +} + +/**************************************************************************** +Desc: This routine updates statistics from one XFLM_UTRANS_STATS structure + into another. +****************************************************************************/ +FINLINE void flmUpdateUTransStats( + XFLM_UTRANS_STATS * pDest, + XFLM_UTRANS_STATS * pSrc) +{ + flmUpdateCountTimeStats( &pDest->CommittedTrans, &pSrc->CommittedTrans); + flmUpdateCountTimeStats( &pDest->GroupCompletes, &pSrc->GroupCompletes); + pDest->ui64GroupFinished += pSrc->ui64GroupFinished; + flmUpdateCountTimeStats( &pDest->AbortedTrans, &pSrc->AbortedTrans); +} + +/**************************************************************************** +Desc: This routine updates statistics from one XFLM_BLOCKIO_STATS structure + into another. +****************************************************************************/ +void flmUpdateBlockIOStats( + XFLM_BLOCKIO_STATS * pDest, + XFLM_BLOCKIO_STATS * pSrc) +{ + flmUpdateDiskIOStats( &pDest->BlockReads, &pSrc->BlockReads); + flmUpdateDiskIOStats( &pDest->OldViewBlockReads, + &pSrc->OldViewBlockReads); + pDest->uiBlockChkErrs += pSrc->uiBlockChkErrs; + pDest->uiOldViewBlockChkErrs += pSrc->uiOldViewBlockChkErrs; + pDest->uiOldViewErrors += pSrc->uiOldViewErrors; + flmUpdateDiskIOStats( &pDest->BlockWrites, &pSrc->BlockWrites); +} + +/**************************************************************************** +Desc: This routine updates statistics from one XFLM_LFILE_STATS structure + into another. +****************************************************************************/ +FSTATIC void flmUpdateLFileStats( + XFLM_LFILE_STATS * pDest, + XFLM_LFILE_STATS * pSrc) +{ + pDest->bHaveStats = TRUE; + flmUpdateBlockIOStats( &pDest->RootBlockStats, &pSrc->RootBlockStats); + flmUpdateBlockIOStats( &pDest->MiddleBlockStats, &pSrc->MiddleBlockStats); + flmUpdateBlockIOStats( &pDest->LeafBlockStats, &pSrc->LeafBlockStats); + pDest->ui64BlockSplits += pSrc->ui64BlockSplits; + pDest->ui64BlockCombines += pSrc->ui64BlockCombines; +} + +/**************************************************************************** +Desc: This routine updates statistics from one XFLM_DB_STATS structure into + another. +****************************************************************************/ +FSTATIC RCODE flmUpdateDbStats( + XFLM_DB_STATS * pDestDb, + XFLM_DB_STATS * pSrcDb) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSrcLFile; + FLMUINT uiDestLFile; + XFLM_LFILE_STATS * pDestLFile; + XFLM_LFILE_STATS * pSrcLFile; + FLMUINT uiLowLFileStart; + FLMUINT uiSaveNumLFiles; + + flmUpdateRTransStats( &pDestDb->ReadTransStats, + &pSrcDb->ReadTransStats); + flmUpdateUTransStats( &pDestDb->UpdateTransStats, + &pSrcDb->UpdateTransStats); + pDestDb->bHaveStats = TRUE; + flmUpdateBlockIOStats( &pDestDb->LFHBlockStats, + &pSrcDb->LFHBlockStats); + flmUpdateBlockIOStats( &pDestDb->AvailBlockStats, + &pSrcDb->AvailBlockStats); + flmUpdateDiskIOStats( &pDestDb->DbHdrWrites, + &pSrcDb->DbHdrWrites); + flmUpdateDiskIOStats( &pDestDb->LogBlockWrites, + &pSrcDb->LogBlockWrites); + flmUpdateDiskIOStats( &pDestDb->LogBlockRestores, + &pSrcDb->LogBlockRestores); + flmUpdateDiskIOStats( &pDestDb->LogBlockReads, + &pSrcDb->LogBlockReads); + pDestDb->uiLogBlockChkErrs += pSrcDb->uiLogBlockChkErrs; + pDestDb->uiReadErrors += pSrcDb->uiReadErrors; + pDestDb->uiWriteErrors += pSrcDb->uiWriteErrors; + flmUpdateCountTimeStats( &pDestDb->NoLocks, &pSrcDb->NoLocks); + flmUpdateCountTimeStats( &pDestDb->WaitingForLock, + &pSrcDb->WaitingForLock); + flmUpdateCountTimeStats( &pDestDb->HeldLock, &pSrcDb->HeldLock); + + // Go through the LFILE statistics. + + for (uiDestLFile = 0, uiSrcLFile = 0, uiLowLFileStart = 0, + pSrcLFile = pSrcDb->pLFileStats; + uiSrcLFile < pSrcDb->uiNumLFileStats; + uiSrcLFile++, pSrcLFile++) + { + if (!pSrcLFile->bHaveStats) + { + continue; + } + + // Find or add the store in the destination store array. + + uiSaveNumLFiles = pDestDb->uiNumLFileStats; + if (RC_BAD( rc = flmStatGetLFile( pDestDb, pSrcLFile->uiLFileNum, + pSrcLFile->eLfType, + uiLowLFileStart, &pDestLFile, NULL, + &uiLowLFileStart))) + { + goto Exit; + } + + if (uiLowLFileStart < pDestDb->uiNumLFileStats - 1) + { + uiLowLFileStart++; + } + + // If we created the LFILE, all we have to do is copy the + // LFILE statistics. It will be quicker. + + if (uiSaveNumLFiles != pDestDb->uiNumLFileStats) + { + f_memcpy( pDestLFile, pSrcLFile, sizeof( XFLM_LFILE_STATS)); + } + else + { + + // LFILE was already present, need to go through and + // update the statistics. + + flmUpdateLFileStats( pDestLFile, pSrcLFile); + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine updates statistics from one FLM_STAT structure into + the global statistics. +****************************************************************************/ +RCODE flmStatUpdate( + XFLM_STATS * pSrcStats) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSrcDb; + FLMUINT uiDestDb; + XFLM_DB_STATS * pDestDb; + XFLM_DB_STATS * pSrcDb; + FLMUINT uiLowDbStart; + + // Do not update the statistics if the source statistics were started + // at an earlier time that the destination statistics start time. + + if (!gv_XFlmSysData.Stats.bCollectingStats || + pSrcStats->uiStartTime < gv_XFlmSysData.Stats.uiStartTime) + { + return( NE_XFLM_OK); + } + + f_mutexLock( gv_XFlmSysData.hStatsMutex); + + // Go through each of the source's databases + + for ( uiDestDb = 0, uiSrcDb = 0, uiLowDbStart = 0, + pSrcDb = pSrcStats->pDbStats; + uiSrcDb < pSrcStats->uiNumDbStats; + uiSrcDb++, pSrcDb++) + { + if (!pSrcDb->bHaveStats) + { + continue; + } + + // Find or add the store in the destination store array. + + if (RC_BAD( rc = flmStatGetDbByName( &gv_XFlmSysData.Stats, + pSrcDb->pszDbName, + uiLowDbStart, &pDestDb, NULL, + &uiLowDbStart))) + { + goto Exit; + } + + if (uiLowDbStart < gv_XFlmSysData.Stats.uiNumDbStats - 1) + { + uiLowDbStart++; + } + + if (RC_BAD( rc = flmUpdateDbStats( pDestDb, pSrcDb))) + { + goto Exit; + } + } + +Exit: + + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + + if (RC_OK( rc)) + { + flmStatReset( pSrcStats, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine frees however many queries that are over the limit of + the number we can save. +Note: This routine will ALWAYS unlock the query mutex on leaving. It may + be locked when entering. +****************************************************************************/ +void flmFreeSavedQueries( + FLMBOOL bMutexAlreadyLocked) +{ + QUERY_HDR * pQueriesToFree = NULL; + + // Must determine the queries to free inside the mutex lock and then free + // them outside the mutex lock, because freeing a query may cause an + // embedded query to be put into the list again, which will cause the + // mutex to be locked again. + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hQueryMutex); + } + while (gv_XFlmSysData.uiQueryCnt > gv_XFlmSysData.uiMaxQueries) + { + gv_XFlmSysData.pOldestQuery = gv_XFlmSysData.pOldestQuery->pPrev; + gv_XFlmSysData.uiQueryCnt--; + } + + // Whatever is found after pOldestQuery should be freed. Unlink + // those from the list and point to them with pQueriesToFree. + + if (!gv_XFlmSysData.pOldestQuery) + { + pQueriesToFree = gv_XFlmSysData.pNewestQuery; + gv_XFlmSysData.pNewestQuery = NULL; + } + else if (gv_XFlmSysData.pOldestQuery->pNext) + { + pQueriesToFree = gv_XFlmSysData.pOldestQuery->pNext; + pQueriesToFree->pPrev = NULL; + gv_XFlmSysData.pOldestQuery->pNext = NULL; + } + f_mutexUnlock( gv_XFlmSysData.hQueryMutex); + + // Now clean up each of the queries in the pQueriesToFree list. + // This can be done outside the mutex lock because we are now + // dealing with a completely local list that no other thread can + // see. Also, the mutex must NOT be locked at this point because + // FlmCursorCleanup may free an embedded query, which will want + // to lock the mutex again. + + while (pQueriesToFree) + { + QUERY_HDR * pQueryHdrToFree = pQueriesToFree; + + pQueriesToFree = pQueriesToFree->pNext; +// VISIT + flmAssert( 0); +// pQueryHdrToFree->pCursor->cleanup(); + f_free( &pQueryHdrToFree); + } +} + +/**************************************************************************** +Desc: This routine saves a query so it can be analyzed later. +****************************************************************************/ +void flmSaveQuery( + F_Query * pQuery) +{ + QUERY_HDR * pQueryHdr = NULL; + FLMBOOL bNeedToCleanup = TRUE; + FLMBOOL bMutexLocked = FALSE; + + // Allocate memory for the new query + + if (RC_BAD( f_calloc( sizeof( QUERY_HDR), &pQueryHdr))) + { + goto Exit; + } + + pQueryHdr->pQuery = pQuery; + + f_mutexLock( gv_XFlmSysData.hQueryMutex); + bMutexLocked = TRUE; + + // uiMaxQueries was originally checked outside of the mutex lock. + // Make sure it is still non-zero. + + if (gv_XFlmSysData.uiMaxQueries) + { + + // Link query to head of list. + + bNeedToCleanup = FALSE; + if ((pQueryHdr->pNext = gv_XFlmSysData.pNewestQuery) != NULL) + { + pQueryHdr->pNext->pPrev = pQueryHdr; + } + else + { + gv_XFlmSysData.pOldestQuery = pQueryHdr; + } + gv_XFlmSysData.pNewestQuery = pQueryHdr; + + if (++gv_XFlmSysData.uiQueryCnt > gv_XFlmSysData.uiMaxQueries) + { + flmFreeSavedQueries( TRUE); + + // flmFreeSavedQueries will always unlock the mutex. + + bMutexLocked = FALSE; + } + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hQueryMutex); + } + + // Must clean up the query if we didn't get it into the list for + // some reason. + + if (bNeedToCleanup) + { + if (pQueryHdr) + { + f_free( &pQueryHdr); + } +// VISIT + flmAssert( 0); +// pCursor->cleanup(); + } +} + +/**************************************************************************** +Desc: This routine copies statistics from one FLM_STAT structure into + another. This is used to retrieve statistics. +****************************************************************************/ +RCODE flmStatCopy( + XFLM_STATS * pDestStats, + XFLM_STATS * pSrcStats) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDb; + XFLM_DB_STATS * pDestDb; + XFLM_DB_STATS * pSrcDb; + FLMUINT uiCount; + FLMUINT uiLoop; + XFLM_DB_STATS * pDbStats; + XFLM_LFILE_STATS * pLFile; + + f_memcpy( pDestStats, pSrcStats, sizeof( XFLM_STATS)); + + // Zero out the database array. We need to do this + // so that if we get an error, we can correctly release memory for + // the destination structure. + + pDestStats->uiNumDbStats = 0; + pDestStats->uiDbStatArraySize = 0; + pDestStats->pDbStats = NULL; + uiCount = 0; + if (pSrcStats->uiNumDbStats) + { + for (uiLoop = 0, pDbStats = pSrcStats->pDbStats; + uiLoop < pSrcStats->uiNumDbStats; + uiLoop++, pDbStats++) + { + if (pDbStats->bHaveStats) + { + uiCount++; + } + } + } + if (uiCount) + { + if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( XFLM_DB_STATS) * uiCount, + &pDestStats->pDbStats))) + { + goto Exit; + } + for (uiLoop = 0, uiCount = 0, pDbStats = pSrcStats->pDbStats; + uiLoop < pSrcStats->uiNumDbStats; + uiLoop++, pDbStats++) + { + if (pDbStats->bHaveStats) + { + pDestDb = &pDestStats->pDbStats [uiCount]; + f_memcpy( pDestDb, pDbStats, sizeof( XFLM_DB_STATS)); + + // Allocate space for the database name + + if (RC_BAD( rc = f_alloc( f_strlen( pDbStats->pszDbName) + 1, + &pDestDb->pszDbName))) + { + goto Exit; + } + f_strcpy( pDestDb->pszDbName, pDbStats->pszDbName); + + // Zero out each store's LFILE statistics. We need to do this + // so that if we get an error, we can correctly release memory for + // the destination structure. + + pDestDb->uiNumLFileStats = 0; + pDestDb->uiLFileStatArraySize = 0; + pDestDb->pLFileStats = NULL; + uiCount++; + } + } + pDestStats->uiDbStatArraySize = + pDestStats->uiNumDbStats = uiCount; + } + + for (uiDb = pSrcStats->uiNumDbStats, + pDestDb = pDestStats->pDbStats, + pSrcDb = pSrcStats->pDbStats; + uiDb; + uiDb--, pSrcDb++) + { + if (!pSrcDb->bHaveStats) + { + continue; + } + + pDestDb->uiNumLFileStats = 0; + pDestDb->uiLFileStatArraySize = 0; + pDestDb->pLFileStats = NULL; + uiCount = 0; + for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; + uiLoop < pSrcDb->uiNumLFileStats; + uiLoop++, pLFile++) + { + if (pLFile->bHaveStats) + { + uiCount++; + } + } + if (uiCount) + { + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( XFLM_LFILE_STATS) * uiCount, + &pDestDb->pLFileStats))) + { + goto Exit; + } + uiCount = 0; + for (uiLoop = 0, pLFile = pSrcDb->pLFileStats; + uiLoop < pSrcDb->uiNumLFileStats; + uiLoop++, pLFile++) + { + if (pLFile->bHaveStats) + { + f_memcpy( &pDestDb->pLFileStats [uiCount], + pLFile, sizeof( XFLM_LFILE_STATS)); + uiCount++; + } + } + pDestDb->uiLFileStatArraySize = + pDestDb->uiNumLFileStats = uiCount; + } + pDestDb++; + } + +Exit: + + if (RC_BAD( rc)) + { + flmStatFree( pDestStats); + } + return( rc); +} + +/**************************************************************************** +Desc: This routine locates the appropriate XFLM_BLOCKIO_STATS structure for + a given block of data. NULL is returned if an appropriate one cannot + be found. +VISIT: uiBlkType passed in is a guess. Remove this parm and start using + pBlk. +****************************************************************************/ +XFLM_BLOCKIO_STATS * flmGetBlockIOStatPtr( + XFLM_DB_STATS * pDbStats, + XFLM_LFILE_STATS * pLFileStats, + FLMBYTE * pucBlk) +{ + F_BLK_HDR * pBlkHdr = (F_BLK_HDR *)pucBlk; + + if (pBlkHdr->ui8BlkType == BT_FREE) + { + pDbStats->bHaveStats = TRUE; + return( &pDbStats->AvailBlockStats); + } + else if (pBlkHdr->ui8BlkType == BT_LFH_BLK) + { + pDbStats->bHaveStats = TRUE; + return( &pDbStats->LFHBlockStats); + } + else if (pLFileStats) + { + pLFileStats->bHaveStats = + pDbStats->bHaveStats = TRUE; + + // VISIT: Consider a one level tree. + // Is it more important to count root stats over leaf stats? + // What about the Data Only Blocks? + + // Consider invalid type. + + if (pBlkHdr->ui8BlkType != BT_LEAF && + pBlkHdr->ui8BlkType != BT_NON_LEAF && + pBlkHdr->ui8BlkType != BT_NON_LEAF_COUNTS && + pBlkHdr->ui8BlkType != BT_LEAF_DATA) + { + return( &pLFileStats->LeafBlockStats); + } + if (pBlkHdr->ui32NextBlkInChain == 0 && + pBlkHdr->ui32PrevBlkInChain == 0) + { + return( &pLFileStats->RootBlockStats); + } + else if ((pBlkHdr->ui8BlkType != BT_LEAF) && + (pBlkHdr->ui8BlkType != BT_LEAF_DATA)) + { + return( &pLFileStats->MiddleBlockStats); + } + else + { + return( &pLFileStats->LeafBlockStats); + } + } + else + { + return( (XFLM_BLOCKIO_STATS *)NULL); + } +} + +/******************************************************************** +Desc: Determine if a given year is a leap year. +*********************************************************************/ +FINLINE FLMUINT flmLeapYear( + FLMUINT uiYear + ) +{ + if (uiYear % 4 != 0) + { + return( 0); + } + if (uiYear % 100 != 0) + { + return( 1); + } + if (uiYear % 400 != 0) + { + return( 0); + } + return( 1); +} + +/******************************************************************** +Desc: Calculate days in a given month of a given year. +*********************************************************************/ +FSTATIC FLMUINT flmDaysInMonth( + FLMUINT uiYear, + FLMUINT uiMonth + ) +{ + switch (uiMonth + 1) + { + case 4: + case 6: + case 9: + case 11: + return( 30); + case 2: + return( 28 + flmLeapYear( uiYear)); + default: + return( 31); + } +} + +/******************************************************************** +Desc: Adjust the time. +*********************************************************************/ +FSTATIC void flmAdjustTime( + F_TMSTAMP * pTime, + FLMINT iStartPoint + ) +{ + switch (iStartPoint) + { + case 1: + goto Adj_1; + case 2: + goto Adj_2; + case 3: + goto Adj_3; + case 4: + goto Adj_4; + case 5: + goto Adj_5; + case 6: + goto Adj_6; + } +Adj_1: + if (pTime->hundredth >= 100) + { + pTime->second++; + pTime->hundredth = 0; + } +Adj_2: + if (pTime->second == 60) + { + pTime->minute++; + pTime->second = 0; + } +Adj_3: + if (pTime->minute == 60) + { + pTime->hour++; + pTime->minute = 0; + } +Adj_4: + if (pTime->hour == 24) + { + pTime->day++; + pTime->hour = 0; + } +Adj_5: + if ((FLMUINT)pTime->day > flmDaysInMonth( pTime->year, pTime->month)) + { + pTime->month++; + pTime->day = 1; + } +Adj_6: + if (pTime->month > 11) + { + pTime->year++; + pTime->month = 1; + } +} + +/******************************************************************** +Desc: Calculate the elapsed time, including milliseconds. +*********************************************************************/ +void flmAddElapTime( + F_TMSTAMP * pStartTime, + FLMUINT64 * pui64ElapMilli + ) +{ + F_TMSTAMP StartTime; + F_TMSTAMP EndTime; + FLMUINT uiSec = 0; + FLMUINT uiHundredth = 0; + + f_timeGetTimeStamp( &EndTime); + f_memcpy( &StartTime, pStartTime, sizeof( F_TMSTAMP)); + + if (StartTime.year < EndTime.year) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + if (StartTime.second) + { + uiSec += (FLMUINT)(60 - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + if (StartTime.minute) + { + uiSec += (FLMUINT)((60 - StartTime.minute) * 60); + StartTime.minute = 0; + StartTime.hour++; + flmAdjustTime( &StartTime, 4); + } + if (StartTime.hour) + { + uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); + StartTime.hour = 0; + StartTime.day++; + flmAdjustTime( &StartTime, 5); + } + if (StartTime.day > 1) + { + uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - + StartTime.day + 1) * (FLMUINT)86400; + StartTime.day = 1; + StartTime.month++; + flmAdjustTime( &StartTime, 6); + } + if (StartTime.month > 1) + { + while (StartTime.month <= 11) + { + uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, + StartTime.month) * (FLMUINT)86400); + StartTime.month++; + } + StartTime.year++; + } + while (StartTime.year < EndTime.year) + { + uiSec += (FLMUINT)((FLMUINT)(365 + flmLeapYear( StartTime.year)) * + (FLMUINT)86400); + StartTime.year++; + } + } + + if (StartTime.month < EndTime.month) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + if (StartTime.second) + { + uiSec += (FLMUINT)(60 - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + if (StartTime.minute) + { + uiSec += (FLMUINT)((60 - StartTime.minute) * 60); + StartTime.minute = 0; + StartTime.hour++; + flmAdjustTime( &StartTime, 4); + } + if (StartTime.hour) + { + uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); + StartTime.hour = 0; + StartTime.day++; + flmAdjustTime( &StartTime, 5); + } + if (StartTime.day > 1) + { + uiSec += (FLMUINT)(flmDaysInMonth( StartTime.year, StartTime.month) - + StartTime.day + 1) * (FLMUINT)86400; + StartTime.day = 1; + StartTime.month++; + flmAdjustTime( &StartTime, 6); + } + while (StartTime.month < EndTime.month) + { + uiSec += (FLMUINT)((FLMUINT)flmDaysInMonth( StartTime.year, + StartTime.month) * (FLMUINT)86400); + StartTime.month++; + } + } + + if (StartTime.day < EndTime.day) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + if (StartTime.second) + { + uiSec += (FLMUINT)(60 - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + if (StartTime.minute) + { + uiSec += (FLMUINT)((60 - StartTime.minute) * 60); + StartTime.minute = 0; + StartTime.hour++; + flmAdjustTime( &StartTime, 4); + } + if (StartTime.hour) + { + uiSec += (FLMUINT)((24 - StartTime.hour) * 3600); + StartTime.hour = 0; + StartTime.day++; + flmAdjustTime( &StartTime, 5); + } + uiSec += (FLMUINT)(EndTime.day - StartTime.day) * (FLMUINT)86400; + StartTime.day = 1; + StartTime.month++; + flmAdjustTime( &StartTime, 6); + } + + if (StartTime.hour < EndTime.hour) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + if (StartTime.second) + { + uiSec += (FLMUINT)(60 - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + if (StartTime.minute) + { + uiSec += (FLMUINT)((60 - StartTime.minute) * 60); + StartTime.minute = 0; + StartTime.hour++; + flmAdjustTime( &StartTime, 4); + } + uiSec += (FLMUINT)((EndTime.hour - StartTime.hour) * 3600); + StartTime.hour = 0; + StartTime.day++; + flmAdjustTime( &StartTime, 5); + } + + if (StartTime.minute < EndTime.minute) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + if (StartTime.second) + { + uiSec += (FLMUINT)(60 - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + uiSec += (FLMUINT)((EndTime.minute - StartTime.minute) * 60); + StartTime.minute = 0; + StartTime.hour++; + flmAdjustTime( &StartTime, 4); + } + + if (StartTime.second < EndTime.second) + { + if (StartTime.hundredth) + { + uiHundredth += (FLMUINT)(100 - StartTime.hundredth); + StartTime.hundredth = 0; + StartTime.second++; + flmAdjustTime( &StartTime, 2); + } + uiSec += (FLMUINT)(EndTime.second - StartTime.second); + StartTime.second = 0; + StartTime.minute++; + flmAdjustTime( &StartTime, 3); + } + + if (StartTime.hundredth < EndTime.hundredth) + { + uiHundredth += (FLMUINT)(EndTime.hundredth - StartTime.hundredth); + } + if (uiSec) + { + (*pui64ElapMilli) += (FLMUINT64)((uiHundredth * 10 + uiSec * 1000)); + } + else + { + (*pui64ElapMilli) += (FLMUINT64)(uiHundredth * 10); + } +} diff --git a/version5/src/flmstat.h b/version5/src/flmstat.h new file mode 100644 index 0000000..ad3cbb0 --- /dev/null +++ b/version5/src/flmstat.h @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the structure definitions and prototypes +// needed to capture statistics. +// +// Tabs: 3 +// +// Copyright (c) 1997-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: flmstat.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FLMSTAT_H +#define FLMSTAT_H + +#define INIT_DB_STAT_ARRAY_SIZE 5 +#define DB_STAT_ARRAY_INCR_SIZE 5 +#define INIT_LFILE_STAT_ARRAY_SIZE 5 +#define LFILE_STAT_ARRAY_INCR_SIZE 5 + +/************************************************************************** + Various function prototypes. +**************************************************************************/ + +RCODE flmStatGetDb( // Source: flmstat.cpp + XFLM_STATS * pFlmStats, + F_Database * pDatabase, + FLMUINT uiLowStart, + XFLM_DB_STATS ** ppDbStatsRV, + FLMUINT * puiDbAllocSeqRV, + FLMUINT * puiDbTblPosRV); + +RCODE flmStatGetLFile( // Source: flmstat.cpp + XFLM_DB_STATS * pDbStats, + FLMUINT uiLFileNum, + eLFileType eLfType, + FLMUINT uiLowStart, + XFLM_LFILE_STATS ** ppLFileStatsRV, + FLMUINT * puiLFileAllocSeqRV, + FLMUINT * puiLFileTblPosRV); + +void flmStatReset( // Source: flmstat.cpp + XFLM_STATS * pStats, + FLMBOOL bFree); + +FINLINE void flmStatStart( + XFLM_STATS * pStats) +{ + pStats->bCollectingStats = TRUE; + flmStatReset( pStats, TRUE); +} + +FINLINE void flmStatStop( + XFLM_STATS * pStats) +{ + if (pStats->bCollectingStats) + { + pStats->bCollectingStats = FALSE; + f_timeGetSeconds( &pStats->uiStopTime); + } +} + +FINLINE void flmStatFree( + XFLM_STATS * pStats) +{ + pStats->bCollectingStats = FALSE; + flmStatReset( pStats, TRUE); +} + +void flmUpdateBlockIOStats( // Source: flmstat.cpp + XFLM_BLOCKIO_STATS * pDest, + XFLM_BLOCKIO_STATS * pSrc); + +RCODE flmStatUpdate( // Source: flmstat.cpp + XFLM_STATS * pSrcStats); + +void flmFreeSavedQueries( // Source: flmstat.cpp + FLMBOOL bMutexAlreadyLocked); + +void flmSaveQuery( // Source: flmstat.cpp + F_Query * pQuery); + +RCODE flmStatCopy( // Source: flmstat.cpp + XFLM_STATS * pDestStats, + XFLM_STATS * pSrcStats); + +XFLM_BLOCKIO_STATS * flmGetBlockIOStatPtr(// Source: flmstat.cpp + XFLM_DB_STATS * pDbStats, + XFLM_LFILE_STATS * pLFileStats, + FLMBYTE * pucBlk); + +void flmAddElapTime( // Source: flmstat.cpp + F_TMSTAMP * pStartTime, + FLMUINT64 * pui64ElapMilli); + +/**************************************************************************** + Inline Functions +****************************************************************************/ + + /* + Desc: This routine updates statistics from one DISKIO_STAT structure into + another. + */ + FINLINE void flmUpdateDiskIOStats( + XFLM_DISKIO_STAT * pDest, + XFLM_DISKIO_STAT * pSrc) + { + pDest->ui64Count += pSrc->ui64Count; + pDest->ui64TotalBytes += pSrc->ui64TotalBytes; + pDest->ui64ElapMilli += pSrc->ui64ElapMilli; + } + + FINLINE void flmUpdateCountTimeStats( + XFLM_COUNT_TIME_STAT * pDest, + XFLM_COUNT_TIME_STAT * pSrc) + { + pDest->ui64Count += pSrc->ui64Count; + pDest->ui64ElapMilli += pSrc->ui64ElapMilli; + } + +#endif // ifdef FLMSTAT_H diff --git a/version5/src/flog.cpp b/version5/src/flog.cpp new file mode 100644 index 0000000..c952b6c --- /dev/null +++ b/version5/src/flog.cpp @@ -0,0 +1,502 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" + +FSTATIC void flmLogProcessFormatString( + FLMUINT uiLen, + IF_LogMessageClient * pLogMessage, ...); + +FSTATIC void flmLogParsePrintfArgs( + FLMBYTE * pszFormat, + f_va_list * args, + IF_LogMessageClient * pLogMessage); + +FSTATIC void flmLogStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void flmLogNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void flmLogErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void flmLogColorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void flmLogCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + IF_LogMessageClient * pLogMessage, + f_va_list * args); + +FSTATIC void flmLogNotHandledFormatter( + 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 flmLogProcessFormatString( + FLMUINT uiLen, + IF_LogMessageClient * pLogMessage, ...) +{ + f_va_list args; + + f_va_start( args, pLogMessage); + if( uiLen) + { + flmLogStringFormatter( 0, uiLen, uiLen, 0, pLogMessage, &args); + } + f_va_end(args); +} + +/**************************************************************************** +Desc: Parse arguments in format string, calling appropriate handlers +****************************************************************************/ +FSTATIC void flmLogParsePrintfArgs( + 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) + { + flmLogProcessFormatString( 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); + flmLogProcessFormatString( uiWidth, pLogMessage, pszTextStart); + + flmSprintfProcessFieldInfo( &pszFormat, &uiWidth, + &uiPrecision, &uiFlags, args); + + ucChar = (unsigned char)*pszFormat++; + switch( ucChar) + { + case '%': + case 'c': + flmLogCharFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'C': + flmLogColorFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'd': + case 'o': + case 'u': + case 'x': + case 'X': + flmLogNumberFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 's': + case 'S': + flmLogStringFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + case 'e': + flmLogErrorFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + default: + flmLogNotHandledFormatter( ucChar, uiWidth, + uiPrecision, uiFlags, pLogMessage, args); + break; + } + + pszTextStart = pszFormat; + } + + flmLogProcessFormatString( (FLMUINT)(pszFormat - pszTextStart - 1), + pLogMessage, pszTextStart); +} + +/**************************************************************************** +Desc: Default string formatter. +****************************************************************************/ +FSTATIC void flmLogStringFormatter( + 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; + + flmSprintfStringFormatter( ucFormatChar, uiWidth, uiPrecision, + uiFlags, &info, args); + + pLogMessage->appendString( (char *)pszDest); + + if( pAllocBuf) + { + f_free( &pAllocBuf); + } +} + +/**************************************************************************** +Desc: Default number formatter. +****************************************************************************/ +FSTATIC void flmLogNumberFormatter( + 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]; + flmSprintfNumberFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Default error formatter. +****************************************************************************/ +FSTATIC void flmLogErrorFormatter( + 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]; + flmSprintfErrorFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Color formatter +****************************************************************************/ +FSTATIC void flmLogColorFormatter( + 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 >= XFLM_NUM_COLORS || eBackground >= XFLM_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 flmLogCharFormatter( + 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]; + flmSprintfCharFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Unhandled format strings +****************************************************************************/ +FSTATIC void flmLogNotHandledFormatter( + 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]; + flmSprintfNotHandledFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, &info, args); + pLogMessage->appendString( (char *)ucOutputBuf); +} + +/**************************************************************************** +Desc: Main entry point for printf functionality. +****************************************************************************/ +void flmLogPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, ...) +{ + f_va_list args; + + f_va_start( args, szFormatStr); + flmLogParsePrintfArgs( (FLMBYTE *)szFormatStr, &args, pLogMessage); + f_va_end( args); +} + +/**************************************************************************** +Desc: Printf routine that accepts a va_list argument +****************************************************************************/ +void flmLogVPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, + f_va_list * args) +{ + flmLogParsePrintfArgs( (FLMBYTE *)szFormatStr, args, pLogMessage); +} + +/**************************************************************************** +Desc: Returns an IF_LogMessageClient object if logging is enabled for the + specified message type +****************************************************************************/ +IF_LogMessageClient * flmBeginLogMessage( + eLogMessageType eMsgType) +{ + IF_LogMessageClient * pNewMsg = NULL; + + f_mutexLock( gv_XFlmSysData.hLoggerMutex); + + if( !gv_XFlmSysData.pLogger) + { + goto Exit; + } + + if( (pNewMsg = gv_XFlmSysData.pLogger->beginMessage( eMsgType)) != NULL) + { + gv_XFlmSysData.uiPendingLogMessages++; + } + +Exit: + + f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); + return( pNewMsg); +} + +/**************************************************************************** +Desc: Logs information about an error +****************************************************************************/ +void flmLogError( + RCODE rc, + const char * pszDoing, + const char * pszFileName, + FLMINT iLineNumber) +{ + FLMBYTE * pszMsgBuf = NULL; + IF_LogMessageClient * pLogMsg = NULL; + + if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != 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( XFLM_YELLOW, XFLM_BLACK); + pLogMsg->appendString( (char *)pszMsgBuf); + } + flmEndLogMessage( &pLogMsg); + } + + if( pszMsgBuf) + { + f_free( &pszMsgBuf); + } +} + +/**************************************************************************** +Desc: Ends a logging message +****************************************************************************/ +void flmEndLogMessage( + IF_LogMessageClient ** ppLogMessage) +{ + if( *ppLogMessage) + { + f_mutexLock( gv_XFlmSysData.hLoggerMutex); + flmAssert( gv_XFlmSysData.uiPendingLogMessages); + + (*ppLogMessage)->endMessage(); + (*ppLogMessage)->Release(); + *ppLogMessage = NULL; + + gv_XFlmSysData.uiPendingLogMessages--; + f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); + } +} diff --git a/version5/src/flog.h b/version5/src/flog.h new file mode 100644 index 0000000..db9694e --- /dev/null +++ b/version5/src/flog.h @@ -0,0 +1,114 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the logging routines. They use the +// IF_Logger_Client and IF_LogMessage_Client 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: flog.h 3108 2006-01-19 13:05:19 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FLOG_H +#define FLOG_H + +// Special defines for use in the format string of flmLogPrintf + +#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" + +// Logging functions for use within FLAIM + +IF_LogMessageClient * flmBeginLogMessage( + eLogMessageType eMsgType); + +void flmLogPrintf( + IF_LogMessageClient * pLogMessage, + const char * pszFormatStr, ...); + +void flmLogVPrintf( + IF_LogMessageClient * pLogMessage, + const char * szFormatStr, + f_va_list * args); + +void flmEndLogMessage( + IF_LogMessageClient ** ppLogMessage); + +/*============================================================================ + Debug Logging Functions +============================================================================*/ + +#ifdef FLM_DBG_LOG + + void scaLogWrite( + F_Database * pDatabase, + FLMUINT uiWriteAddress, + FLMBYTE * pucBlkBuf, + FLMUINT uiBufferLen, + FLMUINT uiBlockSize, + char * pszEvent); + + void flmDbgLogWrite( + F_Database * pDatabase, + FLMUINT uiBlkAddress, + FLMUINT uiWriteAddress, + FLMUINT64 ui64TransId, + char * pszEvent); + + void flmDbgLogUpdate( + F_Database * pDatabase, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + RCODE rc, + char * pszEvent); + + void flmDbgLogMsg( + char * pszMsg); + + void flmDbgLogInit( void); + void flmDbgLogExit( void); + void flmDbgLogFlush( void); + +#endif // #ifdef FLM_DBG_LOG + +#endif // #ifndef FLOG_H diff --git a/version5/src/flopen.cpp b/version5/src/flopen.cpp new file mode 100644 index 0000000..a4037b3 --- /dev/null +++ b/version5/src/flopen.cpp @@ -0,0 +1,2063 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the F_DbSystem::openDb method. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: flopen.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define MAX_DIRTY_PERCENT 70 + +FSTATIC void flmFreeCPInfo( + CP_INFO ** ppCPInfoRV); + +FSTATIC RCODE flmCPThread( + F_Thread * pThread); + +/*************************************************************************** +Desc: Does most of the actual work of opening an existing database, but + doesn't provide COM interfaces... +****************************************************************************/ +RCODE F_DbSystem::openDb( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + IF_Db ** ppDb) +{ + RCODE rc = NE_XFLM_OK; + + *ppDb = NULL; + if (!pszDbFileName || *pszDbFileName == 0) + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + + // Open the file + + if (RC_BAD( rc = openDatabase( NULL, pszDbFileName, pszDataDir, pszRflDir, + pszPassword, uiOpenFlags, FALSE, NULL, NULL, NULL, ppDb))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Constructor for F_Db object. +****************************************************************************/ +F_Db::F_Db( + FLMBOOL bInternalOpen) +{ + m_pDatabase = NULL; + m_pDict = NULL; + m_pNextForDatabase = NULL; + m_pPrevForDatabase = NULL; + m_pvAppData = NULL; + m_uiThreadId = 0; + m_bMustClose = FALSE; + m_pSFileHdl = NULL; + m_uiFlags = bInternalOpen ? FDB_INTERNAL_OPEN : 0; + m_uiTransCount = 0; + m_eTransType = XFLM_NO_TRANS; + m_AbortRc = NE_XFLM_OK; + m_ui64CurrTransID = 0; + m_uiFirstAvailBlkAddr = 0; + m_uiLogicalEOF = 0; + m_uiUpgradeCPFileNum = 0; + m_uiUpgradeCPOffset = 0; + m_uiTransEOF = 0; + f_memset( &m_TransStartTime, 0, sizeof( m_TransStartTime)); + m_bHadUpdOper = FALSE; + m_uiBlkChangeCnt = 0; + m_pIxdFixups = NULL; + m_pNextReadTrans = NULL; + m_pPrevReadTrans = NULL; + m_uiInactiveTime = 0; + m_uiKilledTime = 0; + m_bItemStateUpdOk = FALSE; + m_pDeleteStatus = NULL; + m_pIxClient = NULL; + m_pIxStatus = NULL; + m_pCommitClient = NULL; + m_pStats = NULL; + m_pDbStats = NULL; + m_pLFileStats = NULL; + m_uiLFileAllocSeq = 0; + f_memset( &m_Stats, 0, sizeof( m_Stats)); + m_bStatsInitialized = TRUE; + m_pIxStartList = NULL; + m_pIxStopList = NULL; + m_pCachedBTree = NULL; + m_pKeyColl = NULL; + m_pOldNodeList = NULL; + m_uiDirtyNodeCount = 0; + m_hWaitSem = F_SEM_NULL; + + m_bKrefSetup = FALSE; + m_pKrefTbl = NULL; + m_uiKrefTblSize = 0; + m_uiKrefCount = 0; + m_uiTotalKrefBytes = 0; + m_pucKrefKeyBuf = NULL; + m_pKrefPool = NULL; + m_bReuseKrefPool = FALSE; + m_bKrefCompoundKey = FALSE; + m_pKrefReset = NULL; + + m_tmpKrefPool.poolInit( 8192); + m_TempPool.poolInit( MAX_KEY_SIZ * 4); +} + +/*************************************************************************** +Desc: Allocates and initializes an F_Db object for a database which + is to be opened or created. +****************************************************************************/ +RCODE F_DbSystem::allocDb( + F_Db ** ppDb, + FLMBOOL bInternalOpen) +{ + RCODE rc = NE_XFLM_OK; + F_Db * pDb = NULL; + + *ppDb = NULL; + + // Allocate the F_Db object. + + if ((pDb = f_new F_Db( bInternalOpen)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = f_semCreate( &pDb->m_hWaitSem))) + { + goto Exit; + } + + *ppDb = pDb; + pDb = NULL; + +Exit: + + if (pDb) + { + pDb->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: This routine performs all of the necessary steps to complete + a create or open of a database, including notifying other threads + waiting for the open or create to complete. +NOTE: If RC_BAD( rc), this routine will delete the F_Db object. +****************************************************************************/ +void F_Db::completeOpenOrCreate( + RCODE rc, + FLMBOOL bNewDatabase + ) +{ + if (RC_OK( rc)) + { + + // If this is a newly created F_Database, we need to notify any + // threads waiting for the database to be created or opened that + // the create or open is now complete. + + if (bNewDatabase) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + m_pDatabase->newDatabaseFinish( NE_XFLM_OK); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + } + else + { + F_Database * pDatabase = m_pDatabase; + + // Temporarily increment the open count on the F_Database structure + // so that it will NOT be freed when pDb is freed below. + + if (bNewDatabase) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + pDatabase->m_uiOpenIFDbCount++; + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + // NOTE: Cannot access this F_Db object after this! + // Must do this before potentially deleting the F_Database object + // below, so that the F_Db object will unlink itself from + // the F_Database object. + Release(); + + // If we allocated the F_Database object, notify any + // waiting threads. + + if (bNewDatabase) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + + // Decrement the use count to compensate for the increment + // that occurred above. + + pDatabase->m_uiOpenIFDbCount--; + + // If this is a newly created F_Database, we need to notify any + // threads waiting for the database to be created or opened that + // the create or open is now complete. + + pDatabase->newDatabaseFinish( rc); + pDatabase->freeDatabase(); + f_mutexUnlock ( gv_XFlmSysData.hShareMutex); + } + } +} + +/**************************************************************************** +Desc: Returns the length of the base part of a database name. If the + name ends with a '.' or ".db", this will not be included in the + returned length. +****************************************************************************/ +void F_DbSystem::getDbBasePath( + char * pszBaseDbName, + const char * pszDbName, + FLMUINT * puiBaseDbNameLen) +{ + FLMUINT uiBaseLen = f_strlen( pszDbName); + + if( uiBaseLen <= 3 || + f_stricmp( &pszDbName[ uiBaseLen - 3], ".db") != 0) + { + if( pszDbName[ uiBaseLen - 1] == '.') + { + uiBaseLen--; + } + } + else + { + uiBaseLen -= 3; + } + + f_memcpy( pszBaseDbName, pszDbName, uiBaseLen); + pszBaseDbName[ uiBaseLen] = 0; + + if( puiBaseDbNameLen) + { + *puiBaseDbNameLen = uiBaseLen; + } +} + +/**************************************************************************** +Desc: This routine will open a database, returning a pointer to an IF_Db + object that can be used to access it. +****************************************************************************/ +RCODE F_DbSystem::openDatabase( + F_Database * pDatabase, + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + FLMBOOL bInternalOpen, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus, + IF_FileHdl * pLockFileHdl, + IF_Db ** ppDb) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNewDatabase = FALSE; + FLMBOOL bMutexLocked = FALSE; + F_Db * pDb = NULL; + FLMBOOL bNeedToOpen = FALSE; + + // Allocate and initialize an F_Db object. + + if (RC_BAD( rc = allocDb( &pDb, bInternalOpen))) + { + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + // Free any unused structures that have been unused for the maximum + // amount of time. May unlock and re-lock the global mutex. + + checkNotUsedObjects(); + + // Look up the file using findDatabase to see if we already + // have the file open. + + if (!pDatabase) + { + bNeedToOpen = TRUE; + + // May unlock and re-lock the global mutex. + + if (RC_BAD( rc = findDatabase( pszDbPath, pszDataDir, &pDatabase))) + { + goto Exit; + } + } + + if (pDatabase) + { + if( RC_BAD( rc = pDatabase->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + } + + if (!pDatabase) + { + if (RC_BAD( rc = allocDatabase( pszDbPath, pszDataDir, FALSE, &pDatabase))) + { + goto Exit; + } + flmAssert( !pLockFileHdl); + bNewDatabase = TRUE; + } + else if( pLockFileHdl) + { + flmAssert( pDatabase); + flmAssert( !pDatabase->m_uiOpenIFDbCount); + flmAssert( pDatabase->m_uiFlags & DBF_BEING_OPENED); + + pDatabase->m_pLockFileHdl = pLockFileHdl; + + // Set to NULL to prevent lock file from being released below + + pLockFileHdl = NULL; + + bNewDatabase = TRUE; + bNeedToOpen = TRUE; + } + else + { + FLMBOOL bWaited; + flmAssert( !pLockFileHdl); + + if (RC_BAD( rc = pDatabase->verifyOkToUse( &bWaited))) + { + goto Exit; + } + + if (bWaited) + { + bNewDatabase = FALSE; + bNeedToOpen = FALSE; + } + } + + // Link the F_Db object to the F_Database object. + + rc = pDb->linkToDatabase( pDatabase); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + if (RC_BAD(rc)) + { + goto Exit; + } + + (void)flmStatGetDb( &pDb->m_Stats, pDatabase, + 0, &pDb->m_pDbStats, NULL, NULL); + + if (bNeedToOpen) + { + if (RC_BAD( rc = pDatabase->physOpen( + pDb, pszDbPath, pszRflDir, pszPassword, uiOpenFlags, + bNewDatabase, pRestoreObj, pRestoreStatus))) + { + goto Exit; + } + } + + // Start a checkpoint thread + + if( bNewDatabase && !(uiOpenFlags & XFLM_DONT_REDO_LOG)) + { + flmAssert( !pDatabase->m_pCPThrd); + flmAssert( !pDatabase->m_pMaintThrd); + + if( RC_BAD( rc = pDatabase->startCPThread())) + { + goto Exit; + } + + if( !(uiOpenFlags & XFLM_DONT_RESUME_THREADS)) + { + if( RC_BAD( rc = pDb->startBackgroundIndexing())) + { + goto Exit; + } + + if( RC_BAD( rc = pDatabase->startMaintThread())) + { + goto Exit; + } + } + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + if (pLockFileHdl) + { + pLockFileHdl->Release(); + } + + if (pDb) + { + // completeOpenOrCreate will delete pDb if RC_BAD( rc) + + pDb->completeOpenOrCreate( rc, bNewDatabase); + + if (RC_BAD( rc)) + { + pDb = NULL; + } + } + *ppDb = (IF_Db *)pDb; + return( rc); +} + +/**************************************************************************** +Desc: This routine checks to see if it is OK for another F_Db to use an + F_Database object. + If so, it increments the database's use counter. NOTE: This routine + assumes that the calling routine has locked the global mutex. +****************************************************************************/ +RCODE F_Database::verifyOkToUse( + FLMBOOL * pbWaited) +{ + RCODE rc = NE_XFLM_OK; + F_SEM hWaitSem = F_SEM_NULL; + + // Can't open the database if it is being closed by someone else. + + if (m_uiFlags & DBF_BEING_CLOSED) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + + // If the file is in the process of being opened by another + // thread, wait for the open to complete. + + if (m_uiFlags & DBF_BEING_OPENED) + { + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + *pbWaited = TRUE; + if (RC_BAD( rc = flmWaitNotifyReq( + gv_XFlmSysData.hShareMutex, hWaitSem, &m_pOpenNotifies, (void *)0))) + { + // If flmWaitNotifyReq returns a bad RC, assume that the other + // thread will unlock and free the F_Database object. This + // routine should only unlock the object if an error occurs at + // some other point. + + goto Exit; + } + } + else + { + *pbWaited = FALSE; + } + +Exit: + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine obtains exclusive access to a database by creating + a .lck file. FLAIM holds the .lck file open as long as the database + is open. When the database is finally closed, it deletes the .lck + file. This is only used for 3.x databases. +****************************************************************************/ +RCODE flmCreateLckFile( + const char * pszFilePath, + IF_FileHdl ** ppLockFileHdlRV) +{ + RCODE rc = NE_XFLM_OK; + char szLockPath [F_PATH_MAX_SIZE]; + char szDbBaseName [F_FILENAME_SIZE]; + char * pszFileExt; + F_FileHdl * pLockFileHdl = NULL; + char szFilePathStr[ F_PATH_MAX_SIZE]; + + + if( RC_BAD( rc = gv_pFileSystem->pathToStorageString( + pszFilePath, szFilePathStr))) + { + goto Exit; + } + + // Extract the 8.3 name and put a .lck extension on it to create + // the full path for the .lck file. + + if (RC_BAD( rc = gv_pFileSystem->pathReduce( szFilePathStr, szLockPath, szDbBaseName))) + { + goto Exit; + } + pszFileExt = &szDbBaseName [0]; + while ((*pszFileExt) && (*pszFileExt != '.')) + pszFileExt++; + f_strcpy( pszFileExt, ".lck"); + + if (RC_BAD( rc = gv_pFileSystem->pathAppend( szLockPath, szDbBaseName))) + { + goto Exit; + } + + // Attempt to create the lock file. If that succeeds, we are + // OK to use the database. If it fails, the lock file may have + // been left because of a crash if FLAIM was not shut down properly. + // 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_XFLM_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, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) + { +#ifndef FLM_UNIX + if (RC_BAD( gv_pFileSystem->Delete( szLockPath))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + else if (RC_BAD( pLockFileHdl->Create( szLockPath, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#else + + if( RC_BAD( pLockFileHdl->Open( szLockPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + } + +#ifdef FLM_UNIX + if( RC_BAD( pLockFileHdl->Lock())) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + + *ppLockFileHdlRV = (IF_FileHdl *)pLockFileHdl; + pLockFileHdl = NULL; +Exit: + if (pLockFileHdl) + { + (void)pLockFileHdl->Close(); + pLockFileHdl->Release(); + pLockFileHdl = NULL; + } + return( rc); +} + +/**************************************************************************** +Desc: This routine obtains exclusive access to a database by creating + a .lck file. FLAIM holds the .lck file open as long as the database + is open. When the database is finally closed, it deletes the .lck + file. This is only used for 3.x databases. +****************************************************************************/ +RCODE F_Database::getExclAccess( + const char * pszFilePath) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNotifyWaiters = FALSE; + FLMBOOL bMutexLocked = FALSE; + F_SEM hWaitSem = F_SEM_NULL; + + // If m_pLockFileHdl is non-NULL, it means that we currently + // have the database locked with a lock file. There is no need to make + // this test inside a mutex lock, because the lock file handle can only + // be set to NULL when the use count goes to zero, meaning that the thread + // that sets it to NULL will be the only thread accessing it. + + // However, it is possible that two or more threads will simultaneously + // test m_pLockFileHdl and discover that it is NULL. In that case, + // we allow one thread to proceed and attempt to get a lock on the database + // while the other threads wait to be notified of the results of the + // attempt to lock the database. + + if (m_pLockFileHdl) + { + goto Exit; + } + + lockMutex(); + bMutexLocked = TRUE; + + if (m_bBeingLocked) + { + // If the database is in the process of being locked by another + // thread, wait for the lock to complete. NOTE: flmWaitNotifyReq will + // re-lock the mutex before returning. + + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + rc = flmWaitNotifyReq( m_hMutex, hWaitSem, &m_pLockNotifies, (void *)0); + goto Exit; + } + + // No other thread was attempting to lock the database, so + // set this thread up to make the attempt. Other threads + // coming in at this point will be required to wait and + // be notified of the results. + + m_bBeingLocked = TRUE; + bNotifyWaiters = TRUE; + unlockMutex(); + bMutexLocked = FALSE; + if (RC_BAD( rc = flmCreateLckFile( pszFilePath, &m_pLockFileHdl))) + { + goto Exit; + } + +Exit: + + if (bNotifyWaiters) + { + FNOTIFY * pNotify; + F_SEM hSem; + + // Notify any thread waiting on the lock what its status is. + + if( !bMutexLocked) + { + lockMutex(); + bMutexLocked = TRUE; + } + + pNotify = m_pLockNotifies; + while (pNotify) + { + *(pNotify->pRc) = rc; + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } + + m_bBeingLocked = FALSE; + m_pLockNotifies = NULL; + unlockMutex(); + bMutexLocked = FALSE; + } + + if( bMutexLocked) + { + unlockMutex(); + } + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine checks to see if it is OK for another FDB to use a file. + If so, it increments the file's use counter. NOTE: This routine + assumes that the global mutex is NOT locked. +****************************************************************************/ +RCODE F_Database::physOpen( + F_Db * pDb, + const char * pszFilePath, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + FLMBOOL bNewDatabase, // Is this a new F_Database object? + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus) +{ + RCODE rc = NE_XFLM_OK; + + // See if we need to read in the database header. If the database was + // already open (bNewDatabase == FALSE), we don't need to again. + + if (bNewDatabase) + { + + // Read in the database header. + + if (RC_BAD( rc = readDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, + (FLMBYTE *)pszPassword, + (uiOpenFlags & XFLM_ALLOW_LIMITED_MODE) ? TRUE : FALSE))) + { + goto Exit; + } + + // Allocate the pRfl object. Could not do this until this point + // because we need to have the version number, block size, etc. + // setup in the database header. + + flmAssert( !m_pRfl); + + if ((m_pRfl = f_new F_Rfl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = m_pRfl->setup( this, pszRflDir))) + { + goto Exit; + } + } + + // We must have exclusive access. Create a lock file for that + // purpose, if there is not already a lock file. + + if (!m_pLockFileHdl) + { + if (RC_BAD( rc = getExclAccess( pszFilePath))) + { + goto Exit; + } + } + + // Do a recovery to ensure a consistent database + // state before going any further. The FO_DONT_REDO_LOG + // flag is used ONLY by the VIEW program. + + if (bNewDatabase && !(uiOpenFlags & XFLM_DONT_REDO_LOG)) + { + if (RC_BAD( rc = doRecover( pDb, pRestoreObj, pRestoreStatus))) + { + goto Exit; + } + } + +Exit: + + if (RC_BAD( rc)) + { + (void)pDb->m_pSFileHdl->ReleaseFiles( TRUE); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine finishes up after creating a new F_Database object. It + will notify any other threads waiting for the operation to complete + of the status of the operation. +****************************************************************************/ +void F_Database::newDatabaseFinish( + RCODE OpenRc) // Return code to send to other threads that are + // waiting for the open to complete. +{ + FNOTIFY * pNotify; + F_SEM hSem; + + // Notify anyone waiting on the operation what its status is. + + pNotify = m_pOpenNotifies; + while (pNotify) + { + *(pNotify->pRc) = OpenRc; + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } + + m_pOpenNotifies = NULL; + m_uiFlags &= (~(DBF_BEING_OPENED)); +} + +/**************************************************************************** +Desc: This routine is used to see if a file is already in use somewhere. + This is only called for files which are opened directly by the + application. +Notes: This routine assumes that the global mutex is locked, but it + may unlock and re-lock the mutex if needed. +****************************************************************************/ +RCODE F_DbSystem::findDatabase( + const char * pszDbPath, + const char * pszDataDir, + F_Database ** ppDatabase) +{ + RCODE rc = NE_XFLM_OK; + FBUCKET * pBucket; + FLMUINT uiBucket; + FLMBOOL bMutexLocked = TRUE; + F_Database * pDatabase; + char szDbPathStr1 [F_PATH_MAX_SIZE]; + char szDbPathStr2 [F_PATH_MAX_SIZE]; + F_SEM hWaitSem = F_SEM_NULL; + + *ppDatabase = NULL; + + // Normalize the path to a string before looking for it. + // NOTE: On non-UNIX, non-WIN platforms, this will basically convert + // the string to upper case. + + if (RC_BAD( rc = gv_pFileSystem->pathToStorageString( pszDbPath, szDbPathStr1))) + { + goto Exit; + } + +Retry: + + *ppDatabase = NULL; + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + } + + pBucket = gv_XFlmSysData.pDatabaseHashTbl; + uiBucket = flmStrHashBucket( szDbPathStr1, pBucket, FILE_HASH_ENTRIES); + pDatabase = (F_Database *)pBucket [uiBucket].pFirstInBucket; + while (pDatabase) + { + // Compare the strings. On non-Unix platforms we must use + // f_stricmp, because case does not matter for file names + // on those platforms. + +#ifdef FLM_UNIX + if( f_strcmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) +#else + if( f_stricmp( szDbPathStr1, pDatabase->m_pszDbPath) == 0) +#endif + { + + // Make sure data paths match. + + if (pszDataDir && *pszDataDir) + { + if (RC_BAD( rc = gv_pFileSystem->pathToStorageString( + pszDataDir, szDbPathStr2))) + { + goto Exit; + } + + if (pDatabase->m_pszDataDir) + { + // f_stricmp must be used on non-unix platforms because file + // names are case insensitive on those platforms. +#ifdef FLM_UNIX + if (f_strcmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0) +#else + if (f_stricmp( pDatabase->m_pszDataDir, szDbPathStr2) != 0) +#endif + { + rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); + goto Exit; + } + } + else if (pDatabase->m_pszDataDir) + { + rc = RC_SET( NE_XFLM_DATA_PATH_MISMATCH); + goto Exit; + } + *ppDatabase = pDatabase; + break; + } + pDatabase = pDatabase->m_pNext; + } + + if (*ppDatabase && pDatabase->m_uiFlags & DBF_BEING_CLOSED) + { + if( RC_BAD( rc = f_semCreate( &hWaitSem))) + { + goto Exit; + } + + // Put ourselves into the notify list and then re-try + // the lookup when we wake up + + if (RC_BAD( rc = flmWaitNotifyReq( gv_XFlmSysData.hShareMutex, hWaitSem, + &pDatabase->m_pCloseNotifies, (void *)0))) + { + goto Exit; + } + + f_semDestroy( &hWaitSem); + + // The mutex will be locked at this point. Re-try the lookup. + // IMPORTANT NOTE: pDatabase will have been destroyed by this + // time. DO NOT use it for anything! + + goto Retry; + } + +Exit: + + if( hWaitSem != F_SEM_NULL) + { + f_semDestroy( &hWaitSem); + } + + // Make sure the global mutex is re-locked before exiting + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + } + + + return( rc); +} + +/**************************************************************************** +Desc: Make sure a database is NOT open. If it is, return an error. +****************************************************************************/ +RCODE F_DbSystem::checkDatabaseClosed( + const char * pszDbName, + const char * pszDataDir) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase; + + f_mutexLock( gv_XFlmSysData.hShareMutex); + rc = findDatabase( pszDbName, pszDataDir, &pDatabase); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + if (RC_BAD( rc)) + { + goto Exit; + } + if (pDatabase) + { + rc = RC_SET( NE_XFLM_DATABASE_OPEN); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Constructor for F_Database object. +****************************************************************************/ +F_Database::F_Database( + FLMBOOL bTempDb) +{ + m_pNext = NULL; + m_pPrev = NULL; + m_pFirstQuery = NULL; + m_pLastQuery = NULL; + m_uiBlockSize = 0; + m_uiDefaultLanguage = 0; + m_uiMaxFileSize = 0; + m_uiOpenIFDbCount = 0; + m_bTempDb = bTempDb; + m_pFirstDb = NULL; + m_pszDbPath = NULL; + m_pszDataDir = NULL; + m_pSCacheList = NULL; + m_pFirstNode = NULL; + m_pLastNode = NULL; + m_pLastDirtyNode = NULL; + m_pPendingWriteList = NULL; + m_pLastDirtyBlk = NULL; + m_pFirstInLogList = NULL; + m_pLastInLogList = NULL; + m_uiLogListCount = 0; + m_pFirstInNewList = NULL; + m_pLastInNewList = NULL; + m_uiNewCount = 0; + m_uiDirtyCacheCount = 0; + m_uiLogCacheCount = 0; + m_ppBlocksDone = NULL; + m_uiBlocksDoneArraySize = 0; + m_uiBlocksDone = 0; + m_pTransLogList = NULL; + m_pOpenNotifies = NULL; + m_pCloseNotifies = NULL; + m_pDictList = NULL; + m_bMustClose = FALSE; + m_rcMustClose = NE_XFLM_OK; + m_uiSigBitsInBlkSize = 0; + if (!bTempDb) + { + m_uiFileExtendSize = XFLM_DEFAULT_FILE_EXTEND_SIZE; + } + else + { + m_uiFileExtendSize = 65536; + } + m_pRfl = NULL; + f_memset( &m_lastCommittedDbHdr, 0, sizeof( m_lastCommittedDbHdr)); + f_memset( &m_checkpointDbHdr, 0, sizeof( m_checkpointDbHdr)); + f_memset( &m_uncommittedDbHdr, 0, sizeof( m_uncommittedDbHdr)); + m_pFileIdList = NULL; + m_pBufferMgr = NULL; + m_pCurrLogBuffer = NULL; + m_uiCurrLogWriteOffset = 0; + m_uiCurrLogBlkAddr = 0; + m_pDbHdrWriteBuf = NULL; + m_pucUpdBuffer = NULL; + m_uiUpdBufferSize = 0; + m_uiUpdByteCount = 0; + m_uiUpdCharCount = 0; + m_uiPendingType = XFLM_NODATA_TYPE; + m_pPendingInput = NULL; + m_pPendingBTree = NULL; + m_pucBTreeTmpBlk = NULL; + m_pucBTreeTmpDefragBlk = NULL; + m_pucEntryArray = NULL; + m_pucSortedArray = NULL; + m_pucBtreeBuffer = NULL; + m_pucReplaceStruct = NULL; + m_pDatabaseLockObj = NULL; + m_pWriteLockObj = NULL; + m_pLockFileHdl = NULL; + m_pLockNotifies = NULL; + m_bBeingLocked = FALSE; + m_pFirstReadTrans = NULL; + m_pLastReadTrans = NULL; + m_pFirstKilledTrans = NULL; + m_uiFirstLogBlkAddress = 0; + m_uiFirstLogCPBlkAddress = 0; + m_uiLastCheckpointTime = 0; + m_pCPThrd = NULL; + m_pCPInfo = NULL; + m_CheckpointRc = NE_XFLM_OK; + m_uiBucket = 0; + m_uiFlags = 0; + m_bBackupActive = FALSE; + m_pMaintThrd = NULL; + m_hMaintSem = F_SEM_NULL; + m_bAllowLimitedMode = FALSE; + m_bInLimitedMode = FALSE; + m_pszDbPasswd = NULL; + m_pWrappingKey = NULL; + m_bHaveEncKey = FALSE; + m_rcLimitedCode = NE_XFLM_OK; + m_hMutex = F_MUTEX_NULL; +} + +/**************************************************************************** +Desc: This destructor frees all of the structures associated with an + F_Database object. + Whoever called this routine has already determined that it is safe + to do so. +Notes: The global mutex is assumed to be locked when entering the + routine. It may be unlocked and re-locked before the routine + exits, however. +****************************************************************************/ +F_Database::~F_Database() +{ + FNOTIFY * pCloseNotifies; + F_Dict * pDict; + F_Dict * pTmpDict; + + // At this point, the use count better be zero + + flmAssert( !m_uiOpenIFDbCount); + + // Shut down all background threads before shutting down the CP thread. + + shutdownDatabaseThreads(); + + if (m_pRfl) + { + m_pRfl->closeFile(); + } + + // Shouldn't have any pending input at this point + + flmAssert( !m_pPendingInput); + + // At this point, the use count better be zero + + flmAssert( !m_uiOpenIFDbCount); + + // Unlock the mutex + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + + // Shut down the checkpoint thread + + if( m_pCPThrd) + { + m_pCPThrd->stopThread(); + m_pCPThrd->Release(); + m_pCPThrd = NULL; + } + + // Unlink all of the F_Dict objects that are connected to the + // database. + + lockMutex(); + while (m_pDictList) + { + m_pDictList->unlinkFromDatabase(); + } + unlockMutex(); + + // Take the file out of its name hash bucket, if any. + + if (m_uiBucket != 0xFFFF) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + if (m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + else + { + gv_XFlmSysData.pDatabaseHashTbl[ m_uiBucket].pFirstInBucket = m_pNext; + } + + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + m_uiBucket = 0xFFFF; + + // After this point, we should not need to keep the global mutex locked + // because the F_Database is no longer visible to any thread to find in + // the hash table. + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + + // Shouldn't have any queries at this point. But we will be nice in case + // we do and will unlink the queries from the list + + flmAssert( !m_pFirstQuery); + while (m_pFirstQuery) + { + F_Query * pQuery = m_pFirstQuery; + + m_pFirstQuery = m_pFirstQuery->m_pNext; + pQuery->m_pPrev = NULL; + pQuery->m_pNext = NULL; + pQuery->m_pDatabase = NULL; + } + + // Free the RFL data, if any. + + if (m_pRfl) + { + m_pRfl->Release(); + m_pRfl = NULL; + } + + flmAssert( m_pOpenNotifies == NULL); + m_pOpenNotifies = NULL; + + // Save pCloseNotifies -- we will notify any waiters once the + // F_Database has been freed. + + pCloseNotifies = m_pCloseNotifies; + + // Free any dictionary usage structures associated with the database. + + pDict = m_pDictList; + while (pDict) + { + pTmpDict = pDict; + pDict = pDict->getNext(); + pTmpDict->Release(); + } + m_pDictList = NULL; + + // Free any shared cache associated with the database. + // IMPORTANT NOTE: + // Cannot have the global mutex locked when these are called because + // these routines lock the block cache mutex and the node cache mutex. + // If both the global mutex and the block or node cache mutexes are to be + // locked, the rule is that the block or node cache mutex must be locked + // before locking the global mutex. This is because neededByReadTrans + // will end up doing it in this order - when neededByReadTrans is called + // either the block or node cache mutex is already locked, and it will + // additionally lock the global mutex. Since that order is already + // required, we cannot have anyone else attempting to lock the mutexes + // in a different order. + + freeBlockCache(); + freeNodeCache(); + + // Free the database ID list. This will also remove all file handles + // associated with this database. + + if (m_pFileIdList) + { + FLMUINT uiRefCnt; + + uiRefCnt = m_pFileIdList->Release(); + flmAssert( !uiRefCnt); + m_pFileIdList = NULL; + } + + // Release the lock objects. + + if (m_pWriteLockObj) + { + m_pWriteLockObj->Release( FALSE); + m_pWriteLockObj = NULL; + } + + if (m_pDatabaseLockObj) + { + m_pDatabaseLockObj->Release( FALSE); + m_pDatabaseLockObj = NULL; + } + + // Close and delete the lock file. + + if (m_pLockFileHdl) + { + (void)m_pLockFileHdl->Close(); + m_pLockFileHdl->Release(); + m_pLockFileHdl = NULL; + } + + // Free the write buffer managers. + + if (m_pBufferMgr) + { + m_pBufferMgr->Release(); + m_pBufferMgr = NULL; + } + + // Free the log header write buffer + + if (m_pDbHdrWriteBuf) + { +#ifdef FLM_WIN + (void)VirtualFree( m_pDbHdrWriteBuf, 0, MEM_RELEASE); + m_pDbHdrWriteBuf = NULL; +#elif defined( FLM_LINUX) || defined( FLM_SOLARIS) + free( m_pDbHdrWriteBuf); + m_pDbHdrWriteBuf = NULL; +#else + f_free( &m_pDbHdrWriteBuf); +#endif + } + + // Free the update buffer + + if (m_pucUpdBuffer) + { + f_free( &m_pucUpdBuffer); + m_uiUpdBufferSize = 0; + } + + m_krefPool.poolFree(); + if (m_ppBlocksDone) + { + f_free( &m_ppBlocksDone); + m_uiBlocksDoneArraySize = 0; + } + + // Notify waiters that the F_Database is gone + + while (pCloseNotifies) + { + F_SEM hSem; + + *(pCloseNotifies->pRc) = NE_XFLM_OK; + hSem = pCloseNotifies->hSem; + pCloseNotifies = pCloseNotifies->pNext; + f_semSignal( hSem); + } + + f_free( &m_pszDbPath); + + // Encryption stuff + if (m_pszDbPasswd) + { + f_free( &m_pszDbPasswd); + } + if (m_pWrappingKey) + { + delete m_pWrappingKey; + } + + flmAssert( !m_pFirstNode && !m_pLastNode && !m_pLastDirtyNode); + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + // Global mutex is still expected to be locked at this point + + f_mutexLock( gv_XFlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: This frees an F_Database object. +Note: The global mutex is assumed to be locked when entering the + routine. It may be unlocked and re-locked during the destructor, + however. For this reason, this routine should be called instead of + directly deleting a database object - i.e., delete pDatabase. +****************************************************************************/ +void F_Database::freeDatabase( void) +{ + + // See if another thread is in the process of freeing + // this F_Database. It is possible for this to happen, since + // the monitor thread may have selected this F_Database to be + // freed because it has been unused for a period of time. + // At the same time, a foreground thread could have called + // FlmConfig to close all unused F_Databases. Since the + // destructor for the F_Database object + // may unlock and re-lock the mutex, there is a small window + // of opportunity for both threads to try to free the same + // F_Database. -- Therefore, we must do this check while the + // mutex is still locked. + + if (m_uiFlags & DBF_BEING_CLOSED) + { + return; + } + + // Set the DBF_BEING_CLOSED flag + + m_uiFlags |= DBF_BEING_CLOSED; + Release(); +} + +/**************************************************************************** +Desc: This routine sets up a new F_Database object, allocating member + variables, linking into lists, etc. + NOTE: This routine assumes that the global mutex has already + been locked. It may unlock it temporarily if there is an error, + but will always relock it before exiting. +****************************************************************************/ +RCODE F_Database::setupDatabase( + const char * pszDbPath, + const char * pszDataDir) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiAllocLen; + FLMUINT uiDbNameLen; + FLMUINT uiDirNameLen; + FFileItemId * pFileItemId1 = NULL; + FFileItemId * pFileItemId2 = NULL; + char szDbPathStr[ F_PATH_MAX_SIZE]; + char szDataDirStr[ F_PATH_MAX_SIZE]; + + if( RC_BAD( rc = gv_pFileSystem->pathToStorageString( + pszDbPath, szDbPathStr))) + { + goto Exit; + } + uiDbNameLen = f_strlen( szDbPathStr) + 1; + + if( pszDataDir && *pszDataDir) + { + if( RC_BAD( rc = gv_pFileSystem->pathToStorageString( + pszDataDir, szDataDirStr))) + { + goto Exit; + } + uiDirNameLen = f_strlen( szDataDirStr) + 1; + + } + else + { + szDataDirStr[0] = 0; + uiDirNameLen = 0; + } + + + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + + uiAllocLen = (FLMUINT)(uiDbNameLen + uiDirNameLen); + if (RC_BAD( rc = f_alloc( uiAllocLen, &m_pszDbPath))) + { + goto Exit; + } + + m_krefPool.poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE * 8); + + // Allocate a buffer for writing the DB header + // If we are a temporary database, there is no need + // for this allocation. + + if (!m_bTempDb) + { +#ifdef FLM_WIN + if ((m_pDbHdrWriteBuf = (XFLM_DB_HDR *)VirtualAlloc( NULL, + XFLM_MAX_BLOCK_SIZE, MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_MEM); + goto Exit; + } + f_memset( m_pDbHdrWriteBuf, 0, XFLM_MAX_BLOCK_SIZE); +#elif defined( FLM_LINUX) + if( posix_memalign( (void **)&m_pDbHdrWriteBuf, + sysconf( _SC_PAGESIZE), XFLM_MAX_BLOCK_SIZE) != 0) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_MEM); + goto Exit; + } + f_memset( m_pDbHdrWriteBuf, 0, XFLM_MAX_BLOCK_SIZE); +#elif defined( FLM_SOLARIS) + if( (m_pDbHdrWriteBuf = (XFLM_DB_HDR *)memalign( + sysconf( _SC_PAGESIZE), XFLM_MAX_BLOCK_SIZE)) == NULL) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_MEM); + goto Exit; + } + f_memset( m_pDbHdrWriteBuf, 0, XFLM_MAX_BLOCK_SIZE); +#else + if (RC_BAD( rc = f_calloc( XFLM_MAX_BLOCK_SIZE, &m_pDbHdrWriteBuf))) + { + goto Exit; + } +#endif + } + + // Setup the write buffer managers. + + if ((m_pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + m_pBufferMgr->setMaxBuffers( MAX_PENDING_WRITES); + m_pBufferMgr->setMaxBytes( MAX_WRITE_BUFFER_BYTES); + + // Initialize members of F_Database object. + + m_uiBucket = 0xFFFF; + m_uiFlags = DBF_BEING_OPENED; + + // Copy the database name and directory. + // NOTE: uiDbNameLen includes the null terminating byte. + // and uiDirNameLen includes the null terminating byte. + + f_memcpy( m_pszDbPath, szDbPathStr, uiDbNameLen); + if (uiDirNameLen) + { + m_pszDataDir = m_pszDbPath + uiDbNameLen; + f_memcpy( m_pszDataDir, szDataDirStr, uiDirNameLen); + } + + // Link the file into the various lists it needs to be linked into. + + if (RC_BAD( rc = linkToBucket())) + { + goto Exit; + } + + // Allocate the lock objects - must be done AFTER setting up the + // file name stuff up above. + + if ((pFileItemId1 = f_new FFileItemId( this, TRUE)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if ((pFileItemId2 = f_new FFileItemId( this, FALSE)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Allocate and initialize the file ID list + + if ((m_pFileIdList = f_new F_FileIdList) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileIdList->setup())) + { + goto Exit; + } + + // Allocate a lock object for write locking. + + if ((m_pWriteLockObj = gv_XFlmSysData.pServerLockMgr->GetLockObject( + pFileItemId1)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + m_pWriteLockObj->AddRef(); + + // Allocate a lock object for file locking. + + if ((m_pDatabaseLockObj = gv_XFlmSysData.pServerLockMgr->GetLockObject( + pFileItemId2)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + m_pDatabaseLockObj->AddRef(); + +Exit: + + if (pFileItemId1) + { + pFileItemId1->Release(); + } + + if (pFileItemId2) + { + pFileItemId2->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine allocates a new F_Database object and links it + into its hash buckets. + NOTE: This routine assumes that the global mutex has already + been locked. It may unlock it temporarily if there is an error, + but will always relock it before exiting. +****************************************************************************/ +RCODE F_DbSystem::allocDatabase( + const char * pszDbPath, + const char * pszDataDir, + FLMBOOL bTempDb, + F_Database ** ppDatabase) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = NULL; + + if ((pDatabase = f_new F_Database( bTempDb)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pDatabase->setupDatabase( pszDbPath, pszDataDir))) + { + goto Exit; + } + + *ppDatabase = pDatabase; + +Exit: + + if (RC_BAD( rc)) + { + if (pDatabase) + { + pDatabase->freeDatabase(); + } + } + return( rc); +} + +/*************************************************************************** +Desc: This routine reads the header information for an existing + flaim database and makes sure we have a valid database. +*****************************************************************************/ +RCODE F_Database::readDbHdr( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBYTE * pszPassword, + FLMBOOL bAllowLimited) +{ + RCODE rc = NE_XFLM_OK; + IF_FileHdl * pCFileHdl = NULL; + + if (RC_BAD( rc = pSFileHdl->GetFileHdl( 0, FALSE, &pCFileHdl))) + { + goto Exit; + } + + // Read and verify the database header. + + if (RC_BAD( rc = flmReadAndVerifyHdrInfo( pDbStats, pCFileHdl, + &m_lastCommittedDbHdr))) + { + goto Exit; + } + m_uiBlockSize = (FLMUINT)m_lastCommittedDbHdr.ui16BlockSize; + m_uiDefaultLanguage = (FLMUINT)m_lastCommittedDbHdr.ui8DefaultLanguage; + m_uiMaxFileSize = (FLMUINT)m_lastCommittedDbHdr.ui32MaxFileSize; + m_uiSigBitsInBlkSize = calcSigBits( m_uiBlockSize); + + // Initialize the master database key from the database header + m_bAllowLimitedMode = bAllowLimited; + + if (pszPassword && *pszPassword) + { + if (m_pszDbPasswd) + { + f_free( &m_pszDbPasswd); + } + if ( RC_BAD( rc = f_alloc( (f_strlen( pszPassword) + 1), &m_pszDbPasswd))) + { + goto Exit; + } + f_strcpy(m_pszDbPasswd, pszPassword); + } + + if ((m_pWrappingKey = f_new F_CCS()) == NULL) + { + RC_SET( rc = NE_XFLM_MEM); + goto Exit; + } + + if( RC_OK( rc = m_pWrappingKey->init( TRUE, FLM_NICI_AES))) + { + // If the key was encrypted in a password, then the pszPassword parameter better + // be the key used to encrypt it. If the key was not encrypted in a password, + // then pszPassword parameter should be NULL. + rc = m_pWrappingKey->setKeyFromStore( + m_lastCommittedDbHdr.DbKey, + pszPassword, NULL); + } + + if (RC_BAD( rc)) + { + // NE_XFLM_UNSUPPORTED_FEATURE is returned when we've been compiled + // without NICI support + if ((rc == NE_XFLM_UNSUPPORTED_FEATURE) || bAllowLimited) + { + m_bInLimitedMode = TRUE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + // Note that we might still end up in limited mode if we can't verify all the keys + // that are stored in the dictionary. + +Exit: + + // Need to close the .db file so that we can set the block size. + // This will allow direct I/O to be used when accessing the file later. + + if (pCFileHdl) + { + (void)pSFileHdl->ReleaseFile( (FLMUINT)0, TRUE); + pSFileHdl->SetBlockSize( m_uiBlockSize); + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine frees a CP_INFO structure and all associated data. +*****************************************************************************/ +FSTATIC void flmFreeCPInfo( + CP_INFO ** ppCPInfoRV) +{ + CP_INFO * pCPInfo; + + if ((pCPInfo = *ppCPInfoRV) != NULL) + { + if (pCPInfo->pSFileHdl) + { + pCPInfo->pSFileHdl->Release(); + } + + if (pCPInfo->bStatsInitialized) + { + flmStatFree( &pCPInfo->Stats); + } + + if( pCPInfo->hWaitSem != F_SEM_NULL) + { + f_semDestroy( &pCPInfo->hWaitSem); + } + + f_free( ppCPInfoRV); + } +} + +/*************************************************************************** +Desc: This routine begins a thread that will do checkpoints for the + passed in database. It gives the thread its own FLAIM session and its + own handle to the database. +*****************************************************************************/ +RCODE F_Database::startCPThread( void) +{ + RCODE rc = NE_XFLM_OK; + CP_INFO * pCPInfo = NULL; + char szThreadName[ F_PATH_MAX_SIZE]; + char szBaseName[ 32]; + + // Allocate a CP_INFO structure that will be passed into the + // thread when it is created. + + if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( CP_INFO)), &pCPInfo))) + { + goto Exit; + } + pCPInfo->pDatabase = this; + + // Create a "wait" semaphore + + if( RC_BAD( rc = f_semCreate( &pCPInfo->hWaitSem))) + { + goto Exit; + } + + // Allocate a super file handle. + + if ((pCPInfo->pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Set up the super file + + if (RC_BAD( rc = pCPInfo->pSFileHdl->Setup( m_pFileIdList, + m_pszDbPath, m_pszDataDir))) + { + goto Exit; + } + + if (m_lastCommittedDbHdr.ui32DbVersion) + { + pCPInfo->pSFileHdl->SetBlockSize( m_uiBlockSize); + } + + f_memset( &pCPInfo->Stats, 0, sizeof( XFLM_STATS)); + pCPInfo->bStatsInitialized = TRUE; + + // Generate the thread name + + if (RC_BAD( rc = gv_pFileSystem->pathReduce( m_pszDbPath, + szThreadName, szBaseName))) + { + goto Exit; + } + + f_sprintf( (char *)szThreadName, "Checkpoint (%s)", (char *)szBaseName); + + // Start the checkpoint thread. + + if (RC_BAD( rc = f_threadCreate( &m_pCPThrd, + flmCPThread, szThreadName, + FLM_CHECKPOINT_THREAD_GROUP, 0, pCPInfo, NULL, 32000))) + { + goto Exit; + } + + m_pCPInfo = pCPInfo; + pCPInfo = NULL; + +Exit: + + if( pCPInfo) + { + flmFreeCPInfo( &pCPInfo); + } + + return( rc); +} + +/**************************************************************************** +Desc: Try to perform a checkpoint on the database. Returns TRUE if we need + to terminate. +****************************************************************************/ +FLMBOOL F_Database::tryCheckpoint( + F_Thread * pThread, + CP_INFO * pCPInfo) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bTerminate = FALSE; + FLMBOOL bForceCheckpoint; + FLMINT iForceReason; + FLMUINT uiCurrTime; + XFLM_DB_STATS * pDbStats; + + // See if we should terminate the thread. + + if (pThread->getShutdownFlag()) + { + // Set terminate flag to TRUE and then see if + // we have been set up to do one final checkpoint + // to flush dirty buffers to disk. + + bTerminate = TRUE; + } + + // Determine if we need to force a checkpoint. + + bForceCheckpoint = FALSE; + iForceReason = 0; + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + if (bTerminate) + { + bForceCheckpoint = TRUE; + iForceReason = XFLM_CP_SHUTTING_DOWN_REASON; + } + else if (!m_pRfl->seeIfRflVolumeOk() || RC_BAD( m_CheckpointRc)) + { + bForceCheckpoint = TRUE; + iForceReason = XFLM_CP_RFL_VOLUME_PROBLEM; + } + else if ((FLM_ELAPSED_TIME( uiCurrTime, m_uiLastCheckpointTime) >= + gv_XFlmSysData.uiMaxCPInterval) || + (!gv_XFlmSysData.uiMaxCPInterval)) + { + bForceCheckpoint = TRUE; + iForceReason = XFLM_CP_TIME_INTERVAL_REASON; + } + + if (gv_XFlmSysData.Stats.bCollectingStats) + { + + // Statistics are being collected for the system. Therefore, + // if we are not currently collecting statistics in the + // start. If we were collecting statistics, but the + // start time was earlier than the start time in the system + // statistics structure, reset the statistics. + + if (!pCPInfo->Stats.bCollectingStats) + { + flmStatStart( &pCPInfo->Stats); + } + else if (pCPInfo->Stats.uiStartTime < + gv_XFlmSysData.Stats.uiStartTime) + { + flmStatReset( &pCPInfo->Stats, FALSE); + } + (void)flmStatGetDb( &pCPInfo->Stats, this, + 0, &pDbStats, NULL, NULL); + } + else + { + pDbStats = NULL; + } + + // Lock write object - If we are forcing a checkpoint + // wait until we get the lock. Otherwise, if we can't get + // the lock without waiting, don't do anything. + + if (bForceCheckpoint || + (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && + (m_uiDirtyCacheCount + m_uiLogCacheCount) * m_uiBlockSize > + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache)) + { + if (RC_BAD( rc = dbWriteLock( pCPInfo->hWaitSem, pDbStats))) + { + + // THIS SHOULD NEVER HAPPEN BECAUSE dbWriteLock will + // wait forever for the lock! + + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + pThread->setThreadStatusStr( "Forcing checkpoint"); + + // Must wait for any RFL writes to complete. + + (void)m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, TRUE); + } + else + { + if (RC_BAD( dbWriteLock( pCPInfo->hWaitSem, pDbStats, 0))) + { + goto Exit; + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + + // See if we actually need to do the checkpoint. If the + // current transaction ID and the last checkpoint transaction + // ID are the same, no updates have occurred that would require + // a checkpoint to take place. + + if (m_lastCommittedDbHdr.ui64RflLastCPTransID == + m_lastCommittedDbHdr.ui64CurrTransID || + !m_pRfl->seeIfRflWritesDone( pCPInfo->hWaitSem, FALSE)) + { + dbWriteUnlock( pDbStats); + goto Exit; + } + } + + // Do the checkpoint. + + (void)doCheckpoint( pCPInfo->hWaitSem, pDbStats, pCPInfo->pSFileHdl, FALSE, + bForceCheckpoint, iForceReason, 0, 0); + if (pDbStats) + { + (void)flmStatUpdate( &pCPInfo->Stats); + } + + dbWriteUnlock( pDbStats); + + // Set the thread's status + + pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); + +Exit: + + return( bTerminate); +} + +/**************************************************************************** +Desc: This routine functions as a thread. It monitors open files and + frees up files which have been closed longer than the maximum + close time. +****************************************************************************/ +FSTATIC RCODE flmCPThread( + F_Thread * pThread) +{ + CP_INFO * pCPInfo = (CP_INFO *)pThread->getParm1(); + F_Database * pDatabase = pCPInfo->pDatabase; + + pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); + for (;;) + { + f_sleep( 1000); + if (pDatabase->tryCheckpoint( pThread, pCPInfo)) + { + break; + } + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); + + flmFreeCPInfo( &pCPInfo); + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Recover a database on startup. +****************************************************************************/ +RCODE F_Database::doRecover( + F_Db * pDb, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus) +{ + RCODE rc = NE_XFLM_OK; + XFLM_DB_HDR * pLastCommittedDbHdr; + + // At this point, m_lastCommittedDbHdr contains the header + // that was read from disk, which will be the state of the + // header as of the last completed checkpoint. Therefore, + // we copy it into m_checkpointDbHdr. + + pLastCommittedDbHdr = &m_lastCommittedDbHdr; + f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( XFLM_DB_HDR)); + + // Do a physical rollback on the database to restore the last + // checkpoint. + + if (RC_BAD( rc = pDb->physRollback( + (FLMUINT)pLastCommittedDbHdr->ui32RblEOF, + (FLMUINT)pLastCommittedDbHdr->ui32RblFirstCPBlkAddr, + TRUE, + pLastCommittedDbHdr->ui64RflLastCPTransID))) + { + goto Exit; + } + pLastCommittedDbHdr->ui32RblFirstCPBlkAddr = 0; + pLastCommittedDbHdr->ui32RblEOF = (FLMUINT32)m_uiBlockSize; + if (RC_BAD( rc = writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, + pLastCommittedDbHdr, + &m_checkpointDbHdr, TRUE))) + { + goto Exit; + } + + // Set uiFirstLogCPBlkAddress to zero to indicate that no + // physical blocks have been logged for the current checkpoint. + // The above call to flmPhysRollback will have set the log header + // to the same thing. + + m_uiFirstLogCPBlkAddress = 0; + + // Set the checkpointDbHdr to be the same as the log header + + f_memcpy( &m_checkpointDbHdr, pLastCommittedDbHdr, sizeof( XFLM_DB_HDR)); + + // Open roll forward log and redo the transactions that + // occurred since the last checkpoint, if any. + + if( RC_BAD( rc = m_pRfl->recover( pDb, pRestoreObj, pRestoreStatus))) + { + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version5/src/flprintf.cpp b/version5/src/flprintf.cpp new file mode 100644 index 0000000..4825570 --- /dev/null +++ b/version5/src/flprintf.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 "flaimsys.h" + +FSTATIC void flmSprintfParseArgs( + FLMBYTE * pszFormat, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + +FSTATIC void flmProcessFormatString( + FLMUINT uiLen, + F_SPRINTF_INFO * pInfo, + ...); + +FSTATIC FLMUINT flmPrintNumber( + 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 flmSprintfProcessFieldInfo( + 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 flmProcessFormatString( + FLMUINT uiLen, + F_SPRINTF_INFO * pInfo, + ...) +{ + f_va_list args; + + f_va_start( args, pInfo); + if( uiLen) + { + flmSprintfStringFormatter( 0, uiLen, uiLen, 0, pInfo, &args); + } + f_va_end( args); +} + +/**************************************************************************** +Desc: Parse arguments in format string, calling appropriate handlers +****************************************************************************/ +FSTATIC void flmSprintfParseArgs( + 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); + flmProcessFormatString( uiWidth, pInfo, pszTextStart); + + flmSprintfProcessFieldInfo( &pszFormat, &uiWidth, + &uiPrecision, &uiFlags, args); + + ucFormatChar = (unsigned char)*pszFormat++; + switch( ucFormatChar) + { + case '%': + case 'c': + flmSprintfCharFormatter( 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'; + } + + flmSprintfNumberFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + case 's': + case 'S': + case 'U': + flmSprintfStringFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + case 'e': + case 'E': + flmSprintfErrorFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + + default: + flmSprintfNotHandledFormatter( ucFormatChar, uiWidth, + uiPrecision, uiFlags, pInfo, args); + break; + } + pszTextStart = pszFormat; + } + + flmProcessFormatString( (FLMUINT)(pszFormat - pszTextStart - 1), + pInfo, pszTextStart); +} + +/**************************************************************************** +Desc: Default string formatter. +****************************************************************************/ +void flmSprintfStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args) +{ + static FLMBYTE pszNullPointerStr[] = ""; + FLMUINT uiOutputLen; + FLMUINT uiCount; + FLMUNICODE * pUnicode; + FLMBYTE * pszStr = (FLMBYTE *)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 = flmPrintNumber( (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 flmPrintNumber( + 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 flmSprintfNumberFormatter( + 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 = flmPrintNumber( 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 flmSprintfCharFormatter( + 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 flmSprintfErrorFormatter( + 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_DbSystem::_errorString( (RCODE)uiErrorCode), + (unsigned)uiErrorCode); + } + else + { + pInfo->pszDestStr += + f_sprintf( (char *)pInfo->pszDestStr, "%04X", + (unsigned)uiErrorCode); + } +} + +/**************************************************************************** +Desc: Unknown format handler +****************************************************************************/ +void flmSprintfNotHandledFormatter( + 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 f_vsprintf( + char * pszDestStr, + const char * pszFormat, + f_va_list * args) +{ + F_SPRINTF_INFO info; + + info.pszDestStr = (FLMBYTE *)pszDestStr; + flmSprintfParseArgs( (FLMBYTE *)pszFormat, &info, args); + *info.pszDestStr = 0; + + return( (FLMINT)(info.pszDestStr - (FLMBYTE *)pszDestStr)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT 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/version5/src/flreduce.cpp b/version5/src/flreduce.cpp new file mode 100644 index 0000000..2c727d8 --- /dev/null +++ b/version5/src/flreduce.cpp @@ -0,0 +1,850 @@ +//------------------------------------------------------------------------------ +// Desc: Reduce the database size by move 'N' free blocks the the end of +// the file and truncating the file. +// +// Tabs: 3 +// +// Copyright (c) 1992-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: flreduce.cpp 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc : Reduces the size of a FLAIM database file. +Notes: The size of the database file is reduced by freeing a specified + number of blocks from the available (unused) block list. The blocks + are moved to the end of the file and the file is truncated. If the + available block list is empty, FLAIM will attemp to add blocks to + the list by freeing log extent blocks. +****************************************************************************/ +RCODE XFLMAPI F_Db::reduceSize( + FLMUINT uiCount, + FLMUINT * puiCount) +{ + RCODE rc = NE_XFLM_OK; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMUINT uiLogicalEOF; + FLMUINT uiBlkAddr; + FLMUINT uiNumBlksMoved; + FLMUINT uiBlkSize; + F_LARGEST_BLK_HDR blkHdr; + FLMINT iType; + FLMBOOL bFlagSet; + FLMBOOL bTransActive = FALSE; + FLMBOOL bLockedDb = FALSE; + FLMUINT uiRflToken = 0; + + uiNumBlksMoved = 0; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Make sure we are NOT in a database transaction. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // There must NOT be a shared lock on the file. + + if (m_uiFlags & FDB_FILE_LOCK_SHARED) + { + rc = RC_SET( NE_XFLM_SHARED_LOCK); + goto Exit; + } + + // Must acquire an exclusive file lock first, if it hasn't been + // acquired. + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Lock( this, m_hWaitSem, + TRUE, FALSE, TRUE, XFLM_NO_TIMEOUT, 0, m_pDbStats))) + { + goto Exit; + } + + bLockedDb = TRUE; + m_uiFlags |= FDB_HAS_FILE_LOCK; + } + + // Disable RFL logging - don't want anything logged during reduce + // except for the reduce packet. + + pRfl->disableLogging( &uiRflToken); + + // Keep looping to here until the count is satisfied or there + // are not any more log extent blocks to turn into avail blks. + // The loop does a begin transaction - move blocks - set logical + // EOF and commits the transaction. During the commit if there are + // not any avail blocks left then a log extent (if any) will be turned + // into more avail blocks and we can do this again with more avail + // blocks. + + // Start a database transaction + + if( RC_BAD(rc = beginTrans( XFLM_UPDATE_TRANS, + XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + bTransActive = TRUE; + + // Make sure that commit does something. + + m_bHadUpdOper = TRUE; + + uiBlkSize = m_pDatabase->m_uiBlockSize; + + // Get the logical end of file and use internally. + // Loop until there are not any more free blocks left or the + // input count is matched. Switch on each block type found + // while backing up through the file. + + uiLogicalEOF = m_uiLogicalEOF; + + while (m_uiFirstAvailBlkAddr && + (!uiCount || uiNumBlksMoved < uiCount)) + { + + // Read the last block and determine block type. + + if( FSGetFileOffset( uiLogicalEOF) == 0) + { + IF_FileHdl * pFileHdl; + FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF) - 1; + FLMUINT64 ui64FileSize; + FLMUINT64 ui64Temp; + + if (RC_BAD( rc = m_pSFileHdl->GetFileHdl( + uiFileNumber, TRUE, &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Size( &ui64FileSize))) + { + goto Exit; + } + + // Adjust to a block bounds. + + ui64Temp = (ui64FileSize / uiBlkSize) * uiBlkSize; + if( ui64Temp < ui64FileSize) + { + ui64FileSize = ui64Temp + uiBlkSize; + } + + uiLogicalEOF = FSBlkAddress( uiFileNumber, (FLMUINT)ui64FileSize); + } + + uiBlkAddr = uiLogicalEOF - uiBlkSize; + if (RC_BAD( rc = readBlkHdr( uiBlkAddr, + (F_BLK_HDR *)&blkHdr, &iType))) + { + goto Exit; + } + + switch (iType) + { + case BT_FREE: + rc = m_pDatabase->freeAvailBlk( this, uiBlkAddr); + break; + + case BT_LEAF: + case BT_NON_LEAF: + case BT_NON_LEAF_COUNTS: + case BT_LEAF_DATA: + case BT_DATA_ONLY: + rc = m_pDatabase->moveBtreeBlk( this, uiBlkAddr, + (FLMUINT)blkHdr.all.BTreeBlkHdr.ui16LogicalFile, + getBlkLfType( &blkHdr.all.BTreeBlkHdr)); + break; + + case BT_LFH_BLK: + rc = m_pDatabase->moveLFHBlk( this, uiBlkAddr); + break; + + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + break; + } + + if (RC_BAD(rc)) + { + goto Exit; + } + + uiNumBlksMoved++; + + // Adjust the logical EOF to the new value. + // This is complex when dealing with block files. + + if (FSGetFileOffset( uiLogicalEOF) == 0) + { + FLMUINT uiFileNumber = FSGetFileNumber( uiLogicalEOF); + FLMUINT64 ui64FileOffset; + IF_FileHdl * pFileHdl; + + if (uiFileNumber <= 1) + { + break; + } + + // Leave the current file at zero bytes and move to the + // previous store file. + + uiFileNumber--; + + // Compute the end of the previous block file. + + if (RC_BAD( rc = m_pSFileHdl->GetFileHdl( + uiFileNumber, TRUE, &pFileHdl))) + { + goto Exit; + } + + if (RC_BAD( rc = pFileHdl->Size( &ui64FileOffset))) + { + goto Exit; + } + + uiLogicalEOF = FSBlkAddress( uiFileNumber, (FLMUINT)ui64FileOffset); + } + uiLogicalEOF -= uiBlkSize; + } + + // Log the reduce packet to the RFL if we are not in the middle of a + // restore or recovery. Will need to re-enable logging temporarily + // and then turn it back off after logging the packet. + + pRfl->enableLogging( &uiRflToken); + + // Log the reduce. + + if( RC_BAD( rc = pRfl->logReduce( this, uiCount))) + { + goto Exit; + } + + // Turn logging back off. + + pRfl->disableLogging( &uiRflToken); + + if (RC_BAD( rc)) + { + goto Exit; + } + + // Commit the transaction. + + if (m_uiFlags & FDB_DO_TRUNCATE) + { + bFlagSet = TRUE; + } + else + { + bFlagSet = FALSE; + m_uiFlags |= FDB_DO_TRUNCATE; + } + + bTransActive = FALSE; + rc = commitTrans( uiLogicalEOF, TRUE); + + if (!bFlagSet) + { + m_uiFlags &= (~(FDB_DO_TRUNCATE)); + } + + if (RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc) && bTransActive) + { + (void)abortTrans(); + uiNumBlksMoved = 0; + } + + if (puiCount) + { + // May be more than the count requested. + + *puiCount = uiNumBlksMoved; + } + + if (uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if (bLockedDb) + { + (void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this); + m_uiFlags &= ~FDB_HAS_FILE_LOCK; + } + + return( rc); +} + +/**************************************************************************** +Desc: Read the block header and return the type of block it is +****************************************************************************/ +RCODE F_Db::readBlkHdr( + FLMUINT uiBlkAddress, + F_BLK_HDR * pBlkHdr, + FLMINT * piType + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + FLMUINT uiNumLooks; + IF_FileHdl * pTmpFileHdl = NULL; + F_CachedBlock * pBlkSCache; + XFLM_LFILE_STATS * pLFileStats; + F_TMSTAMP StartTime; + FLMUINT64 ui64ElapMilli; + + // First see if the block is in cache. + // Previous writes may not have been forced out to cache. + + if (RC_BAD( rc = m_pDatabase->getBlock( this, NULL, uiBlkAddress, + &uiNumLooks, &pBlkSCache))) + { + goto Exit; + } + + if (pBlkSCache) // If found in cache ... + { + f_memcpy( pBlkHdr, pBlkSCache->getBlockPtr(), SIZEOF_LARGEST_BLK_HDR); + ScaReleaseCache( pBlkSCache, FALSE); + } + else + { + if (m_pDbStats) + { + ui64ElapMilli = 0; + f_timeGetTimeStamp( &StartTime); + } + + if (RC_OK( rc = m_pSFileHdl->GetFileHdl( + FSGetFileNumber( uiBlkAddress), TRUE, &pTmpFileHdl))) + { + rc = pTmpFileHdl->Read( FSGetFileOffset( uiBlkAddress), + SIZEOF_LARGEST_BLK_HDR, pBlkHdr, &uiBytesRead); + } + + if (m_pDbStats) + { + flmAddElapTime( &StartTime, &ui64ElapMilli); + if (RC_BAD( rc)) + { + m_pDbStats->bHaveStats = TRUE; + m_pDbStats->uiReadErrors++; + } + } + + // Convert the block header if necessary. + + if (blkIsNonNativeFormat( pBlkHdr)) + { + convertBlkHdr( pBlkHdr); + } + + if (m_pDbStats && RC_OK( rc)) + { + FLMUINT uiLFileNum; + XFLM_BLOCKIO_STATS * pBlockIOStats; + + uiLFileNum = (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile; + if (!uiLFileNum) + { + pLFileStats = NULL; + } + else + { + if( RC_BAD( flmStatGetLFile( m_pDbStats, + uiLFileNum, + getBlkLfType((F_BTREE_BLK_HDR *)pBlkHdr), + 0, + &pLFileStats, NULL, NULL))) + { + pLFileStats = NULL; + } + } + if ((pBlockIOStats = flmGetBlockIOStatPtr( m_pDbStats, + pLFileStats, (FLMBYTE *)pBlkHdr)) != NULL) + { + m_pDbStats->bHaveStats = TRUE; + if (pLFileStats) + { + pLFileStats->bHaveStats = TRUE; + } + pBlockIOStats->BlockReads.ui64ElapMilli += ui64ElapMilli; + pBlockIOStats->BlockReads.ui64Count++; + pBlockIOStats->BlockReads.ui64TotalBytes += SIZEOF_LARGEST_BLK_HDR; + } + } + + if (RC_BAD( rc)) + { + if (rc != NE_XFLM_IO_END_OF_FILE && rc != NE_XFLM_MEM) + { + m_pSFileHdl->ReleaseFile( FSGetFileNumber( uiBlkAddress), + TRUE); + } + goto Exit; + } + } + + if (piType) + { + *piType = (FLMINT)pBlkHdr->ui8BlkType; + } + +Exit: + + return( rc ); +} + +/**************************************************************************** +Desc: Find where in the b-tree a matching block is located. Move to + a free block and change all pointers to the block. +Notes: Some of this code could be called in movePcodeLFHBlk but we have + to worry about if the block is a root or right most leaf block. +****************************************************************************/ +RCODE F_Database::moveBtreeBlk( + F_Db * pDb, + FLMUINT uiBlkAddr, + FLMUINT uiLfNumber, + eLFileType eLfType) +{ + RCODE rc; + IXD * pIxd; + LFILE * pLFile; + F_COLLECTION * pCollection = NULL; + F_CachedBlock * pFreeSCache; + FLMBOOL bReleaseCache = FALSE; + F_Btree * pbtree = NULL; + FLMBOOL bHaveCounts; + FLMBOOL bHaveData; + IXKeyCompare compareObject; + IF_ResultSetCompare * pCompareObject = NULL; + + // Get an F_Btree object + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + + if (eLfType == XFLM_LF_COLLECTION) + { + if (RC_BAD( rc = pDb->m_pDict->getCollection( uiLfNumber, &pCollection))) + { + goto Exit; + } + pLFile = &pCollection->lfInfo; + bHaveCounts = FALSE; + bHaveData = TRUE; + } + else + { + if (RC_BAD( rc = pDb->m_pDict->getIndex( uiLfNumber, + &pLFile, &pIxd, TRUE))) + { + goto Exit; + } + bHaveCounts = (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE; + bHaveData = (pIxd->pFirstData) ? TRUE : FALSE; + + pCompareObject = &compareObject; + compareObject.setIxInfo( pDb, pIxd); + } + + // Need to make sure that LFILE is up to date. + // Force reading it in. + + if (RC_BAD( rc = lFileRead( pDb, pLFile, pCollection))) + { + goto Exit; + } + + // If we are moving the root block, create a new dictionary so + // that the LFILE can be modified safely to point to the new + // root block. + + if (pLFile->uiRootBlk == uiBlkAddr) + { + + // Create a new dictionary + + if (!(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = pDb->dictClone())) + { + goto Exit; + } + + // Re-get the LFile + + if (eLfType == XFLM_LF_COLLECTION) + { + if (RC_BAD( rc = pDb->m_pDict->getCollection( uiLfNumber, + &pCollection))) + { + goto Exit; + } + pLFile = &pCollection->lfInfo; + } + else + { + if (RC_BAD( rc = pDb->m_pDict->getIndex( uiLfNumber, + &pLFile, &pIxd, TRUE))) + { + goto Exit; + } + pCompareObject = &compareObject; + compareObject.setIxInfo( pDb, pIxd); + } + } + } + + if (RC_BAD( rc = pbtree->btOpen( pDb, pLFile, bHaveCounts, bHaveData, + pCompareObject))) + { + goto Exit; + } + + // Get the next free block. + + if (RC_BAD( rc = blockUseNextAvail( pDb, &pFreeSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + // Move B-tree block to free block. + + if (RC_BAD( rc = pbtree->btMoveBlock( (FLMUINT32)uiBlkAddr, + (FLMUINT32)pFreeSCache->m_uiBlkAddress))) + { + goto Exit; + } + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pFreeSCache, FALSE); + } + + if (pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: Find where anb LFH input block is located. Move to + a free block and change all pointers to the block. +****************************************************************************/ +RCODE F_Database::moveLFHBlk( + F_Db * pDb, + FLMUINT uiBlkAddr) +{ + RCODE rc; + F_CachedBlock * pSCache; + FLMBOOL bReleaseCache = FALSE; + F_CachedBlock * pFreeSCache; + FLMBOOL bReleaseCache2 = FALSE; + F_BLK_HDR * pBlkHdr; + F_LF_HDR * pLfHdr; + F_BLK_HDR * pFreeBlkHdr; + FLMUINT uiLeftBlkAddr; + FLMUINT uiRightBlkAddr; + FLMUINT uiFreeBlkAddr; + FLMUINT uiSavePriorBlkImgAddr; + FLMUINT uiPos; + FLMUINT uiEndPos; + F_COLLECTION * pTmpCollection; + LFILE * pTmpLFile; + + if (RC_BAD( rc = getBlock( pDb, NULL, uiBlkAddr, NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + pBlkHdr = pSCache->m_pBlkHdr; + + // Get left and rigth block addresses. + // Get next avail block and move data over + + uiLeftBlkAddr = (FLMUINT)pBlkHdr->ui32PrevBlkInChain; + uiRightBlkAddr = (FLMUINT)pBlkHdr->ui32NextBlkInChain; + + if (RC_BAD( rc = blockUseNextAvail( pDb, &pFreeSCache))) + { + goto Exit; + } + bReleaseCache2 = TRUE; + + pFreeBlkHdr = pFreeSCache->m_pBlkHdr; + uiFreeBlkAddr = (FLMUINT)pFreeBlkHdr->ui32BlkAddr; + + // The free block has been logged and set to dirty in + // blockUseNextAvail(). + // BUT, need to preserve prior image block address - it should + // NOT be copied over from the block we are switching with. + + uiSavePriorBlkImgAddr = (FLMUINT)pFreeBlkHdr->ui32PriorBlkImgAddr; + + f_memcpy( pFreeBlkHdr, pBlkHdr, m_uiBlockSize); + pFreeBlkHdr->ui32BlkAddr = (FLMUINT32)uiFreeBlkAddr; + + // Restore the saved previous transaction ID and block address. + + pFreeBlkHdr->ui32PriorBlkImgAddr = (FLMUINT32)uiSavePriorBlkImgAddr; + + // Fix up any LFile entries that were pointing to the + // original LFH block + + uiPos = SIZEOF_STD_BLK_HDR; + uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pFreeBlkHdr); + + // Create a new dictionary + + if (!(pDb->m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = pDb->dictClone())) + { + goto Exit; + } + } + + // Iterate over the set of LFiles in the block and + // update their LFH block addresses + + pLfHdr = (F_LF_HDR *)((FLMBYTE *)pFreeBlkHdr + SIZEOF_STD_BLK_HDR); + while (uiPos < uiEndPos) + { + if (pLfHdr->ui32LfType != (FLMUINT32)XFLM_LF_INVALID) + { + if (pLfHdr->ui32LfType == (FLMUINT32)XFLM_LF_COLLECTION) + { + if (RC_BAD( rc = pDb->m_pDict->getCollection( + (FLMUINT)pLfHdr->ui32LfNumber, &pTmpCollection))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + pTmpLFile = &pTmpCollection->lfInfo; + } + else + { + flmAssert( pLfHdr->ui32LfType == (FLMUINT32)XFLM_LF_INDEX); + if (RC_BAD( rc = pDb->m_pDict->getIndex( + (FLMUINT)pLfHdr->ui32LfNumber, + &pTmpLFile, NULL, TRUE))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + pTmpLFile->uiBlkAddress = uiFreeBlkAddr; + } + uiPos += sizeof( F_LF_HDR); + pLfHdr++; + } + + // Done with both blocks. + + ScaReleaseCache( pFreeSCache, FALSE); + ScaReleaseCache( pSCache, FALSE); + bReleaseCache2 = bReleaseCache = FALSE; + + // Read left and right blocks and adjust their + // pointers to point to the new block. + // This doesn't matter what level of the b-tree + // you are on. + + if (uiLeftBlkAddr) + { + if (RC_BAD( rc = getBlock( pDb, NULL, uiLeftBlkAddr, + NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + + pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiFreeBlkAddr; + ScaReleaseCache( pSCache, FALSE); + bReleaseCache = FALSE; + } + + if (uiRightBlkAddr) + { + if (RC_BAD( rc = getBlock( pDb, NULL, uiRightBlkAddr, + NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + + pSCache->m_pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiFreeBlkAddr; + ScaReleaseCache( pSCache, FALSE); + bReleaseCache = FALSE; + } + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + if (bReleaseCache2) + { + ScaReleaseCache( pFreeSCache, FALSE); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + + +/**************************************************************************** +Desc: Free the input avail block. Link the block out of the free list +****************************************************************************/ +RCODE F_Database::freeAvailBlk( + F_Db * pDb, + FLMUINT uiBlkAddr) +{ + RCODE rc = NE_XFLM_OK; + F_LARGEST_BLK_HDR blkHdr; + FLMUINT uiPrevBlkAddr; + FLMUINT uiNextBlkAddr; + F_CachedBlock * pSCache; + + // Check for first avail block condition. + + if (uiBlkAddr == pDb->m_uiFirstAvailBlkAddr) + { + if (RC_OK( rc = blockUseNextAvail( pDb, &pSCache))) + { + ScaReleaseCache( pSCache, FALSE); + } + goto Exit; + } + + // Not first block, unlink from list. + + // Read the block header and get pointers + + if (RC_BAD( rc = pDb->readBlkHdr( uiBlkAddr, + (F_BLK_HDR *)&blkHdr, (FLMINT *)0))) + { + goto Exit; + } + uiPrevBlkAddr = (FLMUINT)blkHdr.all.stdBlkHdr.ui32PrevBlkInChain; + uiNextBlkAddr = (FLMUINT)blkHdr.all.stdBlkHdr.ui32NextBlkInChain; + + // Read the previous block, if any + + if (uiPrevBlkAddr) + { + if (RC_BAD( rc = getBlock( pDb, NULL, uiPrevBlkAddr, + NULL, &pSCache))) + { + goto Exit; + } + + // Log the block before modifying it. + + if (RC_OK( rc = logPhysBlk( pDb, &pSCache))) + { + pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiNextBlkAddr; + } + ScaReleaseCache( pSCache, FALSE); + if (RC_BAD( rc)) + { + goto Exit; + } + } + + // Read the next block, if any. + + if (uiNextBlkAddr) + { + if (RC_BAD( rc = getBlock( pDb, NULL, uiNextBlkAddr, + NULL, &pSCache))) + { + goto Exit; + } + + // Log the block before modifying it. + + if (RC_OK( rc = logPhysBlk( pDb, &pSCache))) + { + pSCache->m_pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiPrevBlkAddr; + } + ScaReleaseCache( pSCache, FALSE); + if (RC_BAD( rc)) + { + goto Exit; + } + } + +Exit: + + return( rc); +} diff --git a/version5/src/flsweep.cpp b/version5/src/flsweep.cpp new file mode 100644 index 0000000..90d42de --- /dev/null +++ b/version5/src/flsweep.cpp @@ -0,0 +1,1162 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the code to F_Db::sweep method +// +// Tabs: 3 +// +// Copyright (c) 1996-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: flsweep.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC ELM_ATTR_STATE_INFO * sweepFindState( + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT uiNumItems, + FLMUINT uiDictType, + FLMUINT uiDictNum, + FLMUINT * puiTblSlot); + +/**************************************************************************** +Desc: Provides the ability to scan a FLAIM database to delete or check + for usage of elements and attributes. +****************************************************************************/ +RCODE F_Db::sweep( + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + ELM_ATTR_STATE_INFO * pStateTbl = NULL; + FLMUINT uiNumItems = 0; + F_COLLECTION * pCollection; + FLMUINT uiCollection; + F_Btree * pbtree = NULL; + FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMUINT64 ui64NodeId; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + FLMUINT64 ui64SavedTransId; + F_DOMNode * pNode = NULL; + eDomNodeType eNodeType; + + // See if the database is being forced to close + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Must not be a transaction going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Start a read transaction + + if (RC_BAD( rc = beginTrans( XFLM_READ_TRANS, + XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + bStartedTrans = TRUE; + + // Determine which elements and attributes have been marked for + // purging or checking. + + if (RC_BAD( rc = sweepGatherList( &pStateTbl, &uiNumItems))) + { + goto Exit; + } + + // If there were no items to check or purge, we are done + + if (!uiNumItems) + { + goto Exit; // Will return NE_XFLM_OK + } + + // Walk through every node in the database. + + uiCollection = 0; + pCollection = NULL; + for (;;) + { + if( pThread->getShutdownFlag()) + { + goto Exit; + } + + // Get the next collection if necessary. + + if (!pCollection) + { + pCollection = m_pDict->getNextCollection( uiCollection, TRUE); + if (!pCollection) + { + break; + } + uiCollection = pCollection->lfInfo.uiLfNum; + + if (pbtree) + { + pbtree->btClose(); + } + else + { + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + } + + if (RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, + FALSE, TRUE))) + { + goto Exit; + } + + uiKeyLen = sizeof( ucKey); + if (RC_BAD( rc = flmNumber64ToStorage( 1, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + if( RC_BAD( rc = pbtree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + + // Go to the next collection, if any. + + pCollection = NULL; + continue; + } + + goto Exit; + } + } + + // Get the node ID + + if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64NodeId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + if (RC_BAD( rc = getNode( uiCollection, ui64NodeId, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + ui64SavedTransId = getTransID(); + eNodeType = pNode->getNodeType(); + + if( eNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = sweepCheckElementState( pNode, + pStateTbl, &uiNumItems, &bStartedTrans))) + { + goto Exit; + } + } + + if( !uiNumItems) + { + goto Exit; + } + + // If the transaction changed, it was due to a dictionary update. + // Need to refresh the b-tree because it is using an out-of-date + // lfile. + + if( getTransID() != ui64SavedTransId) + { + if( RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + if( rc == NE_XFLM_BAD_COLLECTION) + { + rc = NE_XFLM_OK; + + // Go to the next collection, if any. + + pCollection = NULL; + continue; + } + goto Exit; + } + + pbtree->btClose(); + + if( RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, + FALSE, TRUE))) + { + goto Exit; + } + + if( RC_BAD( rc = pbtree->btLocateEntry( ucKey, sizeof( ucKey), + &uiKeyLen, XFLM_EXCL))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + pCollection = NULL; + continue; + } + + goto Exit; + } + } + else + { + if (RC_BAD( rc = pbtree->btNextEntry( ucKey, + sizeof( ucKey), &uiKeyLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + pCollection = NULL; + continue; + } + + goto Exit; + } + } + } + + // Now go through all of the items we gathered at the beginning and + // if they are still in the state we first looked at them, remove + // them. + + if( RC_BAD( rc = sweepFinalizeStates( pStateTbl, uiNumItems, + &bStartedTrans))) + { + goto Exit; + } + +Exit: + + if( bStartedTrans) + { + (void)abortTrans(); + } + + if( pNode) + { + pNode->Release(); + } + + if( pStateTbl) + { + f_free( &pStateTbl); + } + + if( pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + return( rc); +} + +/**************************************************************************** +Desc: Gather the list of elements and attributes that are marked as + needed to be checked or purged. This routine assumes that a read + transaction is already going. +****************************************************************************/ +RCODE F_Db::sweepGatherList( + ELM_ATTR_STATE_INFO ** ppStateTbl, + FLMUINT * puiNumItems) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDictType; + FLMUINT uiDictNum; + FLMUINT uiStateTblSize = 0; + ELM_ATTR_STATE_INFO * pStateInfo; + F_DOMNode * pDictDoc = NULL; + F_AttrElmInfo defInfo; + + flmAssert( *puiNumItems == 0); + flmAssert( *ppStateTbl == NULL); + + // Gather the elements and attributes that have been marked for + // purging or checking. + + uiDictType = ELM_ELEMENT_TAG; + uiDictNum = 0; + for (;;) + { + if (uiDictType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = m_pDict->getNextElement( this, &uiDictNum, + &defInfo))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + uiDictNum = 0; + uiDictType = ELM_ATTRIBUTE_TAG; + continue; + } + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDict->getNextAttribute( this, &uiDictNum, + &defInfo))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + } + + if (defInfo.m_uiState == ATTR_ELM_STATE_CHECKING || + defInfo.m_uiState == ATTR_ELM_STATE_PURGE) + { + // Add to the state table, increase table size if needed. + + if (*puiNumItems == uiStateTblSize) + { + ELM_ATTR_STATE_INFO * pNewTbl; + FLMUINT uiNewSize; + + // Increase by 100 at a time - should be plenty, because + // applications are not going to be checking 100s of + // elements or attributes at a time. + + uiNewSize = uiStateTblSize + 100; + + if (RC_BAD( rc = f_calloc( uiNewSize * + sizeof( ELM_ATTR_STATE_INFO), + &pNewTbl))) + { + goto Exit; + } + + if (uiStateTblSize) + { + f_memcpy( pNewTbl, *ppStateTbl, + sizeof( ELM_ATTR_STATE_INFO) * uiStateTblSize); + f_free( ppStateTbl); + } + + *ppStateTbl = pNewTbl; + uiStateTblSize = uiNewSize; + } + + pStateInfo = &((*ppStateTbl)[*puiNumItems]); + pStateInfo->uiDictType = uiDictType; + pStateInfo->uiDictNum = uiDictNum; + pStateInfo->uiState = defInfo.m_uiState; + + // Read the dictionary item and get its state change count. + + if (RC_BAD( rc = getDictionaryDef( uiDictType, uiDictNum, + (IF_DOMNode **)&pDictDoc))) + { + goto Exit; + } + + if (RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, + ATTR_STATE_CHANGE_COUNT_TAG, + &pStateInfo->ui64StateChangeCount))) + { + goto Exit; + } + + (*puiNumItems)++; + } + + defInfo.resetInfo(); + } + +Exit: + + if (pDictDoc) + { + pDictDoc->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Find an element or attribute's state. +****************************************************************************/ +FSTATIC ELM_ATTR_STATE_INFO * sweepFindState( + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT uiNumItems, + FLMUINT uiDictType, + FLMUINT uiDictNum, + FLMUINT * puiTblSlot + ) +{ + ELM_ATTR_STATE_INFO * pStateInfo = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblDictType; + FLMUINT uiTblDictNum; + FLMINT iCmp; + + // Do binary search in the table + + if ((uiTblSize = uiNumItems) == 0) + { + goto Exit; + } + + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + + uiTblDictType = pStateTbl [uiMid].uiDictType; + uiTblDictNum = pStateTbl [uiMid].uiDictNum; + if (uiDictType == uiTblDictType) + { + if (uiDictNum == uiTblDictNum) + { + + // Found Match + + pStateInfo = &pStateTbl [uiMid]; + *puiTblSlot = uiMid; + goto Exit; + } + else if (uiDictNum < uiTblDictNum) + { + iCmp = -1; + } + else + { + iCmp = 1; + } + } + else if (uiDictType < uiTblDictType) + { + iCmp = -1; + } + else + { + iCmp = 1; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + + // Done, item not found + + goto Exit; + } + + if (iCmp < 0) + { + if (uiMid == 0) + { + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + goto Exit; + } + uiLow = uiMid + 1; + } + } + +Exit: + + return( pStateInfo); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Db::sweepCheckElementState( + F_DOMNode * pElementNode, + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT * puiNumItems, + FLMBOOL * pbStartedTrans) +{ + RCODE rc = NE_XFLM_OK; + ELM_ATTR_STATE_INFO * pStateInfo; + FLMUINT uiNameId; + FLMUINT uiTblSlot; + F_DOMNode * pDictDoc = NULL; + FLMUINT64 ui64StateChangeCount; + F_AttrElmInfo defInfo; + + if( RC_BAD( rc = pElementNode->getNameId( this, &uiNameId))) + { + goto Exit; + } + + if( !uiNameId) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + pStateInfo = sweepFindState( pStateTbl, *puiNumItems, + ELM_ELEMENT_TAG, uiNameId, &uiTblSlot); + + if( pStateInfo) + { + // Stop the read transaction and start an update + // transaction. + + if( RC_BAD( rc = abortTrans())) + { + goto Exit; + } + + *pbStartedTrans = FALSE; + + if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + *pbStartedTrans = TRUE; + + // Get the current state to see if it has changed. + + if( RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) + { + if( rc != NE_XFLM_BAD_ELEMENT_NUM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + + // Read the dictionary item and get its state change count. + + if( RC_BAD( rc = getDictionaryDef( ELM_ELEMENT_TAG, uiNameId, + (IF_DOMNode **)&pDictDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, + ATTR_STATE_CHANGE_COUNT_TAG, &ui64StateChangeCount))) + { + goto Exit; + } + + if( ui64StateChangeCount != pStateInfo->ui64StateChangeCount) + { + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + + // If the item's state is still 'checking' set it to + // active. + + if( pStateInfo->uiState == ATTR_ELM_STATE_CHECKING) + { + if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) + { + if( RC_BAD( rc = changeItemState( ELM_ELEMENT_TAG, uiNameId, + XFLM_ACTIVE_OPTION_STR))) + { + goto Exit; + } + + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + } + else + { + // If the state is not still purge, don't do anything more + // on this element - set state to active, so no more purges + // will take place. + + if( defInfo.m_uiState != ATTR_ELM_STATE_PURGE) + { + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + else + { + if( RC_BAD( rc = pElementNode->deleteNode( this))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + + pElementNode = NULL; + } + } + + // Commit the transaction + + *pbStartedTrans = FALSE; + if( RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + + // If the state got changed to active, remove the thing from the + // array and decrement the item count. It means we have stopped + // processing this item. + + if( pStateInfo->uiState != defInfo.m_uiState) + { + if( uiTblSlot < *puiNumItems - 1) + { + f_memmove( &pStateTbl [uiTblSlot], &pStateTbl [uiTblSlot + 1], + sizeof( ELM_ATTR_STATE_INFO) * + (*puiNumItems - 1 - uiTblSlot)); + } + + (*puiNumItems)--; + } + + // Restart the read transaction. + + if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS, + XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + + *pbStartedTrans = TRUE; + } + + // Check the element's attributes + + if( pElementNode) + { + if( RC_BAD( rc = sweepCheckAttributeStates( pElementNode, + pStateTbl, puiNumItems, pbStartedTrans))) + { + goto Exit; + } + } + +Exit: + + if( pDictDoc) + { + pDictDoc->Release(); + } + + if( RC_BAD( rc) && *pbStartedTrans) + { + abortTrans(); + *pbStartedTrans = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Db::sweepCheckAttributeStates( + F_DOMNode * pElementNode, + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT * puiNumItems, + FLMBOOL * pbStartedTrans) +{ + RCODE rc = NE_XFLM_OK; + ELM_ATTR_STATE_INFO * pStateInfo; + FLMUINT uiTblSlot; + FLMUINT uiNameId; + F_DOMNode * pDictDoc = NULL; + IF_DOMNode * pAttrNode = NULL; + IF_DOMNode * pNextAttrNode = NULL; + FLMUINT64 ui64StateChangeCount; + F_AttrElmInfo defInfo; + FLMBOOL bModifiedDatabase = FALSE; + + flmAssert( pElementNode->getNodeType() == ELEMENT_NODE); + + if( !pElementNode->hasAttributes()) + { + goto Exit; + } + + if( RC_BAD( rc = pElementNode->getFirstAttribute( this, &pAttrNode))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pAttrNode->getNameId( this, &uiNameId))) + { + goto Exit; + } + + pStateInfo = sweepFindState( pStateTbl, *puiNumItems, + ELM_ATTRIBUTE_TAG, uiNameId, &uiTblSlot); + + // No need to do anything if there is no state info. + + if( !pStateInfo) + { + if( RC_BAD( rc = pAttrNode->getNextSibling( this, &pAttrNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + continue; + } + + // Stop the read transaction and start an update + // transaction. + + if( getTransType() != XFLM_UPDATE_TRANS) + { + if( RC_BAD( rc = abortTrans())) + { + goto Exit; + } + + *pbStartedTrans = FALSE; + + if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + *pbStartedTrans = TRUE; + bModifiedDatabase = TRUE; + } + + // Get the current state to see if it has changed. + + if( RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) + { + if( rc != NE_XFLM_BAD_ATTRIBUTE_NUM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + + // Read the dictionary item and get its state change count. + + if( RC_BAD( rc = getDictionaryDef( ELM_ATTRIBUTE_TAG, + uiNameId, (IF_DOMNode **)&pDictDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, + ATTR_STATE_CHANGE_COUNT_TAG, &ui64StateChangeCount))) + { + goto Exit; + } + + if( ui64StateChangeCount != pStateInfo->ui64StateChangeCount) + { + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + + // Get the next attribute before doing anything to our + // current attribute - because we may end up deleting + // pAttrNode below. + + if( RC_BAD( rc = pAttrNode->getNextSibling( this, + &pNextAttrNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + + // If the item's state is still 'checking' set it to + // active. + + if( pStateInfo->uiState == ATTR_ELM_STATE_CHECKING) + { + if( defInfo.m_uiState == ATTR_ELM_STATE_CHECKING) + { + if( RC_BAD( rc = changeItemState( ELM_ATTRIBUTE_TAG, + uiNameId, XFLM_ACTIVE_OPTION_STR))) + { + goto Exit; + } + + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + } + else + { + // If the state is not still purge, don't do anything more + // on this attribute - set state to active, so no more purges + // will take place. + + if( defInfo.m_uiState != ATTR_ELM_STATE_PURGE) + { + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + else + { + if( RC_BAD( rc = pAttrNode->deleteNode( this))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + } + } + + // If the state got changed to active, remove the thing from the + // array and decrement the item count. It means we have stopped + // processing this item. + + if( pStateInfo->uiState != defInfo.m_uiState) + { + if( uiTblSlot < *puiNumItems - 1) + { + f_memmove( &pStateTbl [uiTblSlot], &pStateTbl [uiTblSlot + 1], + sizeof( ELM_ATTR_STATE_INFO) * + (*puiNumItems - 1 - uiTblSlot)); + } + + (*puiNumItems)--; + + if( *puiNumItems == 0) + { + break; + } + } + + pAttrNode->Release(); + pAttrNode = NULL; + + // Point pAttrNode to pNextAttrNode and steal its AddRef() + + if( (pAttrNode = pNextAttrNode) == NULL) + { + break; + } + pNextAttrNode = NULL; + } + + if( bModifiedDatabase) + { + // Commit the transaction + + *pbStartedTrans = FALSE; + if( RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + + // Restart the read transaction. + + if( RC_BAD( rc = beginTrans( XFLM_READ_TRANS, + XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + + *pbStartedTrans = TRUE; + } + +Exit: + + if( pDictDoc) + { + pDictDoc->Release(); + } + + if( pAttrNode) + { + pAttrNode->Release(); + } + + if( pNextAttrNode) + { + pNextAttrNode->Release(); + } + + if( RC_BAD( rc) && *pbStartedTrans) + { + abortTrans(); + *pbStartedTrans = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Go through items in the element/attribute table and finalize the + state for each item. +****************************************************************************/ +RCODE F_Db::sweepFinalizeStates( + ELM_ATTR_STATE_INFO * pStateTbl, + FLMUINT uiNumItems, + FLMBOOL * pbStartedTrans) +{ + RCODE rc = NE_XFLM_OK; + ELM_ATTR_STATE_INFO * pStateInfo; + F_DOMNode * pNode = NULL; + F_DOMNode * pDictDoc = NULL; + FLMUINT uiLoop; + FLMUINT64 ui64StateChangeCount; + F_AttrElmInfo defInfo; + + m_bItemStateUpdOk = TRUE; + + // Stop the read transaction and start an update transaction. + + abortTrans(); + *pbStartedTrans = FALSE; + + if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + *pbStartedTrans = TRUE; + + // Check the state of all items in the table. + + for (uiLoop = 0, pStateInfo = pStateTbl; + uiLoop < uiNumItems; + uiLoop++, pStateInfo++) + { + if (pStateInfo->uiDictType == ELM_ELEMENT_TAG) + { + if (RC_BAD( rc = m_pDict->getElement( this, + pStateInfo->uiDictNum, &defInfo))) + { + // Element has gone away. + + if( rc != NE_XFLM_BAD_ELEMENT_NUM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + } + else + { + if (RC_BAD( rc = m_pDict->getAttribute( this, + pStateInfo->uiDictNum, &defInfo))) + { + + // Attribute has gone away. + + if( rc != NE_XFLM_BAD_ATTRIBUTE_NUM) + { + goto Exit; + } + + rc = NE_XFLM_OK; + defInfo.m_uiState = ATTR_ELM_STATE_ACTIVE; + } + } + + // Read the dictionary item and get its state change count. + + if (RC_BAD( rc = getDictionaryDef( pStateInfo->uiDictType, + pStateInfo->uiDictNum, + (IF_DOMNode **)&pDictDoc))) + { + goto Exit; + } + + if (RC_BAD( rc = pDictDoc->getAttributeValueUINT64( this, + ATTR_STATE_CHANGE_COUNT_TAG, + &ui64StateChangeCount))) + { + goto Exit; + } + + // If the state is unchanged, purge the definition document + + if (defInfo.m_uiState == pStateInfo->uiState && + ui64StateChangeCount == pStateInfo->ui64StateChangeCount) + { + // First make sure the element or attribute is not + // referenced from an index definition. + + if (pStateInfo->uiDictType == ELM_ELEMENT_TAG) + { + if( RC_BAD( rc = m_pDict->checkElementReferences( + pStateInfo->uiDictNum))) + { + if( rc != NE_XFLM_CANNOT_DEL_ELEMENT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + pStateInfo->uiState = ATTR_ELM_STATE_ACTIVE; + } + } + else + { + if( RC_BAD( rc = m_pDict->checkAttributeReferences( + pStateInfo->uiDictNum))) + { + if( rc != NE_XFLM_CANNOT_DEL_ATTRIBUTE) + { + goto Exit; + } + + rc = NE_XFLM_OK; + pStateInfo->uiState = ATTR_ELM_STATE_ACTIVE; + } + } + + if( pStateInfo->uiState == ATTR_ELM_STATE_ACTIVE) + { + // Change the state to active since it is referenced + // from an index definition + + if (RC_BAD( rc = changeItemState( pStateInfo->uiDictType, + pStateInfo->uiDictNum, + XFLM_ACTIVE_OPTION_STR))) + { + goto Exit; + } + } + else + { + F_DataVector srchKey; + F_DataVector foundKey; + + // Find and purge the definition document. + + if (RC_BAD( rc = srchKey.setUINT( 0, pStateInfo->uiDictType))) + { + goto Exit; + } + + if (RC_BAD( rc = srchKey.setUINT( 1, pStateInfo->uiDictNum))) + { + goto Exit; + } + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &srchKey, XFLM_EXACT, &foundKey))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, + foundKey.getDocumentID(), &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (RC_BAD( rc = pNode->deleteNode( this))) + { + goto Exit; + } + } + } + + defInfo.resetInfo(); + } + + // Commit the transaction. + + *pbStartedTrans = FALSE; + if (RC_BAD( rc = commitTrans( 0, FALSE))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc) && *pbStartedTrans) + { + abortTrans(); + *pbStartedTrans = FALSE; + } + + m_bItemStateUpdOk = FALSE; + + if (pNode) + { + pNode->Release(); + } + + if (pDictDoc) + { + pDictDoc->Release(); + } + + return( rc); +} diff --git a/version5/src/fltrabrt.cpp b/version5/src/fltrabrt.cpp new file mode 100644 index 0000000..365062c --- /dev/null +++ b/version5/src/fltrabrt.cpp @@ -0,0 +1,454 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for aborting a transaction. +// +// 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: fltrabrt.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This routine aborts an active transaction for a particular + database. If the database is open via a server, a message is + sent to the server to abort the transaction. Otherwise, the + transaction is rolled back locally. +****************************************************************************/ +RCODE F_Db::abortTrans( + FLMBOOL bOkToLogAbort) +{ + RCODE rc = NE_XFLM_OK; + eDbTransType eSaveTransType; + XFLM_DB_HDR * pLastCommittedDbHdr; + XFLM_DB_HDR * pUncommittedDbHdr; + FLMBOOL bDumpedCache = FALSE; + FLMBOOL bKeepAbortedTrans; + FLMUINT64 ui64TransId; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + RCODE tmpRc; + + // Should never be calling on a temporary database. + + flmAssert( !m_pDatabase->m_bTempDb); + + // Get transaction type + + if (m_eTransType == XFLM_NO_TRANS) + { + goto Exit; // Will return SUCCESS. + } + + // No recovery required if it is a read transaction. + + if (m_eTransType == XFLM_READ_TRANS) + { + if (m_bKrefSetup) + { + // krefCntrlFree could be called w/o checking bKrefSetup because + // it checks the flag, but it is more optimal to check the + // flag before making the call because most of the time it will + // be false. + + krefCntrlFree(); + } + + goto Unlink_From_Trans; + } + +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID, + 0, 0, NE_XFLM_OK, "TAbrt"); +#endif + + // Disable DB header writes + + pRfl->clearDbHdrs(); + + // End any pending input operations + + m_pDatabase->endPendingInput(); + + // Clear the document list + + m_pDatabase->m_DocumentList.clearNodes(); + + // If the transaction had no update operations, restore it + // to its pre-transaction state - make it appear that no + // transaction ever happened. + + pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr; + pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; + ui64TransId = m_ui64CurrTransID; + + // Free up all keys associated with this database. This is done even + // if we didn't have any update operations because the KREF may + // have been initialized by key generation operations performed + // by cursors, etc. + + krefCntrlFree(); + + if (m_bHadUpdOper) + { + + // Dump any start and stop indexing stubs that should be aborted. + + indexingAfterAbort(); + + // Log the abort record to the rfl file, or throw away the logged + // records altogether, depending on the LOG_KEEP_ABORTED_TRANS_IN_RFL + // flag. If the RFL volume is bad, we will not attempt to keep this + // transaction in the RFL. + + if (!pRfl->seeIfRflVolumeOk()) + { + bKeepAbortedTrans = FALSE; + } + else + { + bKeepAbortedTrans = + (pUncommittedDbHdr->ui8RflKeepAbortedTrans) + ? TRUE + : FALSE; + } + } + else + { + bKeepAbortedTrans = FALSE; + } + + // Log an abort transaction record to the roll-forward log or + // throw away the entire transaction, depending on the + // bKeepAbortedTrans flag. + + // If the transaction is being "dumped" because of a failed commit, + // don't log anything to the RFL. + + if (bOkToLogAbort) + { +#ifdef FLM_DEBUG + if( pRfl->isLoggingEnabled()) + { + flmAssert( m_ui64CurrTransID == pRfl->getCurrTransID()); + } +#endif + + if (RC_BAD( rc = pRfl->logEndTransaction( + this, RFL_TRNS_ABORT_PACKET, !bKeepAbortedTrans))) + { + goto Exit1; + } + } +#ifdef FLM_DEBUG + else + { + // If bOkToLogAbort is FALSE, this always means that either a + // commit failed while trying to log an end transaction packet or a + // commit packet was logged and the transaction commit subsequently + // failed for some other reason. In either case, the RFL should be + // in a good state, with its current transaction ID reset to 0. If + // not, either bOkToLogAbort is being used incorrectly by the caller + // or there is a bug in the RFL logic. + + flmAssert( pRfl->getCurrTransID() == 0); + } +#endif + + // If there were no operations in the transaction, restore + // everything as if the transaction never happened. + + // Even empty transactions can have modified nodes to clean up + // so we need to call this no matter what. + + m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1); + + if (!m_bHadUpdOper) + { + + // Pretend we dumped cache - shouldn't be any to worry about at + // this point. + + bDumpedCache = TRUE; + goto Exit1; + } + + // Dump ALL modified cache blocks associated with the DB. + // NOTE: This needs to be done BEFORE the call to flmGetDbHdrInfo + // below, because that call will change pDb->m_ui64CurrTransID, + // and that value is used by freeModifiedNodes. + + m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID); + bDumpedCache = TRUE; + + // Reset the Db header from the last committed DB header in pFile. + + getDbHdrInfo( pLastCommittedDbHdr); + if (RC_BAD( rc = physRollback( (FLMUINT)pUncommittedDbHdr->ui32RblEOF, + m_pDatabase->m_uiFirstLogBlkAddress, FALSE, 0))) + { + goto Exit1; + } + + m_pDatabase->lockMutex(); + + // Put the new transaction ID into the log header even though + // we are not committing. We want to keep the transaction IDs + // incrementing even though we aborted. + + pLastCommittedDbHdr->ui64CurrTransID = ui64TransId; + + // Preserve where we are at in the roll-forward log. Even though + // the transaction aborted, we may have kept it in the RFL instead of + // throw it away. + + pLastCommittedDbHdr->ui32RflCurrFileNum = + pUncommittedDbHdr->ui32RflCurrFileNum; + pLastCommittedDbHdr->ui32RflLastTransOffset = + pUncommittedDbHdr->ui32RflLastTransOffset; + f_memcpy( pLastCommittedDbHdr->ucLastTransRflSerialNum, + pUncommittedDbHdr->ucLastTransRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_memcpy( pLastCommittedDbHdr->ucNextRflSerialNum, + pUncommittedDbHdr->ucNextRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + + // The following items tell us where we are at in the roll-back log. + // During a transaction we may log blocks for the checkpoint or for + // read transactions. So, even though we are aborting this transaction, + // there may be other things in the roll-back log that we don't want + // to lose. These items should not be reset until we do a checkpoint, + // which is when we know it is safe to throw away the entire roll-back log. + + pLastCommittedDbHdr->ui32RblEOF = + pUncommittedDbHdr->ui32RblEOF; + pLastCommittedDbHdr->ui32RblFirstCPBlkAddr = + pUncommittedDbHdr->ui32RblFirstCPBlkAddr; + + m_pDatabase->unlockMutex(); + + pRfl->commitDbHdrs( pLastCommittedDbHdr, + &m_pDatabase->m_checkpointDbHdr); + +Exit1: + + // Dump cache, if not done above. + + if (!bDumpedCache) + { + m_pDatabase->freeModifiedBlocks( m_ui64CurrTransID); + m_pDatabase->freeModifiedNodes( this, m_ui64CurrTransID - 1); + bDumpedCache = TRUE; + } + + // Throw away IXD_FIXUPs + + if (m_pIxdFixups) + { + IXD_FIXUP * pIxdFixup; + IXD_FIXUP * pDeleteIxdFixup; + + pIxdFixup = m_pIxdFixups; + while (pIxdFixup) + { + pDeleteIxdFixup = pIxdFixup; + pIxdFixup = pIxdFixup->pNext; + f_free( &pDeleteIxdFixup); + } + m_pIxdFixups = NULL; + } + + if (m_eTransType == XFLM_UPDATE_TRANS && + gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) + { + flmTransEventCallback( XFLM_EVENT_ABORT_TRANS, this, rc, + ui64TransId); + } + +Unlink_From_Trans: + + eSaveTransType = m_eTransType; + + if (m_uiFlags & FDB_HAS_WRITE_LOCK) + { + if (RC_BAD( tmpRc = pRfl->completeTransWrites( this, FALSE, FALSE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + } + } + + if (eSaveTransType == XFLM_UPDATE_TRANS) + { + + // Before unlocking, restore collection information. + + if (m_uiFlags & FDB_UPDATED_DICTIONARY) + { + m_pDatabase->lockMutex(); + flmAssert( m_pDict); + unlinkFromDict(); + if (m_pDatabase->m_pDictList) + { + + // Link the F_Db to the right F_Dict object so it will + // fixup the correct F_COLLECTION structures. + + linkToDict( m_pDatabase->m_pDictList); + } + m_pDatabase->unlockMutex(); + } + + if (m_pDict) + { + F_COLLECTION * pCollection; + FLMUINT uiLfNum; +#ifdef FLM_DEBUG + IXD * pIxd; + FLMUINT uiRootBlk; +#endif + + // Only need to do collections. Nothing from the LFH of + // an index is stored in memory except for the root block + // address, and whenever that is changed, we get a new + // dictionary. Since the new dictionary will be discarded + // in that case, there is nothing to restore for an index. + + uiLfNum = 0; + while ((pCollection = m_pDict->getNextCollection( + uiLfNum, TRUE)) != NULL) + { +#ifdef FLM_DEBUG + uiRootBlk = pCollection->lfInfo.uiRootBlk; +#endif + if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, + &pCollection->lfInfo, pCollection))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + } +#ifdef FLM_DEBUG + else + { + // Make sure root block did not change - should not + // have because root block changes are done by creating + // a new dictionary, and we have already discarded + // any new dictionary. Hence, root block address should + // be the same in memory as it is no disk. + + flmAssert( uiRootBlk == pCollection->lfInfo.uiRootBlk); + } +#endif + uiLfNum = pCollection->lfInfo.uiLfNum; + } + + // Do indexes in debug mode to make sure uiRootBlk is correct + +#ifdef FLM_DEBUG + uiLfNum = 0; + while ((pIxd = m_pDict->getNextIndex( uiLfNum, TRUE)) != NULL) + { + uiRootBlk = pIxd->lfInfo.uiRootBlk; + if (RC_BAD( tmpRc = m_pDatabase->lFileRead( this, + &pIxd->lfInfo, NULL))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + } + else + { + // Make sure root block did not change - should not + // have because root block changes are done by creating + // a new dictionary, and we have already discarded + // any new dictionary. Hence, root block address should + // be the same in memory as it is no disk. + + flmAssert( uiRootBlk == pIxd->lfInfo.uiRootBlk); + } + uiLfNum = pIxd->lfInfo.uiLfNum; + } +#endif + } + } + + // Unlink the database from the transaction list. + + unlinkFromTransList( FALSE); + + if (m_pDbStats) + { + FLMUINT64 ui64ElapMilli = 0; + + flmAddElapTime( &m_TransStartTime, &ui64ElapMilli); + m_pDbStats->bHaveStats = TRUE; + if (eSaveTransType == XFLM_READ_TRANS) + { + m_pDbStats->ReadTransStats.AbortedTrans.ui64Count++; + m_pDbStats->ReadTransStats.AbortedTrans.ui64ElapMilli += + ui64ElapMilli; + } + else + { + m_pDbStats->UpdateTransStats.AbortedTrans.ui64Count++; + m_pDbStats->UpdateTransStats.AbortedTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + + if (m_pStats) + { + (void)flmStatUpdate( &m_Stats); + } + +Exit: + + m_AbortRc = NE_XFLM_OK; + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Aborts an active transaction. +*END************************************************************************/ +RCODE F_Db::transAbort( void) +{ + RCODE rc = NE_XFLM_OK; + + if (m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + rc = abortTrans(); + +Exit: + + if (RC_OK( rc)) + { + rc = checkState( __FILE__, __LINE__); + } + + return( rc); +} diff --git a/version5/src/fltrbeg.cpp b/version5/src/fltrbeg.cpp new file mode 100644 index 0000000..27950b7 --- /dev/null +++ b/version5/src/fltrbeg.cpp @@ -0,0 +1,1038 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for starting a transaction. +// +// Tabs: 3 +// +// Copyright (c) 1991, 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: fltrbeg.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This routine unlinks an F_Db from a transaction's list of F_Dbs. +****************************************************************************/ +void F_Db::unlinkFromTransList( + FLMBOOL bCommitting) +{ + flmAssert( m_pIxdFixups == NULL); + if( m_eTransType != XFLM_NO_TRANS) + { + if (m_uiFlags & FDB_HAS_WRITE_LOCK) + { + + // If this is a commit operation and we have a commit callback, + // call the callback function before unlocking the DIB. + + if (bCommitting && m_pCommitClient) + { + m_pCommitClient->commit( this); + } + unlockExclusive(); + } + + m_pDatabase->lockMutex(); + if (m_pDict) + { + unlinkFromDict(); + } + + // Unlink the transaction from the F_Database if it is a read transaction. + + if (m_eTransType == XFLM_READ_TRANS) + { + if (m_pNextReadTrans) + { + m_pNextReadTrans->m_pPrevReadTrans = m_pPrevReadTrans; + } + else if (!m_uiKilledTime) + { + m_pDatabase->m_pLastReadTrans = m_pPrevReadTrans; + } + if (m_pPrevReadTrans) + { + m_pPrevReadTrans->m_pNextReadTrans = m_pNextReadTrans; + } + else if (m_uiKilledTime) + { + m_pDatabase->m_pFirstKilledTrans = m_pNextReadTrans; + } + else + { + m_pDatabase->m_pFirstReadTrans = m_pNextReadTrans; + } + + // Zero out so it will be zero for next transaction begin. + + m_uiKilledTime = 0; + } + else + { + + // Reset to NULL or zero for next update transaction. + + m_pIxStartList = m_pIxStopList = NULL; + flmAssert( !m_pIxdFixups); + } + + m_pDatabase->unlockMutex(); + m_eTransType = XFLM_NO_TRANS; + m_uiFlags &= (~(FDB_UPDATED_DICTIONARY | + FDB_DONT_KILL_TRANS | + FDB_DONT_POISON_CACHE | + FDB_SWEEP_SCHEDULED)); + flmAssert( !m_uiDirtyNodeCount); + } +} + +/**************************************************************************** +Desc: This routine reads a database's dictionary. This is called only + when we did not have a dictionary off of the F_Database object - + which will be the first transaction after a database is opened. +****************************************************************************/ +RCODE F_Db::readDictionary( void) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = dictOpen())) + { + goto Exit; + } + + m_pDatabase->lockMutex(); + + // At this point, we will not yet have opened the database for + // general use, so there is no way that any other thread can have + // created a dictionary yet. + + flmAssert( !m_pDatabase->m_pDictList); + + // Link the new local dictionary to its file structure. + + m_pDict->linkToDatabase( m_pDatabase); + m_pDatabase->unlockMutex(); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine starts a transaction for the specified database. The + transaction may be part of an overall larger transaction. +****************************************************************************/ +RCODE F_Db::beginTrans( + eDbTransType eTransType, + FLMUINT uiMaxLockWait, + FLMUINT uiFlags, + XFLM_DB_HDR * pDbHdr) +{ + RCODE rc = NE_XFLM_OK; + XFLM_DB_HDR * pLastCommittedDbHdr; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + FLMBOOL bMutexLocked = FALSE; + + // Should not be calling on a temporary database + + flmAssert( !m_pDatabase->m_bTempDb); + + // Check the state of the database engine + + if( RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Initialize a few things - as few as is necessary to avoid + // unnecessary overhead. + + m_AbortRc = NE_XFLM_OK; + pLastCommittedDbHdr = &m_pDatabase->m_lastCommittedDbHdr; + m_bKrefSetup = FALSE; + m_eTransType = eTransType; + m_uiThreadId = (FLMUINT)f_threadId(); + m_uiTransCount++; + + // Link the F_Db to the database's most current F_Dict structure, + // if there is one. Also, if it is a read transaction, link the F_Db + // into the list of read transactions off of the F_Database object. + + m_pDatabase->lockMutex(); + bMutexLocked = TRUE; + + if (m_pDatabase->m_pDictList) + { + // Link the F_Db to the right F_Dict object + + linkToDict( m_pDatabase->m_pDictList); + } + + // If it is a read transaction, link into the list of + // read transactions off of the F_Database object. Until we + // get the DB header transaction ID below, we set ui64CurrTransID + // to zero and link this transaction in at the beginning of the + // list. + + if (eTransType == XFLM_READ_TRANS) + { + getDbHdrInfo( pLastCommittedDbHdr); + + // Link in at the end of the transaction list. + + m_pNextReadTrans = NULL; + if ((m_pPrevReadTrans = m_pDatabase->m_pLastReadTrans) != NULL) + { + // Make sure transaction IDs are always in ascending order. They + // should be at this point. + + flmAssert( m_pDatabase->m_pLastReadTrans->m_ui64CurrTransID <= + m_ui64CurrTransID); + m_pDatabase->m_pLastReadTrans->m_pNextReadTrans = this; + } + else + { + m_pDatabase->m_pFirstReadTrans = this; + } + m_pDatabase->m_pLastReadTrans = this; + m_uiInactiveTime = 0; + + if (uiFlags & XFLM_DONT_KILL_TRANS) + { + m_uiFlags |= FDB_DONT_KILL_TRANS; + } + else + { + m_uiFlags &= ~FDB_DONT_KILL_TRANS; + } + + if (pDbHdr) + { + f_memcpy( pDbHdr, &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + } + } + + m_pDatabase->unlockMutex(); + bMutexLocked = FALSE; + + if (uiFlags & XFLM_DONT_POISON_CACHE) + { + m_uiFlags |= FDB_DONT_POISON_CACHE; + } + else + { + m_uiFlags &= ~FDB_DONT_POISON_CACHE; + } + + // Put an exclusive lock on the database if we are not in a read + // transaction. Read transactions require no lock. + + if (eTransType != XFLM_READ_TRANS) + { + // Set the m_bHadUpdOper to TRUE for all transactions to begin with. + // Many calls to beginTrans are internal, and we WANT the + // normal behavior at the end of the transaction when it is + // committed or aborted. The only time this flag will be set + // to FALSE is when the application starts the transaction as + // opposed to an internal starting of the transaction. + + m_bHadUpdOper = TRUE; + + // Initialize the count of blocks changed to be 0 + + m_uiBlkChangeCnt = 0; + + if (RC_BAD( rc = lockExclusive( uiMaxLockWait))) + { + goto Exit; + } + + flmAssert( !m_pDatabase->m_DocumentList.m_uiLastCollection); + flmAssert( !m_pDatabase->m_DocumentList.m_ui64LastDocument); + + // If there was a problem with the RFL volume, we must wait + // for a checkpoint to be completed before continuing. + // The checkpoint thread looks at this same flag and forces + // a checkpoint. If it completes one successfully, it will + // reset this flag. + // Also, if the last forced checkpoint had a problem + // (pFile->CheckpointRc != NE_XFLM_OK), we don't want to + // start up a new update transaction until it is resolved. + + if( !pRfl->seeIfRflVolumeOk() || RC_BAD( m_pDatabase->m_CheckpointRc)) + + { + rc = RC_SET( NE_XFLM_MUST_WAIT_CHECKPOINT); + goto Exit; + } + + // Set the first log block address to zero. + + m_pDatabase->m_uiFirstLogBlkAddress = 0; + + // Header must be read before opening roll forward log file to make + // sure we have the most current log file and log options. + + f_memcpy( &m_pDatabase->m_uncommittedDbHdr, pLastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + getDbHdrInfo( pLastCommittedDbHdr); + + // Need to increment the current checkpoint for update transactions + // so that it will be correct when we go to mark cache blocks. + + if (m_uiFlags & FDB_REPLAYING_RFL) + { + // During recovery we need to set the transaction ID to the + // transaction ID that was logged. + + m_ui64CurrTransID = pRfl->getCurrTransID(); + } + else + { + m_ui64CurrTransID++; + } + + // Link F_Db to the most current local dictionary, if there + // is one. + + m_pDatabase->lockMutex(); + if (m_pDatabase->m_pDictList != m_pDict && + m_pDatabase->m_pDictList) + { + linkToDict( m_pDatabase->m_pDictList); + } + m_pDatabase->unlockMutex(); + + // Set the transaction EOF to the current file EOF + + m_uiTransEOF = m_uiLogicalEOF; + + // Put the transaction ID into the uncommitted log header. + + m_pDatabase->m_uncommittedDbHdr.ui64CurrTransID = m_ui64CurrTransID; + + if (pDbHdr) + { + f_memcpy( pDbHdr, &m_pDatabase->m_uncommittedDbHdr, + sizeof( XFLM_DB_HDR)); + } + } + + // Set up to collect statistics. We only do this at transaction + // begin and not on any other type of operation. So this is the + // only time when an F_Db will sense that statistics have been + // turned on or off. + + if (!gv_XFlmSysData.Stats.bCollectingStats) + { + m_pStats = NULL; + m_pDbStats = NULL; + } + else + { + m_pStats = &m_Stats; + + // Statistics are being collected for the system. Therefore, + // if we are not currently collecting statistics in the + // session, start. If we were collecting statistics, but the + // start time was earlier than the start time in the system + // statistics structure, reset the statistics in the session. + + if (!m_Stats.bCollectingStats) + { + flmStatStart( &m_Stats); + } + else if (m_Stats.uiStartTime < gv_XFlmSysData.Stats.uiStartTime) + { + flmStatReset( &m_Stats, FALSE); + } + (void)flmStatGetDb( &m_Stats, m_pDatabase, + 0, &m_pDbStats, NULL, NULL); + m_pLFileStats = NULL; + } + + if (m_pDbStats) + { + f_timeGetTimeStamp( &m_TransStartTime); + } + + // If we do not have a dictionary, read it in from disk. + // NOTE: This should only happen when we are first opening + // the database. + + if (!m_pDict) + { + if (eTransType != XFLM_READ_TRANS) + { + pRfl->disableLogging( &uiRflToken); + } + + if (RC_BAD( rc = readDictionary())) + { + goto Exit; + } + } + +Exit: + + if( bMutexLocked) + { + m_pDatabase->unlockMutex(); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if (eTransType != XFLM_READ_TRANS) + { + if (RC_OK( rc)) + { + rc = pRfl->logBeginTransaction( this); + } +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( m_pDatabase, m_ui64CurrTransID, + 0, 0, rc, "TBeg"); +#endif + } + + if (eTransType == XFLM_UPDATE_TRANS && + gv_XFlmSysData.EventHdrs [XFLM_EVENT_UPDATES].pEventCBList) + { + flmTransEventCallback( XFLM_EVENT_BEGIN_TRANS, this, rc, + (FLMUINT)(RC_OK( rc) + ? m_ui64CurrTransID + : (FLMUINT64)0)); + } + + if (RC_BAD( rc)) + { + // If there was an error, unlink the database from the transaction + // structure as well as from the FDICT structure. Also dump any nodes + // that are already in the cache. + + unlinkFromTransList( FALSE); + + if (m_pStats) + { + (void)flmStatUpdate( &m_Stats); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine starts a transaction for the specified database. It uses + the transaction information in the passed in pDb. +****************************************************************************/ +RCODE F_Db::beginTrans( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + + // Should not be calling on a temporary database + + flmAssert( !m_pDatabase->m_bTempDb); + + // pDb better be running a read transaction. + + flmAssert( pDb->m_eTransType == XFLM_READ_TRANS); + + if( RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Initialize a few things - as few as is necessary to avoid + // unnecessary overhead. + + m_AbortRc = NE_XFLM_OK; + m_bKrefSetup = FALSE; + m_eTransType = XFLM_READ_TRANS; + m_uiThreadId = (FLMUINT)f_threadId(); + m_uiTransCount++; + + // Link the F_Db to the database's most current F_Dict structure, + // if there is one. Also, if it is a read transaction, link the F_Db + // into the list of read transactions off of the F_Database object. + + m_pDatabase->lockMutex(); + bMutexLocked = TRUE; + + // Link to the same dictionary as pDb. + + linkToDict( pDb->m_pDict); + + // If it is a read transaction, link into the list of + // read transactions off of the F_Database object. Until we + // get the DB header transaction ID below, we set ui64CurrTransID + // to zero and link this transaction in at the beginning of the + // list. + + getDbHdrInfo( pDb); + + // Link into the transaction list right after the point where + // pDb is linked in. We need to keep transaction IDs in ascending + // order. + + m_pPrevReadTrans = pDb; + if ((m_pNextReadTrans = pDb->m_pNextReadTrans) != NULL) + { + m_pNextReadTrans->m_pPrevReadTrans = this; + } + else + { + m_pDatabase->m_pLastReadTrans = this; + } + pDb->m_pNextReadTrans = this; + + m_uiInactiveTime = 0; + + if (pDb->m_uiFlags & FDB_DONT_KILL_TRANS) + { + m_uiFlags |= FDB_DONT_KILL_TRANS; + } + else + { + m_uiFlags &= ~FDB_DONT_KILL_TRANS; + } + if (pDb->m_uiFlags & FDB_DONT_POISON_CACHE) + { + m_uiFlags |= FDB_DONT_POISON_CACHE; + } + else + { + m_uiFlags &= ~FDB_DONT_POISON_CACHE; + } + + m_pDatabase->unlockMutex(); + bMutexLocked = FALSE; + + // Set up to collect statistics. We only do this at transaction + // begin and not on any other type of operation. So this is the + // only time when an F_Db will sense that statistics have been + // turned on or off. + + if (!gv_XFlmSysData.Stats.bCollectingStats) + { + m_pStats = NULL; + m_pDbStats = NULL; + } + else + { + m_pStats = &m_Stats; + + // Statistics are being collected for the system. Therefore, + // if we are not currently collecting statistics in the + // session, start. If we were collecting statistics, but the + // start time was earlier than the start time in the system + // statistics structure, reset the statistics in the session. + + if (!m_Stats.bCollectingStats) + { + flmStatStart( &m_Stats); + } + else if (m_Stats.uiStartTime < gv_XFlmSysData.Stats.uiStartTime) + { + flmStatReset( &m_Stats, FALSE); + } + (void)flmStatGetDb( &m_Stats, m_pDatabase, + 0, &m_pDbStats, NULL, NULL); + m_pLFileStats = NULL; + } + + if (m_pDbStats) + { + f_timeGetTimeStamp( &m_TransStartTime); + } + +Exit: + + if( bMutexLocked) + { + m_pDatabase->unlockMutex(); + } + + if (RC_BAD( rc)) + { + // If there was an error, unlink the database from the transaction + // structure as well as from the FDICT structure. Also dump any nodes + // that are already in the cache. + + unlinkFromTransList( FALSE); + + if (m_pStats) + { + (void)flmStatUpdate( &m_Stats); + } + } + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Starts a transaction. +*END************************************************************************/ +RCODE XFLMAPI F_Db::transBegin( + eDbTransType eTransType, + // [IN] Specifies the type of transaction to begin. + // Possible values are: + // + // XFLM_READ_TRANS: Begins a read transaction. + // XFLM_UPDATE_TRANS: Begins an update transaction. + FLMUINT uiMaxLockWait, + // [IN] Maximum lock wait time. Specifies the amount of time + // to wait for lock requests occuring during the transaction + // to be granted. Valid values are 0 through 255 seconds. Zero + // is used to specify no-wait locks. + FLMUINT uiFlags, + // Transaction flags. + XFLM_DB_HDR * pDbHdr + // [IN] 2K buffer + // [OUT] Returns the DB header for the file. + ) +{ + RCODE rc = NE_XFLM_OK; + + // Verify the transaction type. + + if (eTransType != XFLM_UPDATE_TRANS && eTransType != XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); + goto Exit; + } + + // Verify the transaction flags + + if ((uiFlags & XFLM_DONT_KILL_TRANS) && eTransType != XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); + goto Exit; + } + + // Can't start an update transaction on a database that + // is locked in shared mode. + + if (eTransType == XFLM_UPDATE_TRANS && (m_uiFlags & FDB_FILE_LOCK_SHARED)) + { + rc = RC_SET( NE_XFLM_SHARED_LOCK); + goto Exit; + } + + // If the database is not running a transaction, start one. + + if (m_eTransType != XFLM_NO_TRANS) + { + + // Cannot nest transactions. + + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + if (RC_BAD( rc = beginTrans( eTransType, + uiMaxLockWait, uiFlags, pDbHdr))) + { + goto Exit; + } + + m_bHadUpdOper = FALSE; + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Starts a transaction. +*END************************************************************************/ +RCODE XFLMAPI F_Db::transBegin( + IF_Db * pDb + // [IN] Start a transaction that has the same view as whatever + // transaction is running on this database. NOTE: If pDb is + // running an update transaction, it is illegal for another pDb + // to also run an update transaction, so such a request would fail. + ) +{ + RCODE rc = NE_XFLM_OK; + + // Database cannot already be running a transaction. + + if (m_eTransType != XFLM_NO_TRANS) + { + + // Cannot nest transactions. + + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Verify the transaction type. + + if (((F_Db *)pDb)->m_eTransType != XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_ILLEGAL_TRANS_TYPE); + goto Exit; + } + + if (RC_BAD( rc = beginTrans( (F_Db *)pDb))) + { + goto Exit; + } + m_bHadUpdOper = FALSE; + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Obtains a a lock on the database. +*END************************************************************************/ +RCODE XFLMAPI F_Db::dbLock( + eDbLockType eLockType, + // [IN] Type of lock request - must be FLM_LOCK_EXCLUSIVE or + // FLM_LOCK_SHARED + FLMINT iPriority, + // [IN] Priority to be assigned to lock. + FLMUINT uiTimeout + // [IN] Seconds to wait for lock to be granted. XFLM_NO_TIMEOUT + // means that it will wait forever for the lock to be granted. + ) +{ + RCODE rc = NE_XFLM_OK; + + // eLockType better be exclusive or shared + + if (eLockType != XFLM_LOCK_EXCLUSIVE && eLockType != XFLM_LOCK_SHARED) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Nesting of locks is not allowed - this test also keeps this call from + // being executed inside an update transaction that implicitly acquired + // the lock. + + if (m_uiFlags & + (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED | FDB_FILE_LOCK_IMPLICIT)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Attempt to acquire the lock. + + if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Lock( this, m_hWaitSem, + TRUE, FALSE, (FLMBOOL)((eLockType == XFLM_LOCK_EXCLUSIVE) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE), + uiTimeout, iPriority, m_pDbStats))) + { + goto Exit; + } + m_uiFlags |= FDB_HAS_FILE_LOCK; + if (eLockType == XFLM_LOCK_SHARED) + { + m_uiFlags |= FDB_FILE_LOCK_SHARED; + } + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Releases a lock on the database +*END************************************************************************/ +RCODE XFLMAPI F_Db::dbUnlock( void) +{ + RCODE rc = NE_XFLM_OK; + + // If we don't have an explicit lock, can't do the unlock. It is + // also illegal to do the unlock during an update transaction. + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK) || + (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) || + (m_eTransType == XFLM_UPDATE_TRANS)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Unlock the file. + + if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this))) + { + goto Exit; + } + + // Unset the flags that indicated the file was explicitly locked. + + m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_SHARED)); + +Exit: + + if (RC_OK( rc)) + { + rc = checkState( __FILE__, __LINE__); + } + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Returns information about current and pending locks on the + database. +*END************************************************************************/ +RCODE XFLMAPI F_Db::getLockInfo( + FLMINT iPriority, + // [IN] A count of all locks with a priority >= to this priority + // level will be returned in pLockInfo. + eDbLockType * peCurrLockType, + FLMUINT * puiThreadId, + FLMUINT * puiNumExclQueued, + FLMUINT * puiNumSharedQueued, + FLMUINT * puiPriorityCount + ) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + m_pDatabase->m_pDatabaseLockObj->GetLockInfo( iPriority, + peCurrLockType, puiThreadId, + puiNumExclQueued, puiNumSharedQueued, + puiPriorityCount); + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Desc : Returns information about the lock held by the specified database + handle. +*END************************************************************************/ +RCODE XFLMAPI F_Db::getLockType( + eDbLockType * peLockType, + FLMBOOL * pbImplicit) +{ + RCODE rc = NE_XFLM_OK; + + if (peLockType) + { + *peLockType = XFLM_LOCK_NONE; + } + + if (pbImplicit) + { + *pbImplicit = FALSE; + } + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if (m_uiFlags & FDB_HAS_FILE_LOCK) + { + if (peLockType) + { + if (m_uiFlags & FDB_FILE_LOCK_SHARED) + { + *peLockType = XFLM_LOCK_SHARED; + } + else + { + *peLockType = XFLM_LOCK_EXCLUSIVE; + } + } + + if (pbImplicit) + { + *pbImplicit = (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) + ? TRUE + : FALSE; + } + } + +Exit: + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Forces a checkpoint on the database. +*END************************************************************************/ +RCODE XFLMAPI F_Db::doCheckpoint( + FLMUINT uiTimeout + // [IN] Seconds to wait to obtain lock on the database. + // XFLM_NO_TIMEOUT means that it will wait forever for + // the lock to be granted. + ) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Start an update transaction. Must not already be one going. + + if (m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // If we get to this point, we need to start a transaction on the + // database. + + if( RC_BAD( rc = beginTrans( XFLM_UPDATE_TRANS, uiTimeout))) + { + goto Exit; + } + + // Commit the transaction, forcing it to be checkpointed. + + m_bHadUpdOper = FALSE; + if (RC_BAD( rc = commitTrans( 0, TRUE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine locks a database for exclusive access. +****************************************************************************/ +RCODE F_Db::lockExclusive( + FLMUINT uiMaxLockWait) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bGotFileLock = FALSE; + + flmAssert( !m_pDatabase->m_bTempDb); + + // There must NOT be a shared lock on the file. + + if (m_uiFlags & FDB_FILE_LOCK_SHARED) + { + rc = RC_SET( NE_XFLM_SHARED_LOCK); + goto Exit; + } + + // Must acquire an exclusive file lock first, if it hasn't been + // acquired. + + if (!(m_uiFlags & FDB_HAS_FILE_LOCK)) + { + if (RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Lock( this, + m_hWaitSem, TRUE, FALSE, TRUE, uiMaxLockWait, 0, m_pDbStats))) + { + goto Exit; + } + bGotFileLock = TRUE; + m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); + } + + if (RC_OK( rc = m_pDatabase->dbWriteLock( m_hWaitSem, m_pDbStats))) + { + m_uiFlags |= FDB_HAS_WRITE_LOCK; + } + +Exit: + + if (rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) + { + if (bGotFileLock) + { + (void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this); + m_uiFlags &= (~(FDB_HAS_FILE_LOCK | + FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); + } + + if (m_eTransType != XFLM_NO_TRANS) + { + + // Unlink the DB from the transaction. + + unlinkFromTransList( FALSE); + } + } + else if (RC_BAD( rc)) + { + if (bGotFileLock) + { + (void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this); + m_uiFlags &= (~(FDB_HAS_FILE_LOCK | + FDB_FILE_LOCK_IMPLICIT | FDB_HAS_WRITE_LOCK)); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine unlocks a database that was previously locked + using the lockExclusive routine. +****************************************************************************/ +void F_Db::unlockExclusive( void) +{ + flmAssert( !m_pDatabase->m_bTempDb); + + // If we have the write lock, unlock it first. + + flmAssert( m_uiFlags & FDB_HAS_WRITE_LOCK); + + m_pDatabase->dbWriteUnlock( m_pDbStats); + m_uiFlags &= ~FDB_HAS_WRITE_LOCK; + + // Give up the file lock, if it was acquired implicitly. + + if (m_uiFlags & FDB_FILE_LOCK_IMPLICIT) + { + (void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this); + m_uiFlags &= (~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT)); + } + +} diff --git a/version5/src/fltrcmit.cpp b/version5/src/fltrcmit.cpp new file mode 100644 index 0000000..9f583e0 --- /dev/null +++ b/version5/src/fltrcmit.cpp @@ -0,0 +1,602 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for committing a transaction. +// +// 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: fltrcmit.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This routine commits an active transaction for a particular + database. +****************************************************************************/ +RCODE F_Db::commitTrans( + FLMUINT uiNewLogicalEOF, // New logical end-of-file. This is only + // set by the reduceSize function when + // it is truncating the file. + FLMBOOL bForceCheckpoint, // Force a checkpoint? + FLMBOOL * pbEmpty + ) +{ + RCODE rc = NE_XFLM_OK; + XFLM_DB_HDR * pUncommittedDbHdr; + FLMUINT uiCPFileNum = 0; + FLMUINT uiCPOffset = 0; + FLMUINT64 ui64TransId = 0; + FLMBOOL bTransEndLogged; + FLMBOOL bForceCloseOnError = FALSE; + eDbTransType eSaveTransType; + FLMBOOL bOkToLogAbort = TRUE; + FLMUINT uiCollection; + FLMUINT64 ui64DocId; + FLMUINT64 ui64NodeId; + FLMBOOL bIndexAfterCommit = FALSE; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMUINT uiRflToken = 0; + F_COLLECTION * pCollection; + FLMUINT uiLfNum; + + // Not allowed to commit temporary databases. + + flmAssert( !m_pDatabase->m_bTempDb); + + // See if we even have a transaction going. + + if (m_eTransType == XFLM_NO_TRANS) + { + goto Exit; // Will return NE_XFLM_OK. + } + + // See if we have a transaction going which should be aborted. + + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit1; + } + + // If we are in a read transaction we can skip most of the stuff + // below because no updates would have occurred. This will help + // improve performance. + + if (m_eTransType == XFLM_READ_TRANS) + { + if (m_bKrefSetup) + { + + // krefCntrlFree could be called w/o checking bKrefSetup because + // it checks the flag, but it is more optimal to check the + // flag before making the call because most of the time it will + // be false. + + krefCntrlFree(); + } + + goto Exit1; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Disable DB header writes + + pRfl->clearDbHdrs(); + + // At this point, we know we have an update transaction. + + ui64TransId = m_ui64CurrTransID; +#ifdef FLM_DBG_LOG + flmDbgLogUpdate( m_pDatabase, ui64TransId, 0, 0, NE_XFLM_OK, "TCmit"); +#endif + + // End any pending input operations + + if( m_pDatabase->m_pPendingInput) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INPUT_PENDING); + goto Exit1; + } + + // Call documentDone for any documents in the document list + + for (;;) + { + m_pDatabase->m_DocumentList.getNode( + 0, &uiCollection, &ui64DocId, &ui64NodeId); + + if (!uiCollection) + { + break; + } + + if (RC_BAD( rc = documentDone( uiCollection, ui64DocId))) + { + goto Exit1; + } + } + + // Flush any dirty cache nodes + + if( RC_BAD( rc = flushDirtyNodes())) + { + goto Exit1; + } + + // Write out any LFILE changes for collections + + flmAssert( m_pDict); + + // Only need to do collections. Nothing from the LFH of + // an index is stored in memory except for the root block + // address, and whenever that is changed, we get a new + // dictionary. Since the new dictionary will be discarded + // in that case, there is nothing to restore for an index. + + uiLfNum = 0; + while ((pCollection = m_pDict->getNextCollection( + uiLfNum, TRUE)) != NULL) + { + if (pCollection->bNeedToUpdateNodes) + { + if (RC_BAD( rc = m_pDatabase->lFileWrite( this, pCollection, + &pCollection->lfInfo))) + { + goto Exit; + } + } + uiLfNum = pCollection->lfInfo.uiLfNum; + } + + // Commit any keys in the KREF buffers. + + if (RC_BAD( rc = keysCommit( TRUE))) + { + flmLogError( rc, "calling keysCommit from commitTrans"); + goto Exit1; + } + + // If the transaction had no update operations, restore it + // to its pre-transaction state - make it appear that no + // transaction ever happened. + + if (!m_bHadUpdOper) + { + bOkToLogAbort = FALSE; + pRfl->enableLogging( &uiRflToken); + + rc = pRfl->logEndTransaction( this, RFL_TRNS_COMMIT_PACKET, TRUE); + + // Even though we didn't have any update operations, there may have + // been operations during the transaction (i.e., query operations) + // that initialized the KREF in order to generate keys. + + krefCntrlFree(); + + // Restore everything as if the transaction never happened. + + if (pbEmpty) + { + *pbEmpty = TRUE; + } + + // Even if the transaction is empty, there could be "uncommitted" nodes that + // were created, but never set to "dirty" - hence the m_bHadUpdOper flag + // would never have been set. So we need to call freeModifiedNodes to get + // rid of them. + + m_pDatabase->freeModifiedNodes( this, ui64TransId - 1); + goto Exit1; + } + + // Re-enable RFL logging + + pRfl->enableLogging( &uiRflToken); + + // Log commit record to roll-forward log + + bOkToLogAbort = FALSE; + if (RC_BAD( rc = pRfl->logEndTransaction( + this, RFL_TRNS_COMMIT_PACKET, FALSE, &bTransEndLogged))) + { + goto Exit1; + } + bForceCloseOnError = TRUE; + + // Reinitialize the log header. If the local dictionary was updated + // during the transaction, increment the local dictionary ID so that + // other concurrent users will know that it has been modified and + // that they need to re-read it into memory. + + // If we are in recovery mode, see if we need to force + // a checkpoint with what we have so far. We force a + // checkpoint on one of two conditions: + + // 1. If it appears that we have a buildup of dirty cache + // blocks. We force a checkpoint on this condition + // because it will be more efficient than replacing + // cache blocks one at a time. + // We check for this condition by looking to see if + // our LRU block is not used and it is dirty. That is + // a pretty good indicator that we have a buildup + // of dirty cache blocks. + // 2. We are at the end of the roll-forward log. We + // want to force a checkpoint here to complete the + // recovery phase. + + if (m_uiFlags & FDB_REPLAYING_RFL) + { + // If we are in the middle of upgrading, and are forcing + // a checkpoint, use the file number and offset that were + // set in the F_Db. + + if ((m_uiFlags & FDB_UPGRADING) && bForceCheckpoint) + { + uiCPFileNum = m_uiUpgradeCPFileNum; + uiCPOffset = m_uiUpgradeCPOffset; + } + else + { + FLMUINT uiCurrTime; + + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + f_mutexLock( gv_XFlmSysData.hShareMutex); + if (FLM_ELAPSED_TIME( uiCurrTime, m_pDatabase->m_uiLastCheckpointTime) >= + gv_XFlmSysData.uiMaxCPInterval || + !gv_XFlmSysData.uiMaxCPInterval || + (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && + (m_pDatabase->m_uiDirtyCacheCount + + m_pDatabase->m_uiLogCacheCount) * m_pDatabase->m_uiBlockSize > + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) || + pRfl->atEndOfLog() || + bForceCheckpoint) + { + bForceCheckpoint = TRUE; + uiCPFileNum = pRfl->getCurrFileNum(); + uiCPOffset = pRfl->getCurrReadOffset(); + } + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } + } + + // Move information collected in the pDb into the + // uncommitted DB header. Other things that need to be + // set have already been set in the uncommitted log header + // at various places in the code. + + // Mutex does not have to be locked while we do this because + // the update transaction is the only one that ever accesses + // the uncommitted log header buffer. + + pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; + + // Set the new logical EOF if passed in. + + if (uiNewLogicalEOF) + { + m_uiLogicalEOF = uiNewLogicalEOF; + } + pUncommittedDbHdr->ui32LogicalEOF = (FLMUINT32)m_uiLogicalEOF; + + // Increment the commit counter. + + pUncommittedDbHdr->ui64TransCommitCnt++; + + // Set the last committed transaction ID + + if (bTransEndLogged || (m_uiFlags & FDB_REPLAYING_COMMIT)) + { + pUncommittedDbHdr->ui64LastRflCommitID = ui64TransId; + } + + // Write the header + + pRfl->commitDbHdrs( pUncommittedDbHdr, + &m_pDatabase->m_checkpointDbHdr); + + // Commit any node cache. + + m_pDatabase->commitNodeCache(); + + // Push the IXD_FIXUP values back into the IXD + + if (m_pIxdFixups) + { + IXD_FIXUP * pIxdFixup; + IXD_FIXUP * pDeleteIxdFixup; + IXD * pIxd; + RCODE tmpRc; + + pIxdFixup = m_pIxdFixups; + while (pIxdFixup) + { + if (RC_BAD( tmpRc = m_pDict->getIndex( pIxdFixup->uiIndexNum, + NULL, &pIxd, TRUE))) + { + RC_UNEXPECTED_ASSERT( tmpRc); + pIxd = NULL; + } + + if (pIxd) + { + pIxd->ui64LastDocIndexed = pIxdFixup->ui64LastDocIndexed; + } + pDeleteIxdFixup = pIxdFixup; + pIxdFixup = pIxdFixup->pNext; + f_free( &pDeleteIxdFixup); + } + m_pIxdFixups = NULL; + } + + // Set the update transaction ID back to zero only + // AFTER we know the transaction has safely committed. + + m_pDatabase->lockMutex(); + + f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pUncommittedDbHdr, + sizeof( XFLM_DB_HDR)); + + if (m_uiFlags & FDB_UPDATED_DICTIONARY) + { + + // Link the new local dictionary to its file. + // Since the new local dictionary will be linked at the head + // of the list of FDICT structures, see if the FDICT currently + // at the head of the list is unused and can be unlinked. + + if (m_pDatabase->m_pDictList && !m_pDatabase->m_pDictList->getUseCount()) + { + m_pDatabase->m_pDictList->unlinkFromDatabase(); + } + m_pDict->linkToDatabase( m_pDatabase); + } + m_pDatabase->unlockMutex(); + + // Log blocks must be released after the last committed database header + // has been updated. If done before, there is a slight window of + // opportunity for a caller to attempt to start a read transaction. Between + // the time releaseLogBlocks unlocks the block cache mutex and when + // the committed header is copied into m_pDatabase, the read transaction + // could see the prior committed transaction ID and use that as the basis + // for its transaction. In the mean time, releaseLogBlocks may release + // versions of the blocks that would be needed to satisfy the new reader's + // (incorrect) view of the database. + + m_pDatabase->releaseLogBlocks(); + +Exit1: + + if (RC_BAD( rc)) + { + // Since we failed to commit, do an abort. We are purposely not + // checking the return code from flmAbortDbTrans because we already + // have an error return code. If we attempted to log the transaction + // to the RFL and failed, we don't want to try to log an abort packet. + // The RFL code has already reset the log back to the starting point + // of the transaction, thereby discarding all operations. + + (void)abortTrans( bOkToLogAbort); + eSaveTransType = XFLM_NO_TRANS; + + // Do we need to force all handles to close? + + if (bForceCloseOnError) + { + // Since the commit packet has already been logged to the RFL, + // we must have failed when trying to write the log header. The + // database is in a bad state and must be closed. + + // Set the "must close" flag on all FDBs linked to the FFILE + // and set the FFILE's "must close" flag. This will cause any + // subsequent operations on the database to fail until all + // handles have been closed. + + m_pDatabase->setMustCloseFlags( rc, FALSE); + } + } + else + { + eSaveTransType = m_eTransType; + if (m_eTransType == XFLM_UPDATE_TRANS) + { + if (gv_XFlmSysData.EventHdrs [XFLM_EVENT_UPDATES].pEventCBList) + { + flmTransEventCallback( XFLM_EVENT_COMMIT_TRANS, this, rc, + ui64TransId); + } + + // Do the indexing work before we unlock the db. + + if (m_pIxStopList || m_pIxStartList) + { + // Must not call indexingAfterCommit until after + // completeTransWrites. Otherwise, there is a potential + // deadlock condition where indexingAfterCommit is + // waiting on an indexing thread to quit, but that + // thread is waiting to be signaled by this thread that + // writes are completed. However, indexingAfterCommit + // also must only be called while the database is still + // locked. If we were to leave the database locked for + // every call to completeTransWrites, however, we would + // lose the group commit capability. Hence, we opt to + // only lose it when there are actual indexing operations + // to start or stop - which should be very few transactions. + // That is what the bIndexAfterCommit flag is for. + + bIndexAfterCommit = TRUE; + } + } + } + + // Unlock the database, if the update transaction is still going. + // NOTE: We check m_eTransType because it may have been reset + // to XFLM_NO_TRANS up above if abortTrans was called. + + if (m_eTransType == XFLM_UPDATE_TRANS) + { + if (RC_BAD( rc)) + { + + // SHOULD NEVER HAPPEN - because it would have been taken + // care of above - abortTrans would have been called and + // m_eTransType would no longer be XFLM_UPDATE_TRANS. + + RC_UNEXPECTED_ASSERT( rc); + pRfl->completeTransWrites( this, FALSE, TRUE); + } + else if (!bForceCheckpoint) + { + if (bIndexAfterCommit) + { + rc = pRfl->completeTransWrites( this, TRUE, FALSE); + indexingAfterCommit(); + unlinkFromTransList( TRUE); + } + else + { + rc = pRfl->completeTransWrites( this, TRUE, TRUE); + } + } + else + { + // Do checkpoint, if forcing. Before doing the checkpoint + // we have to make sure the roll-forward log writes + // complete. We don't want to unlock the DB while the + // writes are happening in this case - thus, the FALSE + // parameter to completeTransWrites. + + if (RC_OK( rc = pRfl->completeTransWrites( this, TRUE, FALSE))) + { + bForceCloseOnError = FALSE; + rc = m_pDatabase->doCheckpoint( m_hWaitSem, m_pDbStats, m_pSFileHdl, + (m_uiFlags & FDB_DO_TRUNCATE) ? TRUE : FALSE, + TRUE, XFLM_CP_TIME_INTERVAL_REASON, + uiCPFileNum, uiCPOffset); + } + + if (bIndexAfterCommit) + { + indexingAfterCommit(); + } + + unlinkFromTransList( TRUE); + } + + if (RC_BAD( rc) && bForceCloseOnError) + { + + // Since the commit packet has already been logged to the RFL, + // we must have failed when trying to write the log header. The + // database is in a bad state and must be closed. + + // Set the "must close" flag on all F_Db objects linked to the + // F_Database object and set the F_Database object's "must close" + // flag. This will cause any subsequent operations on the + // database to fail until all handles have been closed. + + m_pDatabase->setMustCloseFlags( rc, FALSE); + } + } + else + { + + // Unlink the database from the transaction list + + unlinkFromTransList( FALSE); + } + + if (m_pDbStats && eSaveTransType != XFLM_NO_TRANS) + { + FLMUINT64 ui64ElapMilli = 0; + + flmAddElapTime( &m_TransStartTime, &ui64ElapMilli); + m_pDbStats->bHaveStats = TRUE; + if (eSaveTransType == XFLM_READ_TRANS) + { + m_pDbStats->ReadTransStats.CommittedTrans.ui64Count++; + m_pDbStats->ReadTransStats.CommittedTrans.ui64ElapMilli += + ui64ElapMilli; + } + else + { + m_pDbStats->UpdateTransStats.CommittedTrans.ui64Count++; + m_pDbStats->UpdateTransStats.CommittedTrans.ui64ElapMilli += + ui64ElapMilli; + } + } + + // Update stats + + if (m_pStats) + { + (void)flmStatUpdate( &m_Stats); + } + +Exit: + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + return( rc); +} + +/*API~*********************************************************************** +Area : TRANSACTION +Desc : Commits an active transaction. +*END************************************************************************/ +RCODE XFLMAPI F_Db::transCommit( + FLMBOOL * pbEmpty) // may be NULL +{ + RCODE rc = NE_XFLM_OK; + + if (m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (pbEmpty) + { + *pbEmpty = FALSE; + } + + rc = commitTrans( 0, FALSE, pbEmpty); + +Exit: + + if( RC_OK( rc)) + { + rc = checkState( __FILE__, __LINE__); + } + + return( rc); +} diff --git a/version5/src/flutil.cpp b/version5/src/flutil.cpp new file mode 100644 index 0000000..cf932e1 --- /dev/null +++ b/version5/src/flutil.cpp @@ -0,0 +1,3953 @@ +//------------------------------------------------------------------------------ +// 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: flutil.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/* +* Macros used by f_wtoa(), f_dtoa(), f_uwtoa(), and f_udtoa() +*/ + +#define HANDLE_NEGATIVE /* output sign and make value positive */\ + if( value < 0) \ + { *ptr++ = '-'; \ + absValue = (FLMUINT)(-(value)); \ + } \ + else absValue = (FLMUINT)value; + +#define HANDLE_DNEGATIVE /* output sign and make value positive */\ + 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'); /* convert and push */\ + } while( reg /= 10); \ + } + +#define POP_DIGITS \ + while( stack < sp--) /* stack assumes post-inc & pre-dec */\ + *ptr++ = *sp; \ + *ptr = '\0'; + + +/**************************************************************************** +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 * f_uwtoa( + FLMUINT16 value, + char * ptr) +{ + char stack[ 10]; /* max: 10 digits */ + char * sp = stack; /* stack pointer */ + + PUSH_DIGITS( value); + + POP_DIGITS; + + return( ptr); /* Return pointer to null */ +} + +/**************************************************************************** +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 f_atoud( + 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 f_atou64( + 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 * f_udtoa( + FLMUINT value, + char * ptr) +{ + char stack[ 10]; /* max: 10 digits */ + char * sp = stack; /* stack pointer */ + + PUSH_DIGITS( value); + + POP_DIGITS; + + return( ptr); /* Return pointer to null */ +} + +/**************************************************************************** +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 * f_wtoa( + FLMINT16 value, + char * ptr) +{ + char stack[ 10]; /* max: 10 digits values on stack */ + char * sp = stack; /* stack pointer */ + FLMUINT absValue; /* algorithm assumes absolute value */ + + HANDLE_NEGATIVE; + + PUSH_DIGITS( absValue); + + POP_DIGITS; + + return( ptr); /* Return pointer to null terminator */ +} + +/**************************************************************************** +Desc: Double (4 byte) number to native value - null terminate the string +Return: char pointer to the NULL byte in the native string +****************************************************************************/ +char * f_dtoa( + FLMINT value, + char * ptr) +{ + char stack[ 10]; /* max: 10 digits values on stack */ + char * sp = stack; /* stack pointer */ + FLMUINT absValue; /* algorithm assumes absolute value */ + + HANDLE_DNEGATIVE; + + PUSH_DIGITS( absValue); + + POP_DIGITS; + + return( ptr); /* Return pointer to null terminator */ +} + +/***************************************************************************** +Desc: Convert unsigned 64 bit value to ASCII. +*****************************************************************************/ +char * 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 * f_i64toa( + FLMINT64 i64Value, + char * pszAscii) +{ + if (i64Value < 0) + { + *pszAscii++ = '-'; + i64Value = -i64Value; + } + return( f_ui64toa( (FLMUINT64)i64Value, pszAscii)); +} + +/**************************************************************************** +Desc: Ascii to integer +****************************************************************************/ +FLMINT f_atoi( + char * pszStr) /* Points to native number */ +{ + return( f_atod( pszStr)); +} + +/**************************************************************************** +Desc: native to long +****************************************************************************/ +FLMINT f_atol( + char * pszStr) /* Points to native number */ +{ + 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 f_atod( + 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 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 * f_unicpy( + FLMUNICODE * puzDestStr, + FLMUNICODE * puzSrcStr) +{ + FLMUNICODE * puzSrc = puzSrcStr; + FLMUNICODE * puzDest = puzDestStr; + + while( *puzSrc) + { + *puzDest++ = *puzSrc++; + } + + *puzDest = 0; + return( puzDestStr); +} + +/**************************************************************************** +Desc: A rather trivial unicode monocase function. +****************************************************************************/ +FLMUNICODE 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 f_unicmp( + FLMUNICODE * puzStr1, + FLMUNICODE * puzStr2) +{ + while( *puzStr1 == *puzStr2 && *puzStr1) + { + puzStr1++; + puzStr2++; + } + + return( (FLMINT)*puzStr1 - (FLMINT)*puzStr2); +} + +/**************************************************************************** +Desc: Performs a case-insensitive comparision of two Unicode strings +****************************************************************************/ +FLMINT f_uniicmp( + FLMUNICODE * puzStr1, + 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 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 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); +} + +#if defined( FLM_MWERKS_NLM) + +/**************************************************************************** +Desc: Atomic increment... returns incremented value +****************************************************************************/ +FLMUINT32 ftkAtomicIncrement( + FLMUINT32 * puiTarget) +{ + __asm + { + mov eax, 1 + mov ecx, puiTarget + lock xadd [ecx], eax + inc eax + } +} + +/**************************************************************************** +Desc: Atomic decrement... returns decremented value +****************************************************************************/ +FLMUINT32 ftkAtomicDecrement( + FLMUINT32 * puiTarget) +{ + __asm + { + mov eax, 0ffffffffh + mov ecx, puiTarget + lock xadd [ecx], eax + dec eax + } +} +#endif + +/*************************************************************************** +Desc: Sort an array of items +****************************************************************************/ +void 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; +} + +/*************************************************************************** +Desc: Functions to test various properties of a unicode character +****************************************************************************/ + +#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}; + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::uniIsUpper( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + // Which nibble do we need to look at? + if (uzChar & 0x1) + { + // Low nibble + bRV = (UnicodeProperties[uzChar/2] & UNICODE_UPPERCASE_MASK) ? TRUE : FALSE; + } + else + { + // High nibble + bRV = ((UnicodeProperties[uzChar/2] >> 4) & UNICODE_UPPERCASE_MASK) ? TRUE : FALSE; + } + return bRV; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::uniIsLower( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + // Which nibble do we need to look at? + if (uzChar & 0x1) + { + // Low nibble + bRV = (UnicodeProperties[uzChar/2] & UNICODE_LOWERCASE_MASK) ? TRUE : FALSE; + } + else + { + // High nibble + bRV = ((UnicodeProperties[uzChar/2] >> 4) & UNICODE_LOWERCASE_MASK) ? TRUE : FALSE; + } + return bRV; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::uniIsAlpha( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + // Which nibble do we need to look at? + if (uzChar & 0x1) + { + // Low nibble + bRV = (UnicodeProperties[uzChar/2] & UNICODE_ALPHABETIC_MASK) ? TRUE : FALSE; + } + else + { + // High nibble + bRV = ((UnicodeProperties[uzChar/2] >> 4) & UNICODE_ALPHABETIC_MASK) ? TRUE : FALSE; + } + return bRV; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::uniIsDecimalDigit( + FLMUNICODE uzChar) +{ + FLMBOOL bRV; + + // Which nibble do we need to look at? + if (uzChar & 0x1) + { + // Low nibble + bRV = (UnicodeProperties[uzChar/2] & UNICODE_DECIMAL_DIGIT_MASK) ? TRUE : FALSE; + } + else + { + // High nibble + bRV = ((UnicodeProperties[uzChar/2] >> 4) & UNICODE_DECIMAL_DIGIT_MASK) ? TRUE : FALSE; + } + return bRV; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMUNICODE XFLMAPI F_DbSystem::uniToLower( + FLMUNICODE uzChar) +{ + return f_unitolower( uzChar); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::nextUCS2Char( + const FLMBYTE ** ppszUTF8, + const FLMBYTE * pszEndOfUTF8String, + FLMUNICODE * puzChar) +{ + return flmGetCharFromUTF8Buf( ppszUTF8, pszEndOfUTF8String, puzChar); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::numUCS2Chars( + const FLMBYTE * pszUTF8, + FLMUINT * puiNumChars) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTemp = 0; + FLMUNICODE uzChar; + + for (;;) + { + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pszUTF8, NULL, &uzChar))) + { + goto Exit; + } + if (!uzChar) + { + break; + } + uiTemp++; + } + +Exit: + + *puiNumChars = uiTemp; + return rc; +} diff --git a/version5/src/flverify.cpp b/version5/src/flverify.cpp new file mode 100644 index 0000000..bac60e5 --- /dev/null +++ b/version5/src/flverify.cpp @@ -0,0 +1,4907 @@ +//------------------------------------------------------------------------------ +// Desc: Verify data in a database. +// +// 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: flverify.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC FLMBYTE * getEntryEnd( + FLMBYTE * pucEntry, + FLMUINT uiBlkType); + +FLMUINT64 getLinkVal( + FLMUINT uiBMId, + NODE_RS_ENTRY * pRSEntry); + +FSTATIC RCODE flmVerifyKeyOrder( + STATE_INFO * pStateInfo, + LFILE * pLFile, + IXD * pIxd, + F_BLK_HDR * pBlkHdr, + FLMBYTE * pucElmKey, + FLMUINT uiElmKeyLen, + FLMUINT uiElmOffset); + +FSTATIC FLMBOOL flmVerifyElementChain( + STATE_INFO * pStateInfo, + LFILE * pLFile); + +FSTATIC RCODE verifyRootLink( + NODE_RS_ENTRY * pRSEntry, + FLMUINT uiRSEntrySize, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyParentLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyFirstChildLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyLastChildLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyPrevSiblingLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyNextSiblingLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +FSTATIC RCODE verifyAnnotationLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode); + +/******************************************************************** +Desc: Verifies a block's header and sets up the STATE_INFO structure + to verify the rest of the block. +*********************************************************************/ +FLMINT flmVerifyBlockHeader( + STATE_INFO * pStateInfo, + BLOCK_INFO * pBlockInfo, + FLMUINT uiBlockSize, + FLMUINT uiExpNextBlkAddr, + FLMUINT uiExpPrevBlkAddr, + FLMBOOL bCheckEOF) +{ + F_BLK_HDR * pBlkHdr = pStateInfo->pBlkHdr; + + if (pBlockInfo) + { + pBlockInfo->uiBlockCount++; + } + + pStateInfo->ui32NextBlkAddr = pBlkHdr->ui32NextBlkInChain; + + if( (FLMUINT)pBlkHdr->ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr)) + { + return( FLM_BAD_BLK_HDR_BLK_END); + } + else + { + if( pBlockInfo) + { + pBlockInfo->ui64BytesUsed += + (FLMUINT64)(uiBlockSize - pBlkHdr->ui16BlkBytesAvail - + blkHdrSize( pBlkHdr)); + } + } + + // Verify the block address. + + if ((FLMUINT)pBlkHdr->ui32BlkAddr != pStateInfo->ui32BlkAddress) + { + return( FLM_BAD_BLK_HDR_ADDR); + } + + // Verify that block address is below the logical EOF + // Rebuild passes in FALSE for bCheckEOF + + if( bCheckEOF && pStateInfo->pDb) + { + if( !FSAddrIsBelow( pStateInfo->ui32BlkAddress, + pStateInfo->pDb->getLogicalEOF())) + { + return( FLM_BAD_FILE_SIZE); + } + } + + // Verify the block type. + + if( pStateInfo->uiBlkType != 0xFF && + pStateInfo->uiBlkType != (FLMUINT)pBlkHdr->ui8BlkType) + { + return( FLM_BAD_BLK_HDR_TYPE); + } + + // Verify the block level - if there is one + + if( pStateInfo->uiBlkType != BT_DATA_ONLY && + pStateInfo->uiLevel != 0xFF && + blkIsBTree( pBlkHdr) && + pStateInfo->uiLevel != + (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel)) + { + return( FLM_BAD_BLK_HDR_LEVEL); + } + + // Verify the previous block address. If uiExpPrevBlkAddr is 0xFFFFFFFF, + // we do not know what the previous address should be, so we don't verify. + + if (uiExpPrevBlkAddr != 0xFFFFFFFF && + uiExpPrevBlkAddr != (FLMUINT)pBlkHdr->ui32PrevBlkInChain) + { + return( FLM_BAD_BLK_HDR_PREV); + } + + // Verify the next block address. If uiExpNextBlkAddr is 0xFFFFFFFF, + // we do not know what the next address should be, se we don't verify. + + if( uiExpNextBlkAddr != 0xFFFFFFFF && + uiExpNextBlkAddr != pStateInfo->ui32NextBlkAddr) + { + return( FLM_BAD_BLK_HDR_NEXT); + } + + // Verify that if it is a root block, the root bit flags is set, + // or if it is NOT a root block, that the root bit flag is NOT set. + // Note that Data Only blocks don't have the extended header, so we + // can't do this check... + + if( pStateInfo->pCollection && + (pStateInfo->uiBlkType != BT_DATA_ONLY) ) + { + if( pStateInfo->uiLevel != 0xFF) + { + F_BTREE_BLK_HDR * pBTreeBlkHdr = (F_BTREE_BLK_HDR *)pBlkHdr; + FLMBOOL bShouldBeRootBlk = + (pStateInfo->uiLevel == + pStateInfo->uiRootLevel) + ? TRUE + : FALSE; + + if ((bShouldBeRootBlk && !isRootBlk( pBTreeBlkHdr)) || + (!bShouldBeRootBlk && isRootBlk( pBTreeBlkHdr))) + { + return( FLM_BAD_BLK_HDR_ROOT_BIT); + } + } + + // Verify the logical file number - if any. + + if( (pBlkHdr->ui8BlkType != BT_DATA_ONLY) && + (pStateInfo->pCollection->lfInfo.uiLfNum != + (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile)) ) + { + return( FLM_BAD_BLK_HDR_LF_NUM); + } + } + + return( 0); +} + +/******************************************************************** +Desc: Verify an index or data element in a b-tree and set pStateInfo. +*********************************************************************/ +RCODE flmVerifyElement( + STATE_INFO * pStateInfo, + LFILE * pLFile, + IXD * pIxd, + FLMINT * piErrCode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEntry; + FLMUINT32 ui32DOAddr = 0; + FLMUINT32 ui32ChildAddr = 0; + FLMUINT uiCounts = 0; + + *piErrCode = 0; + + // Get the pointer to the element. + + pucEntry = BtEntry( (FLMBYTE *)pStateInfo->pBlkHdr, pStateInfo->uiElmOffset); + pStateInfo->pucElm = pucEntry; + + // Verify that the address of the entry is within the block. + + if ((FLMUINT)pucEntry > (FLMUINT)pStateInfo->pBlkHdr + + pStateInfo->pDb->getDatabase()->getBlockSize()) + { + *piErrCode = FLM_BAD_ELM_OFFSET; + goto Exit; + + } + + switch( pStateInfo->pBlkHdr->ui8BlkType) + { + case BT_LEAF: + { + pStateInfo->uiElmKeyLen = FB2UW( pucEntry); + f_memcpy( pStateInfo->pucElmKey, pucEntry + 2, pStateInfo->uiElmKeyLen); + pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 2; + break; + } + + case BT_LEAF_DATA: + { + FLMBYTE ucFlags = *pucEntry; + FLMBYTE * pucPtr = pucEntry + 1; + FLMUINT uiElmLen = 1; + + if( ucFlags & BTE_FLAG_KEY_LEN) + { + // 2 byte key length + + pStateInfo->uiElmKeyLen = FB2UW( pucPtr); + pucPtr += 2; + uiElmLen += 2; + } + else + { + // 1 byte key length + + pStateInfo->uiElmKeyLen = (FLMUINT)*pucPtr; + pucPtr++; + uiElmLen++; + + } + + if( ucFlags & BTE_FLAG_DATA_LEN) + { + // 2 byte data length + + pStateInfo->uiElmDataLen = FB2UW( pucPtr); + pucPtr += 2; + uiElmLen += 2; + } + else + { + // 1 byte data length + + pStateInfo->uiElmDataLen = (FLMUINT)*pucPtr; + pucPtr++; + uiElmLen++; + } + + if( ucFlags & BTE_FLAG_OA_DATA_LEN) + { + pStateInfo->uiElmOADataLen = FB2UD( pucPtr); + pucPtr += 4; + uiElmLen += 4; + } + + f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); + pucPtr += pStateInfo->uiElmKeyLen; + uiElmLen += pStateInfo->uiElmKeyLen; + + pStateInfo->pucElmData = pucPtr; + uiElmLen += pStateInfo->uiElmDataLen; + + if( ucFlags & BTE_FLAG_DATA_BLOCK) + { + ui32DOAddr = FB2UD( pucPtr); + } + + pStateInfo->uiElmLen = uiElmLen; + break; + } + + case BT_NON_LEAF: + { + FLMBYTE * pucPtr = pucEntry; + + ui32ChildAddr = FB2UD( pucPtr); + pucPtr += 4; + + pStateInfo->uiElmKeyLen = FB2UW( pucPtr); + pucPtr += 2; + + f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); + pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 6; + break; + } + + case BT_NON_LEAF_COUNTS: + { + FLMBYTE * pucPtr = pucEntry; + + ui32ChildAddr = FB2UD( pucPtr); + pucPtr += 4; + + uiCounts = FB2UD( pucPtr); + pucPtr += 4; + + pStateInfo->uiElmKeyLen = FB2UW( pucPtr); + pucPtr += 2; + + f_memcpy( pStateInfo->pucElmKey, pucPtr, pStateInfo->uiElmKeyLen); + pStateInfo->uiElmLen = pStateInfo->uiElmKeyLen + 10; + break; + } + + default: + { + *piErrCode = FLM_BAD_BLK_TYPE; + goto Exit; + } + } + + // Verify that the end of the element is not past the end of the block. + + if( pStateInfo->pucElm + pStateInfo->uiElmLen > + (FLMBYTE *)pStateInfo->pBlkHdr + + pStateInfo->pDb->getDatabase()->getBlockSize()) + { + *piErrCode = FLM_BAD_ELM_LEN; + goto Exit; + } + + // Verify the key length is not too big + + if( pStateInfo->uiElmKeyLen > MAX_KEY_SIZ) + { + *piErrCode = FLM_BAD_ELM_KEY_SIZE; + goto Exit; + } + + // Verify the key order. First the previous key, then the next key. + + if( RC_BAD( rc = flmVerifyKeyOrder( pStateInfo, pLFile, pIxd, + pStateInfo->pBlkHdr, pStateInfo->pucElmKey, pStateInfo->uiElmKeyLen, + pStateInfo->uiElmOffset))) + { + *piErrCode = FLM_BAD_ELM_KEY_ORDER; + goto Exit; + } + + pStateInfo->bValidKey = TRUE; + + if( !isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr)) + { + switch( pStateInfo->pBlkHdr->ui8BlkType) + { + case BT_LEAF: + case BT_LEAF_DATA: + { + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + + if( pStateInfo->pucElm[0] & BTE_FLAG_FIRST_ELEMENT) + { + if( !flmVerifyElementChain( pStateInfo, pLFile)) + { + *piErrCode = FLM_BAD_ELEMENT_CHAIN; + goto Exit; + } + } + + // The key length may be zero on a LEM + + if( pStateInfo->uiElmKeyLen) + { + if( RC_BAD( rc = flmCollation2Number( + pStateInfo->uiElmKeyLen, pStateInfo->pucElmKey, + &pStateInfo->ui64ElmNodeId, &bNeg, &uiBytesProcessed))) + { + *piErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + + if( bNeg || uiBytesProcessed != pStateInfo->uiElmKeyLen) + { + *piErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + + if( !pStateInfo->ui64ElmNodeId) + { + flmAssert( 0); + *piErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + } + else + { + // If the key length is zero, then this MUST be the last block! + + if( pStateInfo->pBlkHdr->ui32NextBlkInChain) + { + *piErrCode = FLM_BAD_ELM_KEY; + goto Exit; + } + } + + break; + } + + case BT_NON_LEAF: + { + break; + } + + case BT_NON_LEAF_COUNTS: + { + break; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Verify that the key order is correct. +*****************************************************************************/ +FSTATIC RCODE flmVerifyKeyOrder( + STATE_INFO * pStateInfo, + LFILE * pLFile, + IXD * pIxd, + F_BLK_HDR * pBlkHdr, + FLMBYTE * pucElmKey, + FLMUINT uiElmKeyLen, + FLMUINT uiElmOffset) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pPrevSCache = NULL; + F_CachedBlock * pNextSCache = NULL; + F_BTREE_BLK_HDR * pPrevBlk; + F_BTREE_BLK_HDR * pNextBlk; + FLMBYTE * pucEntry = NULL; + FLMBYTE * pucKey = NULL; + FLMUINT uiKeyLen = 0; + FLMBOOL bCheckPrev = FALSE; + FLMBOOL bCheckNext = FALSE; + FLMUINT uiBlockSize; + + uiBlockSize = pStateInfo->pDb->getDatabase()->getBlockSize(); + + // Get the previous key + + if( uiElmOffset) + { + flmAssert( uiElmOffset < ((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys); + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset - 1); + + if( (FLMUINT)pucEntry > (FLMUINT)pBlkHdr + uiBlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + bCheckPrev = TRUE; + } + else if( uiElmOffset == 0 && pBlkHdr->ui32PrevBlkInChain) + { + if( pLFile) + { + // Need to get the previous block. + + if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( + pStateInfo->pDb, pLFile, pBlkHdr->ui32PrevBlkInChain, NULL, + &pPrevSCache))) + { + goto Exit; + } + + pPrevBlk = (F_BTREE_BLK_HDR *)pPrevSCache->getBlockPtr(); + pucEntry = BtEntry( (FLMBYTE *)pPrevBlk, pPrevBlk->ui16NumKeys - 1); + + if( (FLMUINT)pucEntry > (FLMUINT)pPrevBlk + uiBlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( pBlkHdr->ui8BlkType != pPrevBlk->stdBlkHdr.ui8BlkType) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + bCheckPrev = TRUE; + } + } + + if( bCheckPrev) + { + // Get the key + + switch (pBlkHdr->ui8BlkType) + { + case BT_LEAF: + { + uiKeyLen = FB2UW( pucEntry); + pucKey = pucEntry + 2; + break; + } + + case BT_LEAF_DATA: + { + FLMBYTE ucFlags = *pucEntry; + FLMBYTE * pucPtr = pucEntry + 1; + + flmAssert( (*pucEntry & 0x03) == 0); + + if (ucFlags & BTE_FLAG_KEY_LEN) + { + uiKeyLen = FB2UW( pucPtr); + pucPtr += 2; + } + else + { + uiKeyLen = *pucPtr; + pucPtr++; + } + + if( ucFlags & BTE_FLAG_DATA_LEN) + { + pucPtr += 2; + } + else + { + pucPtr++; + } + + if (ucFlags & BTE_FLAG_OA_DATA_LEN) + { + pucPtr += 4; + } + + pucKey = pucPtr; + break; + } + + case BT_NON_LEAF: + { + FLMBYTE * pucPtr = pucEntry + 4; + + uiKeyLen = FB2UW( pucPtr); + pucKey = pucPtr + 2; + break; + } + + case BT_NON_LEAF_COUNTS: + { + FLMBYTE * pucPtr = pucEntry + 8; + + uiKeyLen = FB2UW( pucPtr); + pucKey = pucPtr + 2; + break; + } + } + + if( uiKeyLen) + { + if( !pLFile || pLFile->eLfType == XFLM_LF_COLLECTION) + { + if( f_memcmp( pucElmKey, pucKey, uiElmKeyLen < uiKeyLen + ? uiElmKeyLen + : uiKeyLen) < 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + else + { + // If uiElmKeyLen == 0, it is the LEM, in which case we won't + // do the comparison, because pucElmKey is greater than pucKey + // by definition. + + if( uiElmKeyLen) + { + FLMINT iCompare; + + if( RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL, + NULL, NULL, TRUE, TRUE, pucElmKey, uiElmKeyLen, + pucKey, uiKeyLen, &iCompare))) + { + goto Exit; + } + + if( iCompare < 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + + // Get the next key + + if( uiElmOffset < (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset + 1); + + if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + bCheckNext = TRUE; + } + else if( uiElmOffset == + (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys - 1 && + pBlkHdr->ui32NextBlkInChain) + { + if( pLFile) + { + if( RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( + pStateInfo->pDb, pLFile, pBlkHdr->ui32NextBlkInChain, + NULL, &pNextSCache))) + { + goto Exit; + } + + pNextBlk = (F_BTREE_BLK_HDR *)pNextSCache->getBlockPtr(); + pucEntry = BtEntry( (FLMBYTE *)pNextBlk, 0); + + if( pucEntry > (FLMBYTE *)pNextBlk + uiBlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( pBlkHdr->ui8BlkType != pNextBlk->stdBlkHdr.ui8BlkType) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + bCheckNext = TRUE; + } + } + + if( bCheckNext) + { + switch (pBlkHdr->ui8BlkType) + { + case BT_LEAF: + { + uiKeyLen = FB2UW( pucEntry); + pucKey = pucEntry + 2; + break; + } + + case BT_LEAF_DATA: + { + FLMBYTE ucFlags = *pucEntry; + FLMBYTE * pucPtr = pucEntry + 1; + + if( (*pucEntry & 0x03) != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( ucFlags & BTE_FLAG_KEY_LEN) + { + uiKeyLen = FB2UW( pucPtr); + pucPtr += 2; + } + else + { + uiKeyLen = *pucPtr; + pucPtr++; + } + + if( ucFlags & BTE_FLAG_DATA_LEN) + { + pucPtr += 2; + } + else + { + pucPtr++; + } + + if( ucFlags & BTE_FLAG_OA_DATA_LEN) + { + pucPtr += 4; + } + + pucKey = pucPtr; + break; + } + + case BT_NON_LEAF: + { + FLMBYTE * pucPtr = pucEntry + 4; + + uiKeyLen = FB2UW( pucPtr); + pucKey = pucPtr + 2; + break; + } + + case BT_NON_LEAF_COUNTS: + { + FLMBYTE * pucPtr = pucEntry + 8; + + uiKeyLen = FB2UW( pucPtr); + pucKey = pucPtr + 2; + break; + } + } + + if (uiKeyLen) + { + if (!pLFile || pLFile->eLfType == XFLM_LF_COLLECTION) + { + if (f_memcmp( pucKey, pucElmKey, uiElmKeyLen < uiKeyLen + ? uiElmKeyLen + : uiKeyLen) < 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + else + { + // If uiElmKeyLen == 0, it is the LEM, in which case we won't + // do the comparison, because pucKey will always be less than + // the LEM, but it is not an error in that case. + + if( uiElmKeyLen) + { + FLMINT iCompare; + + if (RC_BAD( rc = ixKeyCompare( pStateInfo->pDb, pIxd, NULL, + NULL, NULL, TRUE, TRUE, pucKey, uiKeyLen, pucElmKey, + uiElmKeyLen, &iCompare))) + { + goto Exit; + } + + if( iCompare < 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + } + } + else + { + // A zero length key should only occur on the last block in the chain. + + if( pBlkHdr->ui32NextBlkInChain) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + } + +Exit: + + if( pPrevSCache) + { + ScaReleaseCache( pPrevSCache, FALSE); + } + + if( pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC FLMBOOL flmVerifyElementChain( + STATE_INFO * pStateInfo, + LFILE * pLFile) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bResult = TRUE; + F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr; + FLMBYTE * pucElmKey = pStateInfo->pucElmKey; + FLMUINT uiElmKeyLen = pStateInfo->uiElmKeyLen; + FLMUINT uiElmOffset = pStateInfo->uiElmOffset; + FLMBYTE * pucNextKey = NULL; + FLMUINT uiNextKeySize; + FLMBYTE ucFlags = pStateInfo->pucElm[ 0]; + F_CachedBlock * pSCache = NULL; + + if (pLFile) + { + while (!(ucFlags & BTE_FLAG_LAST_ELEMENT)) + { + // Get the next element. + if (uiElmOffset >= (FLMUINT)(pBlkHdr->ui16NumKeys - 1)) + { + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + } + + // Will have to go the the next block to find the key we want. + if (RC_BAD( rc = pStateInfo->pDb->getDatabase()->getBlock( + pStateInfo->pDb, pLFile, pBlkHdr->stdBlkHdr.ui32NextBlkInChain, + NULL, &pSCache))) + { + RC_UNEXPECTED_ASSERT( rc); + bResult = FALSE; + goto Exit; + } + + flmAssert( pBlkHdr->stdBlkHdr.ui8BlkType == + pSCache->getBlockPtr()->ui8BlkType); + + pBlkHdr = (F_BTREE_BLK_HDR *)pSCache->getBlockPtr(); + uiElmOffset = 0; // Get the first element... + } + else + { + uiElmOffset++; + } + + pucNextKey = BtEntry( (FLMBYTE *)pBlkHdr, uiElmOffset); + + // Update the flag for the next iteration + ucFlags = pucNextKey[ 0]; + + if (ucFlags & BTE_FLAG_FIRST_ELEMENT) + { + flmAssert( 0); + bResult = FALSE; + goto Exit; + } + + // Find the key. + pucNextKey++; + + if (ucFlags & BTE_FLAG_KEY_LEN) + { + uiNextKeySize = FB2UW( pucNextKey); + pucNextKey += 2; + } + else + { + uiNextKeySize = *pucNextKey; + pucNextKey++; + } + + // Key size is now set. They should match. + if (uiElmKeyLen != uiNextKeySize) + { + flmAssert( 0); + bResult = FALSE; + goto Exit; + } + + // Scoot past the info we don't need + if (ucFlags & BTE_FLAG_DATA_LEN) + { + pucNextKey += 2; + } + else + { + pucNextKey++; + } + + if (ucFlags & BTE_FLAG_OA_DATA_LEN) + { + pucNextKey += 4; + } + + // Make sure the keys match. + if ( f_memcmp( pucElmKey, pucNextKey, uiElmKeyLen) != 0) + { + flmAssert( 0); + bResult = FALSE; + goto Exit; + } + + } + } + +Exit: + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + return TRUE; +} + +/******************************************************************** +Desc: Build the result set of all keys in the database. +*********************************************************************/ +RCODE F_DbCheck::buildIndexKeyList( + FLMUINT64 * pui64TotalKeys) +{ + RCODE rc = NE_XFLM_OK; + F_KeyCollector * pKeyColl = NULL; + DOC_IXD_XREF XRef; + IXD * pIxd; + LFILE * pLFile; + F_DOMNode * pDocNode = NULL; + F_Dict * pDict; + FLMBOOL bUpdTranStarted = FALSE; + RCODE tmpRc = NE_XFLM_OK; + FLMUINT uiSizeRV; + F_Btree * pBTree = NULL; + + // Set information for the result set sort phase. + + m_Progress.iCheckPhase = XFLM_CHECK_RS_SORT; + m_Progress.bStartFlag = TRUE; + + if ((pKeyColl = f_new F_KeyCollector( this)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Save the key collector in the m_pDb to redirect the + // keys generated into the key result set. + + m_pDb->setKeyCollector( pKeyColl); + + // Start an update transaction, end the read trans. + + if (m_pDb->getTransType() == XFLM_READ_TRANS) + { + if (RC_OK( rc = m_pDb->transCommit())) + { + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bUpdTranStarted = TRUE; + } + else + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pDb->getDictionary( &pDict))) + { + goto Exit; + } + + // For each entry in the Xref result set, call indexDocument to generate the + // keys. + + if (RC_BAD( rc = m_pXRefRS->getBTree( NULL, NULL, &pBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pXRefRS->getFirst( NULL, NULL, pBTree, (FLMBYTE *)&XRef, + sizeof( XRef), &uiSizeRV, NULL, 0, NULL))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = m_pDb->getNode( XRef.uiCollection, XRef.ui64DocId, + &pDocNode))) + { + goto Exit; + } + + if( pDocNode->getDocumentId() == XRef.ui64DocId) + { + if( RC_BAD( rc = pDict->getIndex( XRef.uiIndexNum, &pLFile, &pIxd))) + { + // If the index is offline, skip it and move to the next, if any + + if( rc == NE_XFLM_INDEX_OFFLINE) + { + if( RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree, + (FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, + 0, NULL))) + { + if( rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + + break; + } + + continue; + } + + goto Exit; + } + + if (RC_BAD( rc = m_pDb->indexDocument( pIxd, pDocNode))) + { + goto Exit; + } + + m_Progress.ui64NumKeys++; // += ui64KeysProcessed; + } + + // Get the next document... + + if (RC_BAD( rc = m_pXRefRS->getNext( NULL, NULL, pBTree, + (FLMBYTE *)&XRef, sizeof( XRef), &uiSizeRV, NULL, 0, NULL))) + { + if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + + if( RC_BAD( rc = chkCallProgFunc())) + { + goto Exit; + } + } + + m_pXRefRS->freeBTree( &pBTree); + + if( bUpdTranStarted) + { + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + + bUpdTranStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + } + + *pui64TotalKeys = pKeyColl->getTotalKeys(); + +Exit: + + if( pBTree) + { + m_pXRefRS->freeBTree( &pBTree); + } + + if( bUpdTranStarted) + { + // If we fail here, the whole thing should abort. + + if( RC_OK( rc)) + { + if( RC_OK ( rc = m_pDb->transCommit())) + { + rc = m_pDb->transBegin( XFLM_READ_TRANS); + } + else + { + m_pDb->transAbort(); + } + } + else + { + m_pDb->transAbort(); + if( RC_BAD( tmpRc = m_pDb->transBegin( XFLM_READ_TRANS))) + { + rc = tmpRc; + } + } + } + + if( pDocNode) + { + pDocNode->Release(); + } + + // Be sure we don't leave it this way. + + m_pDb->setKeyCollector( NULL); + + if( pKeyColl) + { + pKeyColl->Release(); + } + + m_Progress.bStartFlag = TRUE; + return( rc); +} + +/*************************************************************************** +Desc: This routine checks all of the B-TREES in the database -- all + indexes and containers. +*****************************************************************************/ +RCODE F_DbCheck::verifyBTrees( + FLMBOOL * pbStartOverRV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurrLf; + FLMUINT uiCurrLevel; + FLMBYTE * pucKeyBuffer = NULL; + FLMUINT uiKeysAllocated = 0; + STATE_INFO State [BH_MAX_LEVELS]; + FLMBOOL bStateInitialized [BH_MAX_LEVELS]; + FLMBYTE * pucResetKey = NULL; + FLMUINT uiResetKeyLen = ~(FLMUINT)0; + FLMUINT64 ui64ResetNodeId = 0; + LF_HDR * pLogicalFile; + FLMUINT uiSaveDictSeq; + FLMBOOL bRSFinalized = FALSE; + char szTmpIoPath [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + F_NodeVerifier * pNodeVerifier = NULL; + F_DbSystem dbSystem; + F_BLK_HDR * pBlkHdr = NULL; + F_CachedBlock * pSCache = NULL; + + // The StateInfo structs may have pointer to this object, + // but we only need one instance, so do the new here, rather + // than inside the loop where we initialize the StateInfo structs. + + if( (pNodeVerifier = f_new F_NodeVerifier) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // szTmpIoPath is the directory where the result sets containing + // node data will be stored. + + if (RC_BAD( rc = dbSystem.getTempDir( szTmpIoPath))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || + rc == NE_XFLM_IO_INVALID_FILENAME) + { + if (RC_BAD( rc = gv_pFileSystem->pathReduce( m_pDb->m_pDatabase->m_pszDbPath, + szTmpIoPath, + szBaseName))) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + + for (uiCurrLevel = 0; uiCurrLevel < BH_MAX_LEVELS; uiCurrLevel++) + { + bStateInitialized [uiCurrLevel] = FALSE; + } + + if (*pbStartOverRV) + { + goto Exit; + } + + uiSaveDictSeq = m_pDb->m_pDict->getDictSeq(); + if (RC_BAD( rc = setupLfTable())) + { + goto Exit; + } + + if (m_uiFlags & XFLM_DO_LOGICAL_CHECK) + { + if (RC_BAD( rc = setupIxInfo())) + { + goto Exit; + } + } + + // Loop through all of the logical files in the database + // and perform a structural and logical check. + + m_pIxd = NULL; + uiCurrLf = 0; + while (uiCurrLf < m_pDbInfo->m_uiNumLogicalFiles) + { + m_Progress.uiCurrLF = uiCurrLf + 1; + pLogicalFile = &m_pDbInfo->m_pLogicalFiles[uiCurrLf]; + + if (pLogicalFile->eLfType == XFLM_LF_COLLECTION) + { + if (RC_BAD( rc = m_pDb->m_pDict->getCollection( pLogicalFile->uiLfNum, + &m_pCollection, TRUE))) + { + + goto Exit; + } + m_pLFile = &m_pCollection->lfInfo; + m_pIxd = NULL; + } + else + { + + // If this is our first index, and we are doing a logical check, + // create the index key result set from all of the documents we + // have created. + + if (!m_bPhysicalCorrupt && + (m_uiFlags & XFLM_DO_LOGICAL_CHECK) && + !bRSFinalized) + { + FLMUINT64 ui64NumRSKeys = 0; + + if (RC_BAD( rc = buildIndexKeyList( &ui64NumRSKeys))) + { + if (rc == NE_XFLM_EOF_HIT && ui64NumRSKeys == 0) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // Reset uiNumKeys to reflect the number of keys + // in the result set now that all duplicates have + // been eliminated. + + if (m_Progress.ui64NumKeys > ui64NumRSKeys) + { + m_Progress.ui64NumDuplicateKeys = + m_Progress.ui64NumKeys - ui64NumRSKeys; + } + m_Progress.ui64NumKeys = ui64NumRSKeys; + + // Set bRSFinalized to TRUE so that subsequent passes will not + // attempt to finalize the result set again. + + bRSFinalized = TRUE; + } + + if (RC_BAD( rc = m_pDb->m_pDict->getIndex( pLogicalFile->uiLfNum, + &m_pLFile, &m_pIxd, TRUE))) + { + goto Exit; + } + m_pCollection = NULL; + } + pLogicalFile->uiRootBlk = m_pLFile->uiRootBlk; + flmAssert( m_pLFile->uiRootBlk); + + // Allocate space to hold the keys, if not already allocated. + + if (uiKeysAllocated < pLogicalFile->uiNumLevels) + { + if (RC_BAD( rc = f_realloc( pLogicalFile->uiNumLevels * MAX_KEY_SIZ, + &pucKeyBuffer))) + { + goto Exit; + } + uiKeysAllocated = pLogicalFile->uiNumLevels; + } + + // Setup XFLM_PROGRESS_CHECK_INFO structure + + m_Progress.iCheckPhase = XFLM_CHECK_B_TREE; + m_Progress.bStartFlag = TRUE; + m_Progress.uiLfNumber = m_pLFile->uiLfNum; + m_Progress.uiLfType = m_pLFile->eLfType; + + if (RC_BAD( rc = chkCallProgFunc())) + { + break; + } + + m_Progress.bStartFlag = FALSE; + + f_yieldCPU(); + + // Initialize the state information for each level of the B-TREE. + + for (uiCurrLevel = 0; + uiCurrLevel < pLogicalFile->uiNumLevels; + uiCurrLevel++) + { + FLMUINT uiExpectedBlkType; + + // If we are resetting to a particular key, save the statistics + // which were gathered so far. + + if (uiResetKeyLen != ~(FLMUINT)0) + { + + // Save the statistics which were gathered. + + pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = + State [uiCurrLevel].ui64KeyCount; + + f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, + &State [uiCurrLevel].BlkInfo, + sizeof( BLOCK_INFO)); + } + + if (m_pLFile->eLfType == XFLM_LF_INDEX) + { + if (uiCurrLevel == 0) + { + if (m_pIxd->uiNumDataComponents) + { + uiExpectedBlkType = BT_LEAF_DATA; + } + else + { + uiExpectedBlkType = BT_LEAF; + } + } + else if (m_pIxd->uiFlags & IXD_ABS_POS) + { + uiExpectedBlkType = BT_NON_LEAF_COUNTS; + } + else + { + uiExpectedBlkType = BT_NON_LEAF; + } + } + else // collection BTree... + { + if (uiCurrLevel == 0) + { + uiExpectedBlkType = BT_LEAF_DATA; + } + else + { + uiExpectedBlkType = BT_NON_LEAF; + } + + } + + flmInitReadState( &State [uiCurrLevel], + &bStateInitialized [uiCurrLevel], + (FLMUINT)m_pDb-> + m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion, + m_pDb, + pLogicalFile, + uiCurrLevel, + uiExpectedBlkType, + &pucKeyBuffer [uiCurrLevel * MAX_KEY_SIZ]); + + State[ uiCurrLevel].pCollection = m_pCollection; + State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1; + State[ uiCurrLevel].uiCurrLf = uiCurrLf; + + if (uiResetKeyLen == ~(FLMUINT)0) + { + State [uiCurrLevel].ui32LastChildAddr = 0; + State [uiCurrLevel].uiElmLastFlag = TRUE; + } + else + { + + // Restore the statistics which were gathered so far. + + State [uiCurrLevel].ui64KeyCount = + pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount; + f_memcpy( &State [uiCurrLevel].BlkInfo, + &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, + sizeof( BLOCK_INFO)); + } + } + + + if (m_pLFile->eLfType == XFLM_LF_COLLECTION) + { + + // Only leaf blocks of collections need a NodeVerifier object + + State[0].pNodeVerifier = pNodeVerifier; + + // If this is a collection BTree, create a result set to hold the pointer + // information from all the nodes in this btree + + if (RC_BAD( rc = getBtResultSet( &State[ 0].pNodeRS))) + { + goto Exit; + } + + // The nodeVerifier will setup the Node Result Set etc.. + + if (pNodeVerifier) + { + pNodeVerifier->setupNodeRS( State[ 0].pNodeRS); + } + } + + if ((m_uiFlags & XFLM_DO_LOGICAL_CHECK) && + State[ 0].pXRefRS == NULL) + { + if (m_pXRefRS == NULL) + { + if (RC_BAD( rc = getBtResultSet( &m_pXRefRS))) + { + goto Exit; + } + + } + + State[ 0].pXRefRS = m_pXRefRS; + + // The nodeVerifier will setup the Node Result Set etc.. + + if (pNodeVerifier) + { + pNodeVerifier->setupXRefRS( State[ 0].pXRefRS); + } + } + + // Call verifySubTree to check the B-TREE starting at the + // root block. +Reset: + + rc = verifySubTree( NULL, + &State [pLogicalFile->uiNumLevels - 1], + m_pLFile->uiRootBlk, + &pucResetKey, + uiResetKeyLen, + ui64ResetNodeId); + + if (rc == NE_XFLM_RESET_NEEDED || rc == NE_XFLM_OLD_VIEW) + { + FLMUINT uiNumLevels; + + if (rc == NE_XFLM_RESET_NEEDED) + { + m_LastStatusRc = NE_XFLM_OK; + } + + // If it is a read transaction, reset. + + if (m_pDb->getTransType() == XFLM_READ_TRANS) + { + + // Free the KrefCntrl + + m_pDb->krefCntrlFree(); + + // Abort the read transaction + + if (RC_BAD( rc = m_pDb->transAbort())) + { + goto Exit; + } + + // Try to start a new read transaction + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS, + XFLM_NO_TIMEOUT, + XFLM_DONT_POISON_CACHE))) + { + goto Exit; + } + } + + // If we already have a reset key buffer, we need to free it. + // We will start by repositioning to the root level key we were + // last on. + + if (pucResetKey) + { + f_free( &pucResetKey); + } + + uiResetKeyLen = State[ pLogicalFile->uiNumLevels - 1].uiElmKeyLen; + ui64ResetNodeId = State[ pLogicalFile->uiNumLevels - 1].ui64ElmNodeId; + + if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1, &pucResetKey))) + { + goto Exit; + } + + f_memcpy( pucResetKey, + State[ pLogicalFile->uiNumLevels - 1].pucElmKey, + uiResetKeyLen); + + // On Reset, we may need to reget the LFILE and IXD. + + uiNumLevels = pLogicalFile->uiNumLevels; + + if (pLogicalFile->eLfType == XFLM_LF_COLLECTION) + { + if (RC_BAD( rc = m_pDb->m_pDict->getCollection( + pLogicalFile->uiLfNum, &m_pCollection, TRUE))) + { + goto Exit; + } + m_pIxd = NULL; + m_pLFile = &m_pCollection->lfInfo; + } + else + { + if (RC_BAD( rc = m_pDb->m_pDict->getIndex( + pLogicalFile->uiLfNum, + &m_pLFile, &m_pIxd, TRUE))) + { + goto Exit; + } + m_pCollection = NULL; + } + if (RC_BAD( rc = getLfInfo( pLogicalFile, m_pLFile))) + { + goto Exit; + } + + if (uiNumLevels != pLogicalFile->uiNumLevels) + { + // Since the block structure of the BTree has been changed, we have + // to begin our scan again from the top. We need to gather stats too. + uiResetKeyLen = ~(FLMUINT)0; + if (pucResetKey) + { + f_free( &pucResetKey); + } + ui64ResetNodeId = 0; + + // Initialize the state information for each level of the B-TREE. + for (uiCurrLevel = 0; + uiCurrLevel < pLogicalFile->uiNumLevels; + uiCurrLevel++) + { + FLMUINT uiExpectedBlkType; + + // If we are resetting to a particular key, save the statistics + // which were gathered so far. + + if (uiResetKeyLen != ~(FLMUINT)0) + { + + // Save the statistics which were gathered. + + pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = + State [uiCurrLevel].ui64KeyCount; + + f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, + &State [uiCurrLevel].BlkInfo, + sizeof( BLOCK_INFO)); + } + + if (m_pLFile->eLfType == XFLM_LF_INDEX) + { + if (uiCurrLevel == 0) + { + if (m_pIxd->uiNumDataComponents) + { + uiExpectedBlkType = BT_LEAF_DATA; + } + else + { + uiExpectedBlkType = BT_LEAF; + } + } + else if (m_pIxd->uiFlags & IXD_ABS_POS) + { + uiExpectedBlkType = BT_NON_LEAF_COUNTS; + } + else + { + uiExpectedBlkType = BT_NON_LEAF; + } + } + else // collection BTree... + { + if (uiCurrLevel == 0) + { + uiExpectedBlkType = BT_LEAF_DATA; + } + else + { + uiExpectedBlkType = BT_NON_LEAF; + } + + } + + flmInitReadState( &State [uiCurrLevel], + &bStateInitialized [uiCurrLevel], + (FLMUINT)m_pDb-> + m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion, + m_pDb, + pLogicalFile, + uiCurrLevel, + uiExpectedBlkType, + &pucKeyBuffer [uiCurrLevel * MAX_KEY_SIZ]); + + State[ uiCurrLevel].pCollection = m_pCollection; + State[ uiCurrLevel].uiRootLevel = pLogicalFile->uiNumLevels - 1; + State[ uiCurrLevel].uiCurrLf = uiCurrLf; + + if (uiResetKeyLen == ~(FLMUINT)0) + { + State [uiCurrLevel].ui32LastChildAddr = 0; + State [uiCurrLevel].uiElmLastFlag = TRUE; + } + else + { + + // Restore the statistics which were gathered so far. + + State [uiCurrLevel].ui64KeyCount = + pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount; + f_memcpy( &State [uiCurrLevel].BlkInfo, + &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, + sizeof( BLOCK_INFO)); + } + } + } + + goto Reset; + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + // If this was a collection, then go through the result set and verify + // all of the node pointers... + + if (State[ 0].pNodeRS) + { + FLMINT iErrCode; + + + // Setup the current progress phase + + m_Progress.iCheckPhase = XFLM_CHECK_DOM_LINKS; + m_Progress.bStartFlag = TRUE; + m_Progress.uiLfNumber = m_pLFile->uiLfNum; + m_Progress.uiLfType = m_pLFile->eLfType; + + if (RC_BAD( rc = chkCallProgFunc())) + { + break; + } + + m_Progress.bStartFlag = FALSE; + + f_yieldCPU(); + + m_LastStatusRc = verifyNodePointers( &State[ 0], &iErrCode); + + if (iErrCode) + { + chkReportError( iErrCode, + XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, + m_Progress.uiLfType, + State[ 0].uiLevel, + m_pLFile->uiBlkAddress, + 0, + 0, + 0); + } + + State[ 0].pNodeRS->Release(); + State[ 0].pNodeRS = NULL; + if (pNodeVerifier) + { + + // Resets the Node verifier RS to NULL. + + pNodeVerifier->setupNodeRS( State[ 0].pNodeRS); + } + } + + // Verify that all of the levels' next block address's are 0. + + if (RC_OK( m_LastStatusRc)) + { + for (uiCurrLevel = 0; + uiCurrLevel < pLogicalFile->uiNumLevels; + uiCurrLevel++) + { + + // Save the statistics which were gathered. + + pLogicalFile->pLevelInfo [uiCurrLevel].ui64KeyCount = + State [uiCurrLevel].ui64KeyCount; + f_memcpy( &pLogicalFile->pLevelInfo [uiCurrLevel].BlockInfo, + &State [uiCurrLevel].BlkInfo, sizeof( BLOCK_INFO)); + + // Make sure the last block had a NEXT block address of 0. + + if (State [uiCurrLevel].ui32NextBlkAddr != 0xFFFFFFFF && + State [uiCurrLevel].ui32NextBlkAddr != 0) + { + FLMINT iBlkErrCode; + + // Verify our finding. Get the block in question and see + // if it realy has a problem. + + if (RC_BAD( rc = blkRead( State[ uiCurrLevel].ui32BlkAddress, + &pBlkHdr, &pSCache, &iBlkErrCode))) + { + // Log the error. + + if (iBlkErrCode) + { + chkReportError( iBlkErrCode, XFLM_LOCALE_LFH_LIST, + 0, 0, 0xFF, State[ uiCurrLevel].ui32BlkAddress, + 0, 0, 0); + } + goto Exit; + } + + if (pBlkHdr->ui32NextBlkInChain != 0) + { + chkReportError( FLM_BAD_LAST_BLK_NEXT, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiCurrLevel, + 0, 0, 0, 0); + } + + ScaReleaseCache( pSCache, FALSE); + pSCache = NULL; + pBlkHdr = NULL; + + } + } + } + + if (RC_BAD( m_LastStatusRc)) + { + break; + } + + // If we are doing a logical index check, need to see if we used up + // all of the keys in the result set for this index. + + if (pLogicalFile->eLfType == XFLM_LF_INDEX && + !m_bPhysicalCorrupt && + (m_uiFlags & XFLM_DO_LOGICAL_CHECK) && + bRSFinalized) + { + for (;;) + { + if (RC_BAD( rc = chkGetNextRSKey())) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + else + { + + // Updated statistics + + m_Progress.ui64NumKeysExamined++; + + if (RC_BAD( rc = resolveIXMissingKey( &(State[ 0])))) + { + goto Exit; + } + } + } + } + + uiCurrLf++; + if (pucResetKey) + { + f_free( &pucResetKey); + } + pucResetKey = NULL; + uiResetKeyLen = ~(FLMUINT)0; + ui64ResetNodeId = 0; + } + +Exit: + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if (pBlkHdr) + { + f_free( &pBlkHdr); + } + + if (pucKeyBuffer) + { + f_free( &pucKeyBuffer); + } + + if (pucResetKey) + { + f_free( &pucResetKey); + } + + if (State[ 0].pNodeRS) + { + State[ 0].pNodeRS->Release(); + State[ 0].pNodeRS = NULL; + if (pNodeVerifier) + { + pNodeVerifier->setupNodeRS( NULL); + } + } + + // Clean up the NodeVerifier instance... + + if (pNodeVerifier) + { + pNodeVerifier->Release(); + } + + // Cleanup any temporary index check files + + if (m_pIxRSet) + { + m_pIxRSet->Release(); + m_pIxRSet = NULL; + } + if (m_puiIxArray) + { + f_free( &m_puiIxArray); + } + + if (RC_OK( rc) && RC_BAD( m_LastStatusRc)) + { + rc = m_LastStatusRc; + } + + return( rc); +} + + + +/*************************************************************************** +Desc: This routine allocates and initializes the LF table (array of + LF_HDR structures). +*****************************************************************************/ +RCODE F_DbCheck::setupLfTable() +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLfHdrOffset; + IXD * pIxd; + F_COLLECTION * pCollection; + F_Dict * pDict = m_pDb->m_pDict; + FLMUINT uiIndexNum; + FLMUINT uiCollectionNum; + + // Set up the table such that the dictionary is checked first, + // followed by data containers, and then indexes. This is + // necessary for the logical (index) check to work. The + // data records must be extracted before the indexes are + // checked so that the temporary result set, used during + // the logical check, can be built. + + m_pDbInfo->freeLogicalFiles(); + m_Progress.uiNumLFs = 0; + + if (pDict) + { + + // Count the indexes. + + uiIndexNum = 0; + for (;;) + { + if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + m_pDbInfo->m_uiNumIndexes++; + } + + // Count the collections + + uiCollectionNum = 0; + for (;;) + { + if ((pCollection = pDict->getNextCollection( uiCollectionNum, + TRUE)) == NULL) + { + break; + } + uiCollectionNum = pCollection->lfInfo.uiLfNum; + m_pDbInfo->m_uiNumCollections++; + } + + m_pDbInfo->m_uiNumLogicalFiles = m_pDbInfo->m_uiNumIndexes + + m_pDbInfo->m_uiNumCollections; + m_Progress.uiNumLFs = m_pDbInfo->m_uiNumLogicalFiles; + + // Allocate memory for each collection and index, then set up each + // collection and index + + if (RC_BAD( rc = f_calloc( (FLMUINT)(sizeof( LF_HDR) * + m_pDbInfo->m_uiNumLogicalFiles), + (void **)&m_pDbInfo->m_pLogicalFiles))) + { + goto Exit; + } + + uiLfHdrOffset = 0; + + // Add in our dictionary collection first. Do field + // definitions first, then collection definitions, then index + // definitions. + + if (RC_BAD( rc = pDict->getCollection( XFLM_DICT_COLLECTION, &pCollection))) + { + goto Exit; + } + if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], + &pCollection->lfInfo))) + { + goto Exit; + } + uiLfHdrOffset++; + + // Add in default data collection + + if (RC_BAD( rc = pDict->getCollection( XFLM_DATA_COLLECTION, &pCollection))) + { + goto Exit; + } + if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], + &pCollection->lfInfo))) + { + goto Exit; + } + uiLfHdrOffset++; + + // Add in the maintenance collection + + if (RC_BAD( rc = pDict->getCollection( XFLM_MAINT_COLLECTION, &pCollection))) + { + goto Exit; + } + if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], + &pCollection->lfInfo))) + { + goto Exit; + } + uiLfHdrOffset++; + + // Add in user defined collections + + uiCollectionNum = 0; + for (;;) + { + if ((pCollection = pDict->getNextCollection( uiCollectionNum, + FALSE)) == NULL) + { + break; + } + uiCollectionNum = pCollection->lfInfo.uiLfNum; + if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], + &pCollection->lfInfo))) + { + goto Exit; + } + uiLfHdrOffset++; + } + + // Indexes need to be in order from lowest to highest + // because the result set is ordered that way. + + uiIndexNum = 0; + for (;;) + { + if ((pIxd = pDict->getNextIndex( uiIndexNum, TRUE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + if (RC_BAD( rc = getLfInfo( &m_pDbInfo->m_pLogicalFiles [uiLfHdrOffset], + &pIxd->lfInfo))) + { + goto Exit; + } + uiLfHdrOffset++; + } + } + +Exit: + + return( rc); +} + + +/*************************************************************************** +Desc: Initializes index checking information. +*****************************************************************************/ +RCODE F_DbCheck::setupIxInfo( void) +{ + RCODE rc = NE_XFLM_OK; + LF_HDR * pLogicalFile; + FLMUINT uiLoop; + FLMUINT uiIxCount; + + // Initialize the result set. The result set will be used + // to build an ordered list of keys for comparison to + // the database's indexes. + + if (RC_BAD( rc = getBtResultSet( &m_pIxRSet))) + { + goto Exit; + } + + // Build list of all indexes + + if (m_pDbInfo->m_uiNumIndexes) + { + + // Allocate memory to save each index number + + if (RC_BAD( rc = f_alloc( + (FLMUINT)(sizeof( FLMUINT) * m_pDbInfo->m_uiNumIndexes), + &m_puiIxArray))) + { + goto Exit; + } + + // Save the index numbers into the array. + + uiIxCount = 0; + pLogicalFile = m_pDbInfo->m_pLogicalFiles; + for( uiLoop = 0; + uiLoop < m_pDbInfo->m_uiNumLogicalFiles; + uiLoop++, pLogicalFile++) + { + if (pLogicalFile->eLfType == XFLM_LF_INDEX) + { + m_puiIxArray[ uiIxCount] = pLogicalFile->uiLfNum; + uiIxCount++; + } + } + } + + m_bGetNextRSKey = TRUE; + +Exit: + + // Clean up any memory on error exit. + + if (RC_BAD( rc)) + { + if (m_pIxRSet) + { + m_pIxRSet->Release(); + m_pIxRSet = NULL; + } + if (m_puiIxArray) + { + f_free( &m_puiIxArray); + } + } + + return( rc); +} + +/*************************************************************************** +Desc: This routine reads the LFH areas from disk to make sure they are up + to date in memory. +*****************************************************************************/ +RCODE F_DbCheck::getLfInfo( + LF_HDR * pLogicalFile, + LFILE * pLFile + ) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = NULL; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT uiSaveLevel; + FLMINT iBlkErrCode; + + pLogicalFile->eLfType = pLFile->eLfType; + pLogicalFile->uiLfNum = pLFile->uiLfNum; + pLogicalFile->uiRootBlk = pLFile->uiRootBlk; + + // Read in the block containing the logical file header. + + if (RC_BAD( rc = blkRead( pLFile->uiBlkAddress, + &pBlkHdr, &pSCache, &iBlkErrCode))) + { + + // Log the error. + + if (iBlkErrCode) + { + chkReportError( iBlkErrCode, + XFLM_LOCALE_LFH_LIST, + 0, + 0, + 0xFF, + pLFile->uiBlkAddress, + 0, + 0, + 0); + } + goto Exit; + } + uiSaveLevel = pLogicalFile->uiNumLevels; + + // Read root block to get the number of levels in the B-TREE + + flmAssert( pLFile->uiRootBlk); + if (RC_BAD( rc = blkRead( pLFile->uiRootBlk, + &pBlkHdr, + &pSCache, + &iBlkErrCode))) + { + if (iBlkErrCode) + { + chkReportError( iBlkErrCode, + XFLM_LOCALE_B_TREE, + pLFile->uiLfNum, + pLFile->eLfType, + 0xFF, + pLFile->uiRootBlk, + 0, + 0, + 0); + } + goto Exit; + } + pLogicalFile->uiNumLevels = + (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel) + 1; + + // Need to make sure that the level extracted from + // the block is valid. + + if (((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel >= BH_MAX_LEVELS) + { + chkReportError( FLM_BAD_BLK_HDR_LEVEL, + XFLM_LOCALE_B_TREE, + pLFile->uiLfNum, + pLFile->eLfType, + (FLMUINT)(((F_BTREE_BLK_HDR *)pBlkHdr)->ui8BlkLevel), + pLFile->uiRootBlk, + 0, + 0, + 0); + + // Force pLogicalFile->uiNumLevels to 1 so that we don't crash + + pLogicalFile->uiNumLevels = 1; + } + + // If the number of levels changed, reset the level information + // on this logical file. + + if (uiSaveLevel != pLogicalFile->uiNumLevels && + pLogicalFile->uiNumLevels) + { + if (pLogicalFile->uiNumLevels > uiSaveLevel) + { + if ( pLogicalFile->pLevelInfo) + { + f_free( &pLogicalFile->pLevelInfo); + } + if (RC_BAD( rc = f_calloc( + (sizeof( LEVEL_INFO) * pLogicalFile->uiNumLevels), + (void **)&pLogicalFile->pLevelInfo))) + { + goto Exit; + } + } + } + +Exit: + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if (pBlkHdr) + { + f_free( &pBlkHdr); + } + + return( rc); +} + + +/*************************************************************************** +Desc: Goes throught the (finalized) result set and validates that all the + node pointers point to the right nodes +*****************************************************************************/ +RCODE F_DbCheck::verifyNodePointers( + STATE_INFO * pStateInfo, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + NODE_RS_ENTRY * pRSEntry = NULL; + NODE_RS_ENTRY * pTmpRSEntry = NULL; + F_BtResultSet * pResult = pStateInfo->pNodeRS; + FLMUINT uiRSEntrySize = sizeof( NODE_RS_ENTRY); + FLMBOOL bFirst = TRUE; + FLMBYTE pucKey[ MAX_KEY_SIZ]; + FLMUINT uiKeyLength = MAX_KEY_SIZ; + F_Btree * pBTree = NULL; + FLMINT iErrCode = 0; + + *piErrCode = 0; + + if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry))) + { + goto Exit; + } + + if (RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pTmpRSEntry))) + { + goto Exit; + } + + for (;;) + { + m_Progress.ui64NumDomNodes++;; + + if (bFirst) + { + + if (RC_BAD( rc = pResult->getBTree( NULL, NULL, &pBTree))) + { + goto Exit; + } + + if (RC_BAD( rc = pResult->getFirst( NULL, NULL, pBTree, + pucKey, + MAX_KEY_SIZ, + &uiKeyLength, + (FLMBYTE *)pRSEntry, + sizeof( NODE_RS_ENTRY), + &uiRSEntrySize))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + } + else + { + if (RC_BAD( rc = pResult->getNext( NULL, NULL, pBTree, + pucKey, + MAX_KEY_SIZ, + &uiKeyLength, + (FLMBYTE *)pRSEntry, + sizeof( NODE_RS_ENTRY), + &uiRSEntrySize))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + + goto Exit; + } + } + bFirst = FALSE; + + if (RC_BAD( rc = verifyRootLink( + pRSEntry, uiRSEntrySize, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = verifyParentLink( + pRSEntry, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if( RC_BAD( rc = verifyFirstChildLink( pRSEntry, pTmpRSEntry, + pResult, &iErrCode))) + { + goto Exit; + } + + if( iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = verifyLastChildLink( + pRSEntry, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = verifyPrevSiblingLink( + pRSEntry, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = verifyNextSiblingLink( + pRSEntry, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = verifyAnnotationLink( + pRSEntry, pTmpRSEntry, pResult, &iErrCode))) + { + goto Exit; + } + + if (iErrCode) + { + chkReportError( iErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, 0, 0, 0, (FLMUINT)~(0), + pRSEntry->hdr.ui64NodeId); + iErrCode = 0; + m_Progress.ui64NumBrokenDomLinks++; + } + else + { + m_Progress.ui64NumDomLinksVerified++; + } + + if (RC_BAD( rc = chkCallProgFunc())) + { + break; + } + + f_yieldCPU(); + + } + +Exit: + + if (pBTree) + { + pResult->freeBTree( &pBTree); + } + + if (pRSEntry) + { + f_free( &pRSEntry); + } + + if (pTmpRSEntry) + { + f_free( &pTmpRSEntry); + } + + return rc; + +} + +/******************************************************************** +Desc: Verify that the Root Id field points to a valid node. +********************************************************************/ +FSTATIC RCODE verifyRootLink( + NODE_RS_ENTRY * pRSEntry, + FLMUINT uiRSEntrySize, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64RootId) + { + // Returns NE_XFLM_OK + goto Exit; + } + + if( ui64RootId != pRSEntry->hdr.ui64NodeId) + { + pTmpRSEntry->hdr.ui64NodeId = ui64RootId; + + if (RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_ROOT_LINK; + goto Exit; + } + + // Found it! + // Make sure this Root is a "True" root node. + + // Cannot have a parent node + if (pTmpRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID) + { + *piErrCode = FLM_BAD_ROOT_PARENT; + goto Exit; + } + + // Cannot be another node child, or attribute or annotation node. + if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || + pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || + pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) + { + *piErrCode = FLM_BAD_ROOT_LINK; + goto Exit; + } + + pTmpRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) + { + goto Exit; + } + } + else + { + // If the node we were passed IS the root node... + // Cannot have a parent node + if (pRSEntry->hdr.ui16BitMap & CHK_BM_PARENT_ID) + { + *piErrCode = FLM_BAD_ROOT_LINK; + goto Exit; + } + + // Cannot be another node child, or attribute or annotation node. + if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || + pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || + pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) + { + *piErrCode = FLM_BAD_ROOT_LINK; + goto Exit; + } + + pRSEntry->hdr.ui16Flags |= CHK_ROOT_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + (FLMBYTE *)pRSEntry, uiRSEntrySize))) + { + goto Exit; + } + + } + +Exit: + + return rc; +} + +/******************************************************************** +Desc: Verify that the parent Id field points to a valid node. +********************************************************************/ +FSTATIC RCODE verifyParentLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT64 ui64TmpRootId; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64ParentId) + { + // With no parent node, this node cannot have any child + // or attribute or annotation nodes. + if (pRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED || + pRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED || + pRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) + { + *piErrCode = FLM_BAD_PARENT_LINK; + } + + goto Exit; + } + + if (ui64ParentId == pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + + pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; + + if (RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + + // Verify that they are in the same document... + ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); + if (ui64TmpRootId) + { + if (ui64RootId != ui64TmpRootId) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + } + else + { + if (ui64ParentId != ui64RootId) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + } + + pTmpRSEntry->hdr.ui16Flags |= CHK_PARENT_VERIFIED; + + if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Verify that the First Child link points to a vaild node. +********************************************************************/ +FSTATIC RCODE verifyFirstChildLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64FirstChildId = getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT64 ui64TmpRootId; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64FirstChildId) + { + // Better not have a last child either. + + if (getLinkVal( CHK_BM_LAST_CHILD, pRSEntry)) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + } + + if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED) + { + if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry)) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + } + } + goto Exit; + + } + + if (ui64FirstChildId == pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + + pTmpRSEntry->hdr.ui64NodeId = ui64FirstChildId; + + if (RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + + // Must belong to the same document / root + + ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); + if (ui64RootId) + { + if (ui64RootId != ui64TmpRootId) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + } + else + { + if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + } + + // Make sure this child has not been visited as a first child already. + if (pTmpRSEntry->hdr.ui16Flags & CHK_FIRST_CHILD_VERIFIED) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + + // Does this child reference the correct parent? + if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + goto Exit; + } + + // Mark the child as visited as a first child. + pTmpRSEntry->hdr.ui16Flags |= CHK_FIRST_CHILD_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, + uiTmpRSEntrySize))) + { + goto Exit; + } + +Exit: + + return rc; +} + +/******************************************************************** +Desc: Verify that the Last Child link points to a vaild node. +********************************************************************/ +FSTATIC RCODE verifyLastChildLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64LastChildId = getLinkVal( CHK_BM_LAST_CHILD, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT64 ui64TmpRootId; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64LastChildId) + { + // Better not have a first child either. + + if (getLinkVal( CHK_BM_FIRST_CHILD, pRSEntry)) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + } + + if (pRSEntry->hdr.ui16Flags & CHK_PARENT_VERIFIED) + { + if( !getLinkVal( CHK_BM_ANNOTATION, pRSEntry)) + { + *piErrCode = FLM_BAD_FIRST_CHILD_LINK; + } + } + goto Exit; + + } + + if (ui64LastChildId == pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + + pTmpRSEntry->hdr.ui64NodeId = ui64LastChildId; + + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + + // Must belong to the same document / root + ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); + if (ui64RootId) + { + if (ui64RootId != ui64TmpRootId) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + } + else + { + if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + } + + // Make sure this child has not been visited as a last child already. + if (pTmpRSEntry->hdr.ui16Flags & CHK_LAST_CHILD_VERIFIED) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + + // Does this child reference the correct parent? + if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_LAST_CHILD_LINK; + goto Exit; + } + + // Mark the child as visited as a last child. + pTmpRSEntry->hdr.ui16Flags |= CHK_LAST_CHILD_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, + uiTmpRSEntrySize))) + { + goto Exit; + } + + +Exit: + + return rc; +} + +/******************************************************************** +Desc: Verify that the Prev Sibling link points to a valid node. +********************************************************************/ +FSTATIC RCODE verifyPrevSiblingLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64PrevSibling = getLinkVal( CHK_BM_PREV_SIBLING, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64PrevSibling) + { + // Should not be a Next Sibling to anyone. + if (pRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + } + + // Must also verify that this node is the first child of the parent node + // - if there is a parent. + if (ui64ParentId) + { + FLMUINT64 ui64FirstChild; + + pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + ui64FirstChild = getLinkVal( CHK_BM_FIRST_CHILD, pTmpRSEntry); + if (ui64FirstChild != pRSEntry->hdr.ui64NodeId) + { + FLMUINT64 ui64Annot; + + // It may be an annotation Node. + + ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry); + if (ui64Annot != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + goto Exit; + } + } + } + goto Exit; + } + + pTmpRSEntry->hdr.ui64NodeId = ui64PrevSibling; + + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + goto Exit; + } + + // Must belong to the same document unless both nodes are + // document roots + + if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry)) + { + if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry)) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + goto Exit; + } + } + + // The previous sibling should not have been visited as a previous + // sibling before now. + + if (pTmpRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + goto Exit; + } + + // Should point to "this" node. + if (getLinkVal( CHK_BM_NEXT_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_PREV_SIBLING_LINK; + goto Exit; + } + + // Mark the previous sibling as being visited as such. + pTmpRSEntry->hdr.ui16Flags |= CHK_PREV_SIBLING_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, + uiTmpRSEntrySize))) + { + goto Exit; + } + + +Exit: + + return rc; +} + +/******************************************************************** +Desc: Verify that the Next Sibling link points to a valid node. +********************************************************************/ +FSTATIC RCODE verifyNextSiblingLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NextSibling = getLinkVal( CHK_BM_NEXT_SIBLING, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT64 ui64ParentId = getLinkVal( CHK_BM_PARENT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64NextSibling) + { + // Should not be a Prev Sibling to anyone. + if (pRSEntry->hdr.ui16Flags & CHK_PREV_SIBLING_VERIFIED) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + } + // Must also verify that this node is the last child of the parent node + // - if there is a parent. + if (ui64ParentId) + { + FLMUINT64 ui64LastChild; + + pTmpRSEntry->hdr.ui64NodeId = ui64ParentId; + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_PARENT_LINK; + goto Exit; + } + ui64LastChild = getLinkVal( CHK_BM_LAST_CHILD, pTmpRSEntry); + if (ui64LastChild != pRSEntry->hdr.ui64NodeId) + { + FLMUINT64 ui64Annot; + + // It may be an annotation Node. + + ui64Annot = getLinkVal( CHK_BM_ANNOTATION, pTmpRSEntry); + if (ui64Annot != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + goto Exit; + } + } + + } + goto Exit; + } + + + pTmpRSEntry->hdr.ui64NodeId = ui64NextSibling; + + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + goto Exit; + } + + // Must belong to the same document unless both nodes are + // document roots + + if( ui64RootId != getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry)) + { + if( ui64ParentId || getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry)) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + goto Exit; + } + } + + // The next sibling should not have been visited as a next + // sibling before now. + + if( pTmpRSEntry->hdr.ui16Flags & CHK_NEXT_SIBLING_VERIFIED) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + goto Exit; + } + + // Should point to "this" node. + + if( getLinkVal( CHK_BM_PREV_SIBLING, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_NEXT_SIBLING_LINK; + goto Exit; + } + + // Mark the previous sibling as being visited as such. + + pTmpRSEntry->hdr.ui16Flags |= CHK_NEXT_SIBLING_VERIFIED; + + if( RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), (FLMBYTE *)pTmpRSEntry, uiTmpRSEntrySize))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +********************************************************************/ +FSTATIC RCODE verifyAnnotationLink( + NODE_RS_ENTRY * pRSEntry, + NODE_RS_ENTRY * pTmpRSEntry, + F_BtResultSet * pResult, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Annotation = getLinkVal( CHK_BM_ANNOTATION, pRSEntry); + FLMUINT64 ui64RootId = getLinkVal( CHK_BM_ROOT_ID, pRSEntry); + FLMUINT uiTmpRSEntrySize; + FLMUINT64 ui64TmpRootId; + FLMUINT uiKeySize = sizeof( FLMUINT64); + + f_memset( pTmpRSEntry, 0, sizeof(NODE_RS_ENTRY)); + + if (!ui64Annotation) + { + goto Exit; + } + + pTmpRSEntry->hdr.ui64NodeId = ui64Annotation; + + if( RC_BAD( rc = pResult->findEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), sizeof( FLMUINT64), + &uiKeySize, (FLMBYTE *)pTmpRSEntry, sizeof( NODE_RS_ENTRY), + &uiTmpRSEntrySize))) + { + *piErrCode = FLM_BAD_ANNOTATION_LINK; + goto Exit; + } + + // Must belong to the same document / root + // Annotations may belong to a node that does not have a Root Id. If + // that is the case, the parent and root of the annotaion should match and + // the parent should point to the source node. + + ui64TmpRootId = getLinkVal( CHK_BM_ROOT_ID, pTmpRSEntry); + + if( ui64RootId) + { + if (ui64RootId != ui64TmpRootId) + { + *piErrCode = FLM_BAD_ANNOTATION_LINK; + goto Exit; + } + } + else + { + // With no root, the temporary root must point to "this" node. + + if (ui64TmpRootId != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_ANNOTATION_LINK; + goto Exit; + } + } + + // The annotation should not have been visited as such before now. + if (pTmpRSEntry->hdr.ui16Flags & CHK_ANNOTATION_VERIFIED) + { + *piErrCode = FLM_BAD_ANNOTATION_LINK; + goto Exit; + } + + // Parent should point to "this" node. + if (getLinkVal( CHK_BM_PARENT_ID, pTmpRSEntry) != pRSEntry->hdr.ui64NodeId) + { + *piErrCode = FLM_BAD_ANNOTATION_LINK; + goto Exit; + } + + // Mark the last attr as being visited as such. + pTmpRSEntry->hdr.ui16Flags |= CHK_ANNOTATION_VERIFIED; + + if (RC_BAD( rc = pResult->modifyEntry( NULL, NULL, + (FLMBYTE *)&(pTmpRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), + (FLMBYTE *)pTmpRSEntry, + uiTmpRSEntrySize))) + { + goto Exit; + } + + +Exit: + + return rc; +} + +/*************************************************************************** +Desc: This routine does for chains of data-only blocks what verifySubTree + does for the other blocks. Basically, it's going to read in each + block in the chain, perform some basic verification on the header + and add the data to the NodeVerifier object. +*****************************************************************************/ +RCODE F_DbCheck::verifyDOChain( + STATE_INFO * pParentState, + FLMUINT uiBlkAddr, + FLMINT * piElmErrCode) +{ + RCODE rc = NE_XFLM_OK; + F_NodeVerifier * pNodeVerifier = pParentState->pNodeVerifier; + F_CachedBlock * pSCache = NULL; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT uiParentBlkAddr = pParentState->pBlkHdr->ui32BlkAddr; + FLMUINT uiPrevNextBlkAddr; // The ui32NextBlkInChain field from the previous block + FLMUINT uiNumErrors = 0; + FLMUINT uiNumBlksRead = 0; + FLMUINT uiBlockSize = m_pDb->m_pDatabase->getBlockSize(); + FLMBYTE * pucData = NULL; + FLMUINT uiDataLen = 0; + BLOCK_INFO * pBlkInfo = &pParentState->BlkInfo; + STATE_INFO StateInfo; + + + // All leaf nodes are level 0, and only leaf nodes can point + // to data only blocks... + if (pParentState->uiLevel != 0) + { + *piElmErrCode = FLM_BAD_ELM_INVALID_LEVEL; + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + + //Initialize the StateInfo struct + f_memset( &StateInfo, 0, sizeof( STATE_INFO)); + StateInfo.pCollection = pParentState->pCollection; + StateInfo.pDb = pParentState->pDb; + StateInfo.ui64ElmNodeId = pParentState->ui64ElmNodeId; + + // Initialize the NodeVerifier object + if (pNodeVerifier) + { + pNodeVerifier->Reset( pParentState); + } + + // Now, loop through every block in the chain... + uiPrevNextBlkAddr = 0; + while (uiBlkAddr) + { + // Read in the next block in the chain + f_yieldCPU(); + + if (RC_BAD( rc = blkRead( uiBlkAddr, + &pBlkHdr, + &pSCache, + piElmErrCode))) + { + if (*piElmErrCode) + { + uiNumErrors++; + chkReportError( *piElmErrCode, + XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, + m_Progress.uiLfType, + StateInfo.uiLevel, + uiBlkAddr, + uiParentBlkAddr, + 0, + 0); + } + else if (rc == NE_XFLM_OLD_VIEW) + { + // We're going to have to reset. Unfortunately, + // we don't know how to position ourselves into the middle + // of a record, so we'll have to reset back to the beginning + // of the record. We should still be able to finish processing. + // We only need to see enough of the DOM node to build the header. + // It's the header that gives us the DOM link information. + if (pNodeVerifier) + { + pNodeVerifier->Reset( pParentState); + } + m_Progress.ui64BytesExamined -= + (FLMUINT64)(uiBlockSize*uiNumBlksRead); + chkCallProgFunc(); + } + + goto Exit; + } + + // Chains of data only blocks should always have at least 2 blocks... + if ((uiNumBlksRead == 0) && (pBlkHdr->ui32NextBlkInChain == 0)) + { + *piElmErrCode = FLM_BAD_DATA_BLOCK_COUNT; + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + // Record the progress that we're making + uiNumBlksRead++; + m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; + chkCallProgFunc(); + + // Check the block header. + StateInfo.pBlkHdr = pBlkHdr; + StateInfo.uiBlkType = BT_DATA_ONLY; + StateInfo.ui32BlkAddress = (FLMUINT32)uiBlkAddr; + *piElmErrCode = flmVerifyBlockHeader( &StateInfo, pBlkInfo, uiBlockSize, + 0xFFFFFFFF, (uiNumBlksRead > 1) ? + uiPrevNextBlkAddr : 0, TRUE); + if (*piElmErrCode != 0) + { + uiNumErrors++; + chkReportError( *piElmErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, StateInfo.uiLevel, uiBlkAddr, + uiParentBlkAddr, 0, 0); + } + + // Verify that the ui16BlkBytesAvail is a reasonable size... + + if( (pBlkHdr->ui32NextBlkInChain != 0) && + (pBlkHdr->ui16BlkBytesAvail != 0)) + { + *piElmErrCode = FLM_BAD_AVAIL_SIZE; + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + // Add the current data to the verifier.... + // We really only should need to do this if this is the first block + // in the chain. + + if( !pBlkHdr->ui32PrevBlkInChain) + { + FLMBYTE * pucPtr = (FLMBYTE *)pBlkHdr + sizeof( F_BLK_HDR); + FLMUINT uiKeySize = (FLMUINT)FB2UW( pucPtr); + + pucData = pucPtr + uiKeySize + 2; + + uiDataLen = uiBlockSize - sizeof( F_BLK_HDR) - + pBlkHdr->ui16BlkBytesAvail - uiKeySize - 2; + + if (pNodeVerifier) + { + if (RC_BAD( rc = pNodeVerifier->AddData( + StateInfo.ui64ElmNodeId, pucData, uiDataLen))) + { + goto Exit; + } + } + } + + uiPrevNextBlkAddr = uiBlkAddr; + uiBlkAddr = pBlkHdr->ui32NextBlkInChain; + } // end of while (uiNextBlkAddress) + + +Exit: + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if (pBlkHdr) + { + // The reason for the else is that pBlkHdr will normally be + // pointing into pSCache. Only if the call to getBlock() + // inside of blkRead() fails will memory be allocated + // explicitly for pBlkHdr + f_free( &pBlkHdr); + } + + return rc; +} + + +/*************************************************************************** +Desc: This routine checks all of the blocks/links in a sub-tree of a + B-TREE. It calls itself recursively whenever it descends a level + in the tree. +*****************************************************************************/ +RCODE F_DbCheck::verifySubTree( + STATE_INFO * pParentState, + STATE_INFO * pStateInfo, + FLMUINT uiBlkAddress, + FLMBYTE ** ppucResetKey, + FLMUINT uiResetKeyLen, + FLMUINT64 ui64ResetNodeId) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = NULL; + F_BLK_HDR * pBlkHdr = NULL; + FLMUINT uiLevel = pStateInfo->uiLevel; + FLMUINT uiBlkType = pStateInfo->uiBlkType; + FLMUINT uiLfType = m_pLFile->eLfType; + FLMUINT uiBlockSize = m_pDb->m_pDatabase->m_uiBlockSize; + FLMUINT uiParentBlkAddress; + FLMUINT uiChildBlkAddress; + FLMUINT uiPrevNextBlkAddress; + FLMINT iElmErrCode; + FLMINT iBlkErrCode = 0; + FLMINT iLastErrCode = 0; + FLMUINT uiNumErrors = 0; + FLMUINT64 ui64SaveKeyCount = 0; + FLMUINT64 ui64SaveKeyRefs = 0; + BLOCK_INFO SaveBlkInfo; + BLOCK_INFO * pBlkInfo; + FLMBOOL bProcessElm; + FLMBOOL bCountElm; + FLMBOOL bDescendToChildBlocks; + FLMINT iCompareStatus; + FLMINT iHdrErrCode; + F_NodeVerifier * pNodeVerifier = pStateInfo->pNodeVerifier; + STATE_INFO * pChildStateInfo = NULL; + F_CachedBlock * pTmpSCache = NULL; + F_BLK_HDR * pTmpBlkHdr = NULL; + + // Setup the state information. + + pStateInfo->pBlkHdr = NULL; + pStateInfo->ui32BlkAddress = (FLMUINT32)uiBlkAddress; + uiPrevNextBlkAddress = pStateInfo->ui32NextBlkAddr; + uiParentBlkAddress = (pParentState) + ? pParentState->ui32BlkAddress + : 0xFFFFFFFF; + + // Read the sub-tree block into memory + + bDescendToChildBlocks = TRUE; + + if (RC_BAD( rc = blkRead( uiBlkAddress, &pBlkHdr, &pSCache, &iBlkErrCode))) + { + if (iBlkErrCode) + { + uiNumErrors++; + iLastErrCode = iBlkErrCode; + + chkReportError( iBlkErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress, + uiParentBlkAddress, 0, 0); + + if( iBlkErrCode == FLM_BAD_BLK_CHECKSUM) + { + bDescendToChildBlocks = FALSE; + + // Allow to continue the check, but if this is a non-leaf block + // a non-zero iBlkErrCode will prevent us from descending to + // child blocks. Set rc to SUCCESS so we won't goto Exit below. + + rc = NE_XFLM_OK; + } + else if (iBlkErrCode == FLM_COULD_NOT_SYNC_BLK) + { + iLastErrCode = iBlkErrCode; + + // Need the goto here, because rc is changed to SUCCESS, + // and the goto below would get skipped. + + rc = NE_XFLM_OK; + goto fix_state; + } + } + + // If rc was not changed to SUCCESS above, goto Exit. + + if (RC_BAD( rc)) + { + goto Exit; + } + } + pStateInfo->pBlkHdr = pBlkHdr; + + // Verify the block header + // Don't recount the block if we are resetting. + + if (uiResetKeyLen == ~(FLMUINT)0) + { + m_Progress.ui64BytesExamined += (FLMUINT64)uiBlockSize; + pBlkInfo = &pStateInfo->BlkInfo; + } + else + { + pBlkInfo = NULL; + } + + chkCallProgFunc(); + + // Check the block header. + + if ((iHdrErrCode = + flmVerifyBlockHeader( pStateInfo, pBlkInfo, uiBlockSize, + (pParentState == NULL) + ? 0 + : 0xFFFFFFFF, + (pParentState == NULL) + ? 0 + : pParentState->ui32LastChildAddr, + TRUE)) == 0) + { + + // Verify the previous block's next block address -- it should + // equal the current block's address. + + if (uiPrevNextBlkAddress != 0xFFFFFFFF && + uiPrevNextBlkAddress != uiBlkAddress && + (uiResetKeyLen == ~(FLMUINT)0)) + { + iHdrErrCode = FLM_BAD_PREV_BLK_NEXT; + } + } + + if (iHdrErrCode != 0) + { + // Check to see if the previous block is still valid. + // It may be that the block has gone away, and so is no longer valid. + + if (iHdrErrCode == FLM_BAD_BLK_HDR_PREV) + { + + flmAssert( pParentState); + + if (RC_BAD( rc = blkRead( pParentState->ui32LastChildAddr, + &pTmpBlkHdr, &pTmpSCache, &iBlkErrCode))) + { + iLastErrCode = iBlkErrCode; + uiNumErrors++; + + chkReportError( iBlkErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, + uiBlkAddress, uiParentBlkAddress, 0, 0); + } + else + { + // If the block is free, it means that somewhere in our check, we + // deleted entries that resulted in this block being emptied, + // thus freed. + + if (pTmpBlkHdr->ui8BlkType == BT_FREE) + { + iHdrErrCode = 0; + } + else + { + iLastErrCode = iHdrErrCode; + uiNumErrors++; + chkReportError( iHdrErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, + uiBlkAddress, uiParentBlkAddress, 0, 0); + } + } + } + else + { + iLastErrCode = iHdrErrCode; + uiNumErrors++; + chkReportError( iHdrErrCode, XFLM_LOCALE_B_TREE, m_Progress.uiLfNumber, + m_Progress.uiLfType, uiLevel, uiBlkAddress, uiParentBlkAddress, + 0, 0); + } + + if (pTmpSCache) + { + ScaReleaseCache( pTmpSCache, FALSE); + pTmpSCache = NULL; + pTmpBlkHdr = NULL; + } + } + + // Verify the structure of the block + + if( RC_BAD( rc = verifyBlockStructure( uiBlockSize, + (F_BTREE_BLK_HDR *)pBlkHdr))) + { + if (rc == NE_XFLM_BTREE_ERROR) + { + iBlkErrCode = FLM_BAD_BLOCK_STRUCTURE; + rc = NE_XFLM_OK; + goto fix_state; + } + else + { + goto Exit; + } + } + + // Go through the elements in the block. + + for ( pStateInfo->uiElmOffset=0; + (pStateInfo->uiElmOffset < + ((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys && + RC_OK( m_LastStatusRc)); + pStateInfo->uiElmOffset++) + { + // If we are resetting, save any statistical information so we + // can back it out if we need to. + + if (uiResetKeyLen != ~(FLMUINT)0) + { + ui64SaveKeyCount = pStateInfo->ui64KeyCount; + ui64SaveKeyRefs = pStateInfo->ui64KeyRefs; + f_memcpy( &SaveBlkInfo, &pStateInfo->BlkInfo, sizeof( BLOCK_INFO)); + bCountElm = FALSE; + bProcessElm = FALSE; + } + else + { + bCountElm = TRUE; + bProcessElm = TRUE; + } + + // Verify the element first, then check if we are restting... + + m_LastStatusRc = flmVerifyElement( pStateInfo, m_pLFile, m_pIxd, + &iElmErrCode); + if (iElmErrCode) + { + // Report any errors in the element. + + iLastErrCode = iElmErrCode; + uiNumErrors++; + + if (RC_BAD( rc = chkReportError( iElmErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress, + uiParentBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->ui64ElmNodeId))) + { + break; + } + } + + // See if we are resetting + + iCompareStatus = 0; + + if ((uiResetKeyLen != ~(FLMUINT)0) && + pStateInfo->bValidKey && + (!pStateInfo->uiElmKeyLen || + ((iCompareStatus = f_memcmp( + pStateInfo->pucElmKey, *ppucResetKey, + pStateInfo->uiElmKeyLen < uiResetKeyLen + ? pStateInfo->uiElmKeyLen + : uiResetKeyLen)) >= 0))) + { + + // A key length of 0 is valid. It refers to the LEM. All keys are + // less than the LEM if their length is > 0. + + if ((uiResetKeyLen && pStateInfo->uiElmKeyLen || + !pStateInfo->uiElmKeyLen)) + { + // If we passed the target key, or we are on the last element + // then count it. + + bProcessElm = TRUE; + if ((iCompareStatus > 0) || !(pStateInfo->uiElmKeyLen)) + { + if ( (uiBlkType == BT_LEAF_DATA) || + (uiBlkType == BT_LEAF) ) + { + bCountElm = TRUE; + + uiResetKeyLen = ~(FLMUINT)0; + + if (*ppucResetKey) + { + f_free( ppucResetKey); + } + + *ppucResetKey = NULL; + ui64ResetNodeId = 0; + } + } + else if ( uiLfType == XFLM_LF_INDEX) + { + bCountElm = TRUE; + } + } + } + + if (bCountElm) + { + pStateInfo->BlkInfo.ui64ElementCount++; + } + + // Check for index keys that can be verified on leaf level blocks. + + if (uiResetKeyLen == ~(FLMUINT)0) + { + if ( isIndexBlk( (F_BTREE_BLK_HDR *)pStateInfo->pBlkHdr) && + (pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF || + pStateInfo->pBlkHdr->ui8BlkType == BT_LEAF_DATA) && + pStateInfo->uiElmKeyLen) + { + FLMUINT64 ui64CurrTransId = pStateInfo->pDb->m_ui64CurrTransID; + + if (RC_BAD( rc = verifyIXRefs( pStateInfo, ui64ResetNodeId))) + { + goto Exit; + } + + // Make sure we resynchronize when we make changes to + // blocks we are looking at. + + if (pStateInfo->pDb->m_ui64CurrTransID != ui64CurrTransId) + { + if (m_bPhysicalCorrupt) + { + m_bPhysicalCorrupt = FALSE; + m_uiFlags |= XFLM_DO_LOGICAL_CHECK; + } + + rc = m_LastStatusRc = RC_SET( NE_XFLM_RESET_NEEDED); + goto Exit; + } + } + + if (RC_BAD( m_LastStatusRc)) + { + break; + } + + // Keep track of the number of continuation elements. + + if( (uiBlkType == BT_LEAF_DATA) && + ((*pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT) == 0)) + { + pStateInfo->BlkInfo.ui64ContElementCount++; + pStateInfo->BlkInfo.ui64ContElmBytes += pStateInfo->uiElmLen; + } + } + + // Do some further checking. + + if (iElmErrCode == 0) + { + if (bProcessElm && + (uiBlkType == BT_LEAF_DATA || + uiBlkType == BT_LEAF) && + uiLfType == XFLM_LF_COLLECTION) + { + // No need to process LEM element + + if ((pStateInfo->uiElmKeyLen != 0) && (pStateInfo->bValidKey)) + { + // Is this record stored in a chain of DO blocks...? + + if ((*pStateInfo->pucElm & BTE_FLAG_DATA_BLOCK) != 0) + { + flmAssert( pStateInfo->uiElmDataLen == 4); + + if( RC_BAD( rc = verifyDOChain( pStateInfo, + FB2UD( pStateInfo->pucElmData), &iElmErrCode))) + { + goto Exit; + } + } + else + { + // Since DOM nodes may be spread across multiple entries + // in the Btree block, it may be impractical to read in + // the entire node all at once, we need a way of doing the + // verification a little at a time. The NodeVerifier + // object handles this. We pass in the data as we get it, + // it appends the data to any data it had left over from + // a previous call, and then verifies as much as it can. + // Any "left over" data is saved for the next call. + + // If the "first" flag is set on this element, twe need + // to reset the verifier + + if( *pStateInfo->pucElm & BTE_FLAG_FIRST_ELEMENT && + pNodeVerifier) + { + pNodeVerifier->Reset( pStateInfo); + } + + // Add the current data to the verifier.... + + flmAssert( pStateInfo->ui64ElmNodeId); + if (pNodeVerifier) + { + if (RC_BAD( rc = pNodeVerifier->AddData( + pStateInfo->ui64ElmNodeId, pStateInfo->pucElmData, + pStateInfo->uiElmDataLen))) + { + goto Exit; + } + } + } + + // If this is the last entry for this element, then we can + // finalize the node verifier. This entails assembling + // the DOM node, and possibly adding required information + // to a result set for later DOM link verification. We also + // add the node Id to the index result set if we are + // checking/verifying indexes. + + if( *pStateInfo->pucElm & BTE_FLAG_LAST_ELEMENT) + { + if( pNodeVerifier) + { + if( RC_BAD( rc = pNodeVerifier->finalize( + m_pDb, m_pDb->m_pDict, + pStateInfo->pCollection->lfInfo.uiLfNum, + pStateInfo->ui64ElmNodeId, m_bSkipDOMLinkCheck, + &iElmErrCode))) + { + goto Exit; + } + } + } + } + + if (bProcessElm) + { + if (iElmErrCode != 0) + { + // Report any errors in the element. + + iLastErrCode = iElmErrCode; + uiNumErrors++; + + chkReportError( iElmErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, + uiBlkAddress, uiParentBlkAddress, pStateInfo->uiElmOffset, + pStateInfo->ui64ElmNodeId); + + if (RC_BAD( m_LastStatusRc)) + { + break; + } + } + } + } + else if (uiBlkType != BT_LEAF_DATA && uiBlkType != BT_LEAF) + { + flmAssert( uiBlkType != BT_DATA_ONLY); + uiChildBlkAddress = (FLMUINT)FB2UD(pStateInfo->pucElm); + + // Check the child sub-tree -- NOTE, this is a recursive call. + + if (bProcessElm) + { + if (!bDescendToChildBlocks) + { + rc = NE_XFLM_OK; + } + else + { + pChildStateInfo = (pStateInfo - 1); + if (pChildStateInfo->uiElmKeyLen && + (uiResetKeyLen != ~(FLMUINT)0)) + { + uiResetKeyLen = pChildStateInfo->uiElmKeyLen; + ui64ResetNodeId = pChildStateInfo->ui64ElmNodeId; + + if (*ppucResetKey) + { + f_free( ppucResetKey); + } + + if (RC_BAD( rc = f_calloc( uiResetKeyLen + 1, + ppucResetKey))) + { + goto Exit; + } + + f_memcpy( *ppucResetKey, pChildStateInfo->pucElmKey, + uiResetKeyLen); + } + + rc = verifySubTree( pStateInfo, (pStateInfo - 1), + uiChildBlkAddress, ppucResetKey, uiResetKeyLen, + ui64ResetNodeId); + } + + if (RC_BAD( rc) || RC_BAD( m_LastStatusRc)) + { + goto Exit; + } + + // Once we reach the key, set it to an empty to key so that + // we will always descend to the child block after this point. + + uiResetKeyLen = ~(FLMUINT)0; + if (*ppucResetKey) + { + f_free( ppucResetKey); + } + + *ppucResetKey = NULL; + ui64ResetNodeId = 0; + } + + // Save the child block address in the level information + + pStateInfo->ui32LastChildAddr = (FLMUINT32)uiChildBlkAddress; + } + } + + // If we were resetting on this element, restore the statistics to what + // they were before. + + if( !bCountElm) + { + pStateInfo->ui64KeyCount = ui64SaveKeyCount; + pStateInfo->ui64KeyRefs = ui64SaveKeyRefs; + f_memcpy( &pStateInfo->BlkInfo, &SaveBlkInfo, sizeof( BLOCK_INFO)); + } + + if (RC_BAD( rc = chkCallProgFunc())) + { + break; + } + } + + // Verify that the last key in the block matches the parent's key. + + if (iLastErrCode == 0 && pParentState && RC_OK( m_LastStatusRc)) + { + if (pStateInfo->bValidKey && pParentState->bValidKey && + f_memcmp( pStateInfo->pucElmKey, + pParentState->pucElmKey, + pStateInfo->uiElmKeyLen < pParentState->uiElmKeyLen + ? pStateInfo->uiElmKeyLen + : pParentState->uiElmKeyLen) != 0) + { + iLastErrCode = FLM_BAD_PARENT_KEY; + uiNumErrors++; + + chkReportError( iLastErrCode, XFLM_LOCALE_B_TREE, + m_Progress.uiLfNumber, m_Progress.uiLfType, uiLevel, uiBlkAddress, + uiParentBlkAddress, 0, 0); + } + } + +fix_state: + + // If the block could not be verified, set the level's next block + // address and last child address to zero to indicate that we really + // aren't sure we're at the right place in this level in the B-TREE. + + if (iLastErrCode != 0) + { + pStateInfo->BlkInfo.iErrCode = iLastErrCode; + pStateInfo->BlkInfo.uiNumErrors += uiNumErrors; + + // Reset all child block states. + + for (;;) + { + pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF; + pStateInfo->ui32LastChildAddr = 0xFFFFFFFF; + pStateInfo->bValidKey = FALSE; + pStateInfo->uiElmLastFlag = 0xFF; + + // Quit when the leaf level has been reached. + + if (pStateInfo->uiLevel == 0) + { + break; + } + + pStateInfo--; + } + } + +Exit: + + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + else if (pBlkHdr) + { + f_free( &pBlkHdr); + } + + pStateInfo->pBlkHdr = NULL; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBYTE * getEntryEnd( + FLMBYTE * pucEntry, + FLMUINT uiBlkType) +{ + FLMBYTE * pucEnd = pucEntry; + + switch (uiBlkType) + { + case BT_LEAF: + { + FLMUINT uiKL = (FLMUINT)FB2UW( pucEnd); + pucEnd += (uiKL + 2); + break; + } + + case BT_LEAF_DATA: + { + FLMUINT uiKL; + FLMUINT uiDL; + FLMUINT ucFlags = *pucEnd; + + pucEnd++; + + if (ucFlags & BTE_FLAG_KEY_LEN) + { + uiKL = (FLMUINT)FB2UW( pucEnd); + pucEnd += 2; + } + else + { + uiKL = *pucEnd; + pucEnd++; + } + + if (ucFlags & BTE_FLAG_DATA_LEN) + { + uiDL = FB2UW( pucEnd); + pucEnd += 2; + } + else + { + uiDL = *pucEnd; + pucEnd++; + } + + if (ucFlags & BTE_FLAG_OA_DATA_LEN) + { + pucEnd += 4; + } + + pucEnd += (uiKL + uiDL); + break; + } + + case BT_NON_LEAF: + { + FLMUINT uiKL; + + pucEnd += 4; + uiKL = (FLMUINT)FB2UW( pucEnd); + pucEnd += (uiKL + 2); + break; + } + case BT_NON_LEAF_COUNTS: + { + FLMUINT uiKL; + + pucEnd += 8; + uiKL = (FLMUINT)FB2UW( pucEnd); + pucEnd += (uiKL + 2); + break; + } + } + + return( pucEnd); +} + +/*************************************************************************** +Desc: Compare two cache blocks during a sort to determine which + one has lower address. +*****************************************************************************/ +FINLINE FLMINT blkSortCompare( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + BlkStruct * pBlk1 = ((BlkStruct *)pvBuffer) + uiPos1; + BlkStruct * pBlk2 = ((BlkStruct *)pvBuffer) + uiPos2; + + if (pBlk1->uiStartOfEntry < pBlk2->uiStartOfEntry) + { + return( -1); + } + else if (pBlk1->uiStartOfEntry > pBlk2->uiStartOfEntry) + { + return( 1); + } + else + { + return( 0); + } +} + +/*************************************************************************** +Desc: Swap two entries in cache table during sort. +*****************************************************************************/ +FINLINE void blkSortSwap( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + BlkStruct * pBlkEntryTable = (BlkStruct *)pvBuffer; + BlkStruct TmpEntry = pBlkEntryTable [uiPos1]; + + pBlkEntryTable[ uiPos1] = pBlkEntryTable[ uiPos2]; + pBlkEntryTable[ uiPos2] = TmpEntry; +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_DbCheck::verifyBlockStructure( + FLMUINT uiBlockSize, + F_BTREE_BLK_HDR * pBlkHdr) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIndex; + FLMBYTE * pucEntry; + FLMBYTE * pucEnd; + BlkStruct * pCurBlk; + FLMUINT uiNumEntries; + + // No need to check anything in the block if there are less than 2 entries. + + if ((uiNumEntries = pBlkHdr->ui16NumKeys) < 2) + { + goto Exit; + } + + if (uiNumEntries > m_uiBlkEntryArraySize) + { + FLMUINT uiNewEntryArraySize = uiNumEntries + 200; + + if (RC_BAD( rc = f_realloc( sizeof( BlkStruct) * uiNewEntryArraySize, + &m_pBlkEntries))) + { + goto Exit; + } + f_memset( &m_pBlkEntries [m_uiBlkEntryArraySize], 0, + sizeof( BlkStruct) * (uiNewEntryArraySize - m_uiBlkEntryArraySize)); + m_uiBlkEntryArraySize = uiNewEntryArraySize; + } + + for (uiIndex = 0, pCurBlk = m_pBlkEntries; + uiIndex < uiNumEntries; + uiIndex++, pCurBlk++) + { + pucEntry = BtEntry( (FLMBYTE *)pBlkHdr, uiIndex); + + if( pucEntry > (FLMBYTE *)pBlkHdr + uiBlockSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + pucEnd = getEntryEnd( pucEntry, pBlkHdr->stdBlkHdr.ui8BlkType); + pCurBlk->uiStartOfEntry = (FLMUINT)pucEntry; + pCurBlk->uiEndOfEntry = (FLMUINT)pucEnd; + } + + // Now sort the entries and check the offsets. + + f_qsort( m_pBlkEntries, 0, uiNumEntries - 1, blkSortCompare, blkSortSwap); + + for (uiIndex = 1; uiIndex < uiNumEntries; uiIndex++) + { + if (m_pBlkEntries [uiIndex - 1].uiEndOfEntry > + m_pBlkEntries [uiIndex].uiStartOfEntry) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Verify the reference set for a key +*********************************************************************/ +RCODE F_DbCheck::verifyIXRefs( + STATE_INFO * pStateInfo, + FLMUINT64 ui64ResetNodeId + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId; + + // Get the NodeId for the element. + ui64NodeId = pStateInfo->ui64ElmNodeId; + + if (ui64NodeId <= ui64ResetNodeId) + { + ui64ResetNodeId = 0; + } + + if (!ui64ResetNodeId && !m_bPhysicalCorrupt) + { + if (RC_BAD( rc = verifyIXRSet( pStateInfo))) + { + goto Exit; + } + } + + pStateInfo->ui64KeyRefs++; + +Exit: + + return( rc); +} + + +/******************************************************************** +Desc: Initialize the STATE_INFO in preparation to do checking. +*********************************************************************/ +void flmInitReadState( + STATE_INFO * pStateInfo, + FLMBOOL * pbStateInitialized, + FLMUINT, //uiVersionNum, + F_Db * pDb, // May be NULL. + LF_HDR *, //pLogicalFile, + FLMUINT uiLevel, + FLMUINT uiExpectedBlkType, + FLMBYTE * pucKeyBuffer) +{ + + f_memset( pStateInfo, 0, sizeof( STATE_INFO)); + *pbStateInitialized = TRUE; + pStateInfo->pDb = pDb; + pStateInfo->uiLevel = uiLevel; + + pStateInfo->uiBlkType = uiExpectedBlkType; + pStateInfo->pucElmKey = pucKeyBuffer; + pStateInfo->uiElmLastFlag = 0xFF; + pStateInfo->ui32NextBlkAddr = 0xFFFFFFFF; + pStateInfo->ui32LastChildAddr = 0xFFFFFFFF; +} + +/****************************************************************************** +Desc: Constructor +******************************************************************************/ +F_NodeVerifier::F_NodeVerifier() +{ + m_pucBuf = &m_ucBuf [0]; + m_uiBufSize = sizeof( m_ucBuf); + m_uiBytesInBuf = 0; + m_pXRefRS = NULL; + m_bFinalizeCalled = FALSE; + m_pRS = NULL; + + f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); +} + + +/****************************************************************************** +Desc: Destructor +******************************************************************************/ +F_NodeVerifier::~F_NodeVerifier() +{ + if (m_pRS) + { + m_pRS->Release(); + } + if (m_pucBuf != &m_ucBuf [0]) + { + f_free( &m_pucBuf); + } +} + +/****************************************************************************** +Desc: Reset +******************************************************************************/ +void F_NodeVerifier::Reset( + STATE_INFO * pStateInfo) +{ + m_uiBytesInBuf = 0; + m_bFinalizeCalled = FALSE; + f_memset( m_pucBuf, 0, m_uiBufSize); + f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); + + if( *pStateInfo->pucElm & BTE_FLAG_OA_DATA_LEN) + { + m_uiOverallLength = pStateInfo->uiElmOADataLen; + } + else + { + m_uiOverallLength = pStateInfo->uiElmDataLen; + } +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_NodeVerifier::AddData( + FLMUINT64 ui64NodeId, + void * pucData, + FLMUINT uiDataLen) +{ + RCODE rc = NE_XFLM_OK; + + // Verify the node Id or Save it (first time) + if( m_nodeInfo.ui64NodeId) + { + if (m_nodeInfo.ui64NodeId != ui64NodeId) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + } + else + { + m_nodeInfo.ui64NodeId = ui64NodeId; + } + + // Copy the entry data into the local buffer. At most, + // we only need MAX_DOM_HEADER_SIZE bytes to make a + // complete DOM node header - unless we have an + // element node, in which case we want to get all of + // the attribute nodes. + + if (uiDataLen + m_uiBytesInBuf > m_uiBufSize) + { + eDomNodeType eNodeType; + + // Node type should be in the first byte of data we have for + // the node. + + if (!m_uiBytesInBuf) + { + eNodeType = (eDomNodeType)((*((FLMBYTE *)pucData)) & 0x0F); + } + else + { + eNodeType = (eDomNodeType)((*m_pucBuf) & 0x0F); + } + if (eNodeType != ELEMENT_NODE) + { + flmAssert( m_uiBufSize >= MAX_DOM_HEADER_SIZE); + + // Only copy enough to fill up the current buffer - don't + // really need any more than the header for non-element nodes. + + uiDataLen = m_uiBufSize - m_uiBytesInBuf; + } + else + { + // Must get everything for element nodes + + if (m_pucBuf != &m_ucBuf [0]) + { + if (RC_BAD( rc = f_realloc( uiDataLen + m_uiBytesInBuf, &m_pucBuf))) + { + goto Exit; + } + } + else + { + FLMBYTE * pucNew; + + if (RC_BAD( rc = f_alloc( uiDataLen + m_uiBytesInBuf, &pucNew))) + { + goto Exit; + } + if (m_uiBytesInBuf) + { + f_memcpy( pucNew, m_pucBuf, m_uiBytesInBuf); + } + m_pucBuf = pucNew; + } + m_uiBufSize = uiDataLen + m_uiBytesInBuf; + } + } + + f_memcpy( &m_pucBuf[m_uiBytesInBuf], pucData, uiDataLen); + m_uiBytesInBuf += uiDataLen; + +Exit: + + return rc; +} + +/******************************************************************** +Desc: Do the final setup to store the node information in the Node + Result Set. +*********************************************************************/ +RCODE F_NodeVerifier::finalize( + F_Db * pDb, + F_Dict * pDict, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMBOOL bSkipDOMLinkCheck, + FLMINT * piElmErrCodeRV) +{ + RCODE rc = NE_XFLM_OK; + NODE_RS_ENTRY * pRSEntry = NULL; + FLMUINT uiRSBufIndex; + FLMUINT uiStorageFlags; + F_NameTable * pNameTable = pDict->getNameTable(); + F_BufferIStream bufferStream; + + *piElmErrCodeRV = 0; + + if( m_bFinalizeCalled) + { + flmAssert( 0); + goto Exit; + } + + f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); + + if( RC_BAD( rc = bufferStream.open( m_pucBuf, m_uiBytesInBuf))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadNodeInfo( uiCollection, ui64NodeId, + &bufferStream, m_uiOverallLength, FALSE, &m_nodeInfo, &uiStorageFlags))) + { + goto Exit; + } + + // See if child element count is set. + + if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) + { + // This bit should only be set for elements. + + if( m_nodeInfo.eNodeType != ELEMENT_NODE) + { + *piElmErrCodeRV = FLM_BAD_NODE_TYPE; + goto Exit; + } + + // If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set. + + if( m_nodeInfo.uiChildElmCount) + { + if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT)) + { + *piElmErrCodeRV = FLM_BAD_CHILD_ELM_COUNT; + goto Exit; + } + } + } + + // Verify the Name and Prefix Ids. + + if( RC_BAD( rc = verifyNameId( pDb, m_nodeInfo.eNodeType, + m_nodeInfo.uiNameId, pNameTable, piElmErrCodeRV))) + { + goto Exit; + } + + if( *piElmErrCodeRV) + { + goto Exit; + } + + if( RC_BAD( rc = verifyPrefixId( pDb, + m_nodeInfo.uiPrefixId, pNameTable, piElmErrCodeRV))) + { + goto Exit; + } + + if( *piElmErrCodeRV) + { + goto Exit; + } + + if( !bSkipDOMLinkCheck) + { + FLMUINT16 ui16BitMap = 0; + + // Build a buffer to set into the Result Set for later verification... + + if( RC_BAD( rc = f_calloc( sizeof( NODE_RS_ENTRY), &pRSEntry))) + { + goto Exit; + } + + uiRSBufIndex = 0; + if( m_nodeInfo.ui64DocumentId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64DocumentId; + ui16BitMap |= CHK_BM_ROOT_ID; + } + + if( m_nodeInfo.ui64ParentId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64ParentId; + ui16BitMap |= CHK_BM_PARENT_ID; + } + + if( m_nodeInfo.ui64PrevSibId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64PrevSibId; + ui16BitMap |= CHK_BM_PREV_SIBLING; + } + + if( m_nodeInfo.ui64NextSibId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64NextSibId; + ui16BitMap |= CHK_BM_NEXT_SIBLING; + } + + if( m_nodeInfo.ui64FirstChildId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64FirstChildId; + ui16BitMap |= CHK_BM_FIRST_CHILD; + } + + if( m_nodeInfo.ui64LastChildId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64LastChildId; + ui16BitMap |= CHK_BM_LAST_CHILD; + } + + if( m_nodeInfo.ui64AnnotationId) + { + pRSEntry->ui64FieldArray[ uiRSBufIndex++] = m_nodeInfo.ui64AnnotationId; + ui16BitMap |= CHK_BM_ANNOTATION; + } + + pRSEntry->hdr.ui64NodeId = m_nodeInfo.ui64NodeId; + pRSEntry->hdr.ui16BitMap = ui16BitMap; + m_bFinalizeCalled = TRUE; + + // Finally add the result to the result set for later evaluation. + + if( RC_BAD( rc = m_pRS->addEntry( NULL, NULL, + (FLMBYTE *)&(pRSEntry->hdr.ui64NodeId), + sizeof( FLMUINT64), (FLMBYTE *)pRSEntry, + sizeof( NODE_RS_HDR) + (uiRSBufIndex * sizeof( FLMUINT64))))) + { + *piElmErrCodeRV = -1; + goto Exit; + } + } + + if( m_pXRefRS) + { + if (RC_BAD( rc = checkForIndexes( pDb, pDict, uiCollection))) + { + goto Exit; + } + } + +Exit: + + if( pRSEntry) + { + f_free( &pRSEntry); + } + + return( rc); +} + +/****************************************************************************** +Desc: Method to add any indexes to the document list that qualify. +******************************************************************************/ +RCODE F_NodeVerifier::checkForIndexes( + F_Db * pDb, + F_Dict * pDict, + FLMUINT uiCollection) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64DocumentId; + DOC_IXD_XREF DocXRef; + ICD * pIcd; + F_AttrElmInfo defInfo; + + // Determine if there are any indexs that qualify to be included in the + // index list. + // + // If there is no document id, then this node is the the root node? + + ui64DocumentId = m_nodeInfo.ui64DocumentId; + + if( !ui64DocumentId) + { + if( !m_nodeInfo.ui64ParentId) + { + ui64DocumentId = m_nodeInfo.ui64NodeId; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + } + } + + switch( m_nodeInfo.eNodeType) + { + case DOCUMENT_NODE: + case ELEMENT_NODE: + case DATA_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + case ANNOTATION_NODE: + { + if( RC_BAD( rc = pDict->getElement( pDb, + m_nodeInfo.uiNameId, &defInfo))) + { + goto Exit; + } + break; + } + + case ATTRIBUTE_NODE: + { + if (RC_BAD( rc = pDict->getAttribute( pDb, m_nodeInfo.uiNameId, + &defInfo))) + { + goto Exit; + } + break; + } + + default: + { + goto Exit; + } + } + + pIcd = defInfo.m_pFirstIcd; + + // Do we have any qualifying indexes? + + while (pIcd) + { + // Only process if the target collection matches. + + if( pIcd->pIxd->uiCollectionNum == uiCollection) + { + if( pIcd->uiFlags & (ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET)) + { + FLMBYTE ucDummy = 0; + + // Get the document list entry for this node. + // Build a buffer to set into the Result Set for later verification... + + DocXRef.uiIndexNum = pIcd->pIxd->uiIndexNum; + DocXRef.ui64DocId = m_nodeInfo.ui64DocumentId; + DocXRef.uiCollection = uiCollection; + + if (RC_BAD( rc = m_pXRefRS->addEntry( NULL, NULL, + (FLMBYTE *)&DocXRef, + sizeof( DOC_IXD_XREF), + &ucDummy, 1))) + { + // It's okay if we get a duplicate entry, only one will be saved in + // the result set. + + if (rc == NE_XFLM_NOT_UNIQUE) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + } + pIcd = pIcd->pNextInChain; + } + +Exit: + + return rc; +} + +/****************************************************************************** +Desc: +******************************************************************************/ +FLMUINT64 getLinkVal( + FLMUINT uiBMId, + NODE_RS_ENTRY * pRSEntry + ) +{ + FLMUINT64 * pui64Ptr = pRSEntry->ui64FieldArray; + FLMUINT16 ui16BitMap = pRSEntry->hdr.ui16BitMap; + FLMUINT64 ui64Link = 0; + + if (ui16BitMap & CHK_BM_ROOT_ID) + { + if (uiBMId == CHK_BM_ROOT_ID) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_PARENT_ID) + { + if (uiBMId == CHK_BM_PARENT_ID) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_PREV_SIBLING) + { + if (uiBMId == CHK_BM_PREV_SIBLING) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_NEXT_SIBLING) + { + if (uiBMId == CHK_BM_NEXT_SIBLING) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_FIRST_CHILD) + { + if (uiBMId == CHK_BM_FIRST_CHILD) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_LAST_CHILD) + { + if (uiBMId == CHK_BM_LAST_CHILD) + { + ui64Link = *pui64Ptr; + goto Exit; + } + pui64Ptr++; + } + + if (ui16BitMap & CHK_BM_ANNOTATION) + { + if (uiBMId == CHK_BM_ANNOTATION) + { + ui64Link = *pui64Ptr; + goto Exit; + } + } + + +Exit: + + return ui64Link; +} + + +/****************************************************************************** +Desc: Verify that the nameId is in the dictionary. +******************************************************************************/ +RCODE F_NodeVerifier::verifyNameId( + F_Db * pDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + F_NameTable * pNameTable, + FLMINT * piErrCode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiType; + FLMUINT uiLen; + + if( !uiNameId) + { + goto Exit; + } + + switch (eNodeType) + { + case DOCUMENT_NODE: + case ELEMENT_NODE: + case DATA_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + case ANNOTATION_NODE: + { + uiType = ELM_ELEMENT_TAG; + break; + } + case ATTRIBUTE_NODE: + { + uiType = ELM_ATTRIBUTE_TAG; + break; + } + default: + { + flmAssert( 0); + *piErrCode = FLM_UNSUPPORTED_NODE_TYPE; + goto Exit; + } + } + + if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( pDb, uiType, + uiNameId, NULL, NULL, &uiLen, NULL, NULL, NULL, NULL, TRUE))) + { + *piErrCode = FLM_BAD_INVALID_NAME_ID; + goto Exit; + } + + +Exit: + + return rc; +} + +/****************************************************************************** +Desc: Verify that the prefixId is in the dictionary. +******************************************************************************/ +RCODE F_NodeVerifier::verifyPrefixId( + F_Db * pDb, + FLMUINT uiPrefixId, + F_NameTable * pNameTable, + FLMINT * piErrCode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + + if (!uiPrefixId) + { + goto Exit; // Ok. + } + + if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( + pDb, ELM_PREFIX_TAG, uiPrefixId, NULL, NULL, &uiLen))) + { + *piErrCode = FLM_BAD_INVALID_PREFIX_ID; + goto Exit; + } + +Exit: + + return rc; +} + +/****************************************************************************** +Desc: Add a key to the result set. +******************************************************************************/ +RCODE F_KeyCollector::addKey( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY * pKref + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucKey = (FLMBYTE *)&pKref[1]; + FLMBYTE * pucData = pucKey + pKref->ui16KeyLen; + FLMUINT uiDataLen; + + flmAssert( pKref->ui16KeyLen <= MAX_KEY_SIZ); + + // Can't store an entry with zero length data. + + if ((uiDataLen = pKref->uiDataLen) == 0) + { + *pucData = 0; + uiDataLen = 1; + } + + // Save the key in the result set. + + if (RC_BAD( rc = m_pDbCheck->m_pIxRSet->addEntry( pDb, pIxd, pucKey, + (FLMUINT)pKref->ui16KeyLen, + pucData, uiDataLen))) + { + if (rc == NE_XFLM_NOT_UNIQUE) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + m_ui64TotalKeys++; + +Exit: + + return( rc); +} + diff --git a/version5/src/fntable.cpp b/version5/src/fntable.cpp new file mode 100644 index 0000000..39c2834 --- /dev/null +++ b/version5/src/fntable.cpp @@ -0,0 +1,2566 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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 RCODE findTagName( + F_Db * pDb, + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + F_DataVector * pSrchKey, + FLMUINT * puiDictNum, + FLMUINT64 * pui64DocumentID); + +FLMUNICODE gv_uzXFLAIMNamespace[] = // http://www.novell.com/XMLDatabase/Schema +{ 'h','t','t','p',':','/','/','w','w','w','.','n','o','v','e','l','l','.','c','o','m','/', + 'X','M','L','D','a','t','a','b','a','s','e','/','S','c','h','e','m','a', 0}; + +RESERVED_TAG_NAME FlmReservedElementTags[] = +{ + {ELM_ELEMENT_TAG_NAME, ELM_ELEMENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ATTRIBUTE_TAG_NAME, ELM_ATTRIBUTE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_INDEX_TAG_NAME, ELM_INDEX_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ELEMENT_COMPONENT_TAG_NAME, ELM_ELEMENT_COMPONENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ATTRIBUTE_COMPONENT_TAG_NAME, ELM_ATTRIBUTE_COMPONENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_COLLECTION_TAG_NAME, ELM_COLLECTION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_PREFIX_TAG_NAME, ELM_PREFIX_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_NEXT_DICT_NUMS_TAG_NAME, ELM_NEXT_DICT_NUMS_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_DOCUMENT_TITLE_TAG_NAME, ELM_DOCUMENT_TITLE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ELM_INVALID_TAG_NAME, ELM_INVALID_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_QUARANTINED_TAG_NAME, ELM_QUARANTINED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ALL_TAG_NAME, ELM_ALL_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ANNOTATION_TAG_NAME, ELM_ANNOTATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ANY_TAG_NAME, ELM_ANY_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ATTRIBUTE_GROUP_TAG_NAME, ELM_ATTRIBUTE_GROUP_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_CHOICE_TAG_NAME, ELM_CHOICE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_COMPLEX_CONTENT_TAG_NAME, ELM_COMPLEX_CONTENT_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_COMPLEX_TYPE_TAG_NAME, ELM_COMPLEX_TYPE_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_DOCUMENTATION_TAG_NAME, ELM_DOCUMENTATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ENUMERATION_TAG_NAME, ELM_ENUMERATION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_EXTENSION_TAG_NAME, ELM_EXTENSION_TAG, XFLM_NODATA_TYPE, gv_uzXFLAIMNamespace}, + {ELM_DELETE_TAG_NAME, ELM_DELETE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ELM_BLOCK_CHAIN_TAG_NAME, ELM_BLOCK_CHAIN_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ELM_ENCDEF_TAG_NAME, ELM_ENCDEF_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ELM_SWEEP_TAG_NAME, ELM_SWEEP_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {NULL, 0, 0, NULL} +}; + +RESERVED_TAG_NAME FlmReservedAttributeTags[] = +{ + {ATTR_DICT_NUMBER_TAG_NAME, ATTR_DICT_NUMBER_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_COLLECTION_NUMBER_TAG_NAME, ATTR_COLLECTION_NUMBER_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_COLLECTION_NAME_TAG_NAME, ATTR_COLLECTION_NAME_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NAME_TAG_NAME, ATTR_NAME_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_TARGET_NAMESPACE_TAG_NAME, ATTR_TARGET_NAMESPACE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_TYPE_TAG_NAME, ATTR_TYPE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_STATE_TAG_NAME, ATTR_STATE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_LANGUAGE_TAG_NAME, ATTR_LANGUAGE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_INDEX_OPTIONS_TAG_NAME, ATTR_INDEX_OPTIONS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_INDEX_ON_TAG_NAME, ATTR_INDEX_ON_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_REQUIRED_TAG_NAME, ATTR_REQUIRED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_LIMIT_TAG_NAME, ATTR_LIMIT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_COMPARE_RULES_TAG_NAME, ATTR_COMPARE_RULES_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_KEY_COMPONENT_TAG_NAME, ATTR_KEY_COMPONENT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_DATA_COMPONENT_TAG_NAME, ATTR_DATA_COMPONENT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_LAST_DOC_INDEXED_TAG_NAME, ATTR_LAST_DOC_INDEXED_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_ELEMENT_NUM_TAG_NAME, ATTR_NEXT_ELEMENT_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_ATTRIBUTE_NUM_TAG_NAME, ATTR_NEXT_ATTRIBUTE_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_INDEX_NUM_TAG_NAME, ATTR_NEXT_INDEX_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_COLLECTION_NUM_TAG_NAME, ATTR_NEXT_COLLECTION_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_PREFIX_NUM_TAG_NAME, ATTR_NEXT_PREFIX_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_SOURCE_TAG_NAME, ATTR_SOURCE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_STATE_CHANGE_COUNT_TAG_NAME, ATTR_STATE_CHANGE_COUNT_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_XMLNS_TAG_NAME, ATTR_XMLNS_TAG, XFLM_TEXT_TYPE, NULL}, + {ATTR_ABSTRACT_TAG_NAME, ATTR_ABSTRACT_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_BASE_TAG_NAME, ATTR_BASE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_BLOCK_TAG_NAME, ATTR_BLOCK_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_DEFAULT_TAG_NAME, ATTR_DEFAULT_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_FINAL_TAG_NAME, ATTR_FINAL_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_FIXED_TAG_NAME, ATTR_FIXED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_ITEM_TYPE_TAG_NAME, ATTR_ITEM_TYPE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_MEMBER_TYPES_TAG_NAME, ATTR_MEMBER_TYPES_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_MIXED_TAG_NAME, ATTR_MIXED_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NILLABLE_TAG_NAME, ATTR_NILLABLE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_REF_TAG_NAME, ATTR_REF_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_USE_TAG_NAME, ATTR_USE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_VALUE_TAG_NAME, ATTR_VALUE_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_ADDRESS_TAG_NAME, ATTR_ADDRESS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_XMLNS_XFLAIM_TAG_NAME, ATTR_XMLNS_XFLAIM_TAG, XFLM_TEXT_TYPE, NULL}, + {ATTR_ENCRYPTION_KEY_TAG_NAME, ATTR_ENCRYPTION_KEY_TAG, XFLM_BINARY_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_TRANSACTION_TAG_NAME, ATTR_TRANSACTION_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_NEXT_ENCDEF_NUM_TAG_NAME, ATTR_NEXT_ENCDEF_NUM_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_ENCRYPTION_ID_TAG_NAME, ATTR_ENCRYPTION_ID_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_ENCRYPTION_KEY_SIZE_TAG_NAME, ATTR_ENCRYPTION_KEY_SIZE_TAG, XFLM_NUMBER_TYPE, gv_uzXFLAIMNamespace}, + {ATTR_UNIQUE_SUB_ELEMENTS_TAG_NAME, ATTR_UNIQUE_SUB_ELEMENTS_TAG, XFLM_TEXT_TYPE, gv_uzXFLAIMNamespace}, + {NULL, 0, 0, NULL} +}; + +FSTATIC void sortTagTbl( + FLM_TAG_INFO * * ppTagInfoTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + TAG_COMPARE_FUNC fnTagCompare); + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +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; + + // The m_bLoadedAllElements will be set to FALSE if we call addTag and + // there is no more room for elements. Same for m_bLoadedAllAttributes + // with respect to attributes. + + m_bLoadedAllElements = TRUE; + m_bLoadedAllAttributes = TRUE; + m_uiNumElementsLoaded = 0; + m_uiNumAttributesLoaded = 0; + m_ppuzNamespaces = NULL; + m_uiNamespaceTblSize = 0; + m_uiNumNamespaces = 0; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_NameTable::~F_NameTable() +{ + clearTable( 0); +} + +/**************************************************************************** +Desc: Setup name table. This routine should be called immediately after + the object is allocated. +****************************************************************************/ +RCODE F_NameTable::setupNameTable( void) +{ + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Free everything in the table +****************************************************************************/ +void 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; + m_bLoadedAllElements = TRUE; + m_bLoadedAllAttributes = TRUE; + m_uiNumElementsLoaded = 0; + m_uiNumAttributesLoaded = 0; +} + +/**************************************************************************** +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) + { + + // Elements and attributes need to also compare namespace + + if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + + // If the bMatchNamespace flag is FALSE, only search on + // name. If there are multiple elements or attributes + // with the same name, return an ambiguous flag. + + 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_XFLM_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_XFLM_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_XFLM_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_XFLM_CONV_ILLEGAL); + goto Exit; + } + + } + *pszDestTagName = 0; + *puiDestBufSize = uiCharCnt; + if (!bTruncatedNamesOk && *puzSrcTagName) + { + rc = RC_SET( NE_XFLM_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 if (pTagInfo1->uiType == ELM_ELEMENT_TAG || + pTagInfo1->uiType == ELM_ATTRIBUTE_TAG) + { + return( tagNameCompare( pTagInfo1->puzNamespace, NULL, + pTagInfo2->puzNamespace)); + } + else + { + return( 0); + } +} + +/*************************************************************************** +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_XFLM_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_XFLM_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; + + // If this is an element or attribute, set namespace and data type + + if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + 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_XFLM_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: Add the reserved dictionary tags to the name table. +****************************************************************************/ +RCODE F_NameTable::addReservedDictTags( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + + for (uiLoop = 0; FlmReservedElementTags [uiLoop].pszTagName; uiLoop++) + { + if (RC_BAD( rc = addTag( ELM_ELEMENT_TAG, NULL, + FlmReservedElementTags [uiLoop].pszTagName, + FlmReservedElementTags [uiLoop].uiTagNum, + FlmReservedElementTags [uiLoop].uiDataType, + FlmReservedElementTags [uiLoop].puzNamespace, FALSE, FALSE))) + { + goto Exit; + } + } + + for (uiLoop = 0; FlmReservedAttributeTags [uiLoop].pszTagName; uiLoop++) + { + if (RC_BAD( rc = addTag( ELM_ATTRIBUTE_TAG, NULL, + FlmReservedAttributeTags [uiLoop].pszTagName, + FlmReservedAttributeTags [uiLoop].uiTagNum, + FlmReservedAttributeTags [uiLoop].uiDataType, + FlmReservedAttributeTags [uiLoop].puzNamespace, FALSE, FALSE))) + { + goto Exit; + } + } + +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 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_XFLM_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 (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + if (puzNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, NULL, + &uiNamespaceBufSize, + pTagInfo->puzNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + } + else + { + flmAssert( !puiDataType && !puzNamespace); + } + + // 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_XFLM_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 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_XFLM_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 (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + if (puiDataType) + { + *puiDataType = pTagInfo->uiDataType; + } + if (puzNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, NULL, + &uiNamespaceBufSize, + pTagInfo->puzNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + } + else + { + flmAssert( !puiDataType && !puzNamespace); + } + + // 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_XFLM_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 F_NameTable::getFromTagTypeAndNum( + F_Db * pDb, + 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_XFLM_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 (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + 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 + { + flmAssert( !puiDataType && !puzNamespace && !pszNamespace); + } + } + else + { + + // If we do not have all of the tags cached, and this tag number is + // outside the range of tag numbers we read in, read the database + // definition document to get the information. + + if (pDb && + ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || + (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) + { + F_DataVector searchKey; + F_DataVector foundKey; + F_AttrElmInfo defInfo; + + // Need to lookup the document's ID, using the tag number + + if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) + { + goto Exit; + } + if (RC_BAD( rc = searchKey.setUINT( 1, uiTagNum))) + { + goto Exit; + } + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + if (RC_BAD( rc = pDb->getElmAttrInfo( uiType, + foundKey.getDocumentID(), &defInfo, TRUE, FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = defInfo.m_pNameAttr->getUnicode( + pDb, &puzTmpName))) + { + goto Exit; + } + + if( defInfo.m_pTargetNamespaceAttr) + { + if( RC_BAD( rc = defInfo.m_pTargetNamespaceAttr->getUnicode( + pDb, &puzTmpNamespace))) + { + goto Exit; + } + } + + if( puiDataType) + { + *puiDataType = defInfo.m_uiDataType; + } + + if (puzTagName || pszTagName) + { + if (RC_BAD( rc = copyTagName( puzTagName, pszTagName, + puiNameBufSize, + puzTmpName, bTruncatedNamesOk))) + { + goto Exit; + } + } + else if (puiNameBufSize) + { + *puiNameBufSize = f_unilen( puzTmpName); + } + + if (puzNamespace || pszNamespace) + { + if (RC_BAD( rc = copyTagName( puzNamespace, pszNamespace, + puiNamespaceBufSize, puzTmpNamespace, + bTruncatedNamesOk))) + { + goto Exit; + } + } + else if (puiNamespaceBufSize) + { + *puiNamespaceBufSize = f_unilen( puzTmpNamespace); + } + } + else + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + } + +Exit: + + if (puzTmpName) + { + f_free( &puzTmpName); + } + + if (puzTmpNamespace) + { + f_free( &puzTmpNamespace); + } + + return( rc); +} + +/**************************************************************************** +Desc: Find a name of a particular type and determine if it is ambiguous. +****************************************************************************/ +FSTATIC RCODE findTagName( + F_Db * pDb, + FLMUINT uiType, + const FLMUNICODE * puzTagName, + const char * pszTagName, + F_DataVector * pSrchKey, + FLMUINT * puiDictNum, + FLMUINT64 * pui64DocumentID) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector foundKey; + F_DataVector foundKey2; + FLMUINT uiFoundType; + FLMUNICODE * puzTmpName = NULL; + + // Node type and namespace are unknown, find the first name + // that matches. + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + pSrchKey, XFLM_INCL, &foundKey))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + } + goto Exit; + } + + if (RC_BAD( rc = foundKey.getUINT( 0, &uiFoundType))) + { + goto Exit; + } + + // Make sure we are still on the right kind of definition. + + if (uiFoundType != uiType) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + // Verify that we landed on a key that has the name we + // were searching for. Name is in component [1] of the key. + + if (RC_BAD( rc = foundKey.getUnicode( 1, &puzTmpName))) + { + goto Exit; + } + if (tagNameCompare( puzTagName, pszTagName, puzTmpName) != 0) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + // Get the document ID + + *pui64DocumentID = foundKey.getDocumentID(); + + // Get the dictionary number from the data part of the key + + if (RC_BAD( rc = foundKey.getUINT( 3, puiDictNum))) + { + goto Exit; + } + + // Determine if there is more than one of that key. + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, &foundKey, + XFLM_EXCL | XFLM_MATCH_IDS, &foundKey2))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = foundKey2.getUINT( 0, &uiFoundType))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // Make sure we are still on the right kind of definition. + + if (uiFoundType != uiType) + { + goto Exit; // will return NE_XFLM_OK + } + + // Verify that we landed on a key that has a different name than we + // were searching for. Name is in component [1] of the key. + + if (RC_BAD( rc = foundKey2.getUnicode( 1, &puzTmpName))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + if (tagNameCompare( puzTagName, pszTagName, puzTmpName) != 0) + { + goto Exit; // will return NE_XFLM_OK + } + + rc = RC_SET( NE_XFLM_MULTIPLE_MATCHES); + goto Exit; + } + +Exit: + + if (puzTmpName) + { + f_free( &puzTmpName); + } + + 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 F_NameTable::getFromTagTypeAndName( + F_Db * pDb, + 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_XFLM_OK; + FLM_TAG_INFO * pTagInfo; + FLMUINT uiDictNum; + FLMBOOL bAmbiguous; + FLMUINT uiTmpDictNum; + FLMUINT64 ui64DocumentID; + F_DataVector searchKey; + F_DataVector foundKey; + + if ((pTagInfo = findTagByTypeAndName( uiType, + puzTagName, pszTagName, bMatchNamespace, + puzNamespace, + &bAmbiguous)) != NULL) + { + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if (puiDataType) + { + *puiDataType = (FLMUINT)(uiType == ELM_ELEMENT_TAG || + uiType == ELM_ATTRIBUTE_TAG + ? pTagInfo->uiDataType + : 0); + } + if (bAmbiguous) + { + rc = RC_SET( NE_XFLM_MULTIPLE_MATCHES); + goto Exit; + } + else if (pDb && !bMatchNamespace && + ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || + (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) + { + + // Must see if it is ambiguous - create a search key + + if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) + { + goto Exit; + } + + // Put name into the key. + + if (puzTagName) + { + if (RC_BAD( rc = searchKey.setUnicode( 1, puzTagName))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszTagName))) + { + goto Exit; + } + } + + if (RC_BAD( rc = findTagName( pDb, uiType, puzTagName, pszTagName, + &searchKey, &uiTmpDictNum, &ui64DocumentID))) + { + + // Better be found at this point because it was in the + // name table! Error may be NE_XFLM_MULTIPLE_MATCHES. + + flmAssert( rc != NE_XFLM_NOT_FOUND); + goto Exit; + } + + // What we found in the index better match what we found in + // the table! + + flmAssert( uiTmpDictNum == *puiTagNum); + } + } + else + { + + // If we do not have all of the tags cached, and we did not read in + // all of the elements or attributes, read the dictionary name index + // to see if we can find this tag name. NOTE that if bMatchNamespace == + // FALSE we will simply find the first tag name that + // matches and not worry about matching the namespace. + + if (pDb && + ((uiType == ELM_ELEMENT_TAG && !m_bLoadedAllElements) || + (uiType == ELM_ATTRIBUTE_TAG && !m_bLoadedAllAttributes))) + { + F_AttrElmInfo defInfo; + + // Create a search key + + if (RC_BAD( rc = searchKey.setUINT( 0, uiType))) + { + goto Exit; + } + + if (puzTagName) + { + if (RC_BAD( rc = searchKey.setUnicode( 1, puzTagName))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = searchKey.setUTF8( 1, (FLMBYTE *)pszTagName))) + { + goto Exit; + } + } + if (!bMatchNamespace) + { + if (RC_BAD( rc = findTagName( pDb, uiType, puzTagName, pszTagName, + &searchKey, &uiDictNum, &ui64DocumentID))) + { + goto Exit; + } + } + else + { + // Add the namespace as a child node. + + if (puzNamespace) + { + if (RC_BAD( rc = searchKey.setUnicode( 2, puzNamespace))) + { + goto Exit; + } + } + + // Search for this exact key. + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NAME_INDEX, + &searchKey, XFLM_EXACT, &foundKey))) + { + goto Exit; + } + + ui64DocumentID = foundKey.getDocumentID(); + + // Data component [0]'s value will be the dictionary number + + if (RC_BAD( rc = foundKey.getUINT( 3, &uiDictNum))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + flmAssert( 0); + uiDictNum = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pDb->getElmAttrInfo( uiType, + ui64DocumentID, &defInfo, TRUE, FALSE))) + { + goto Exit; + } + + if( puiDataType) + { + *puiDataType = defInfo.m_uiDataType; + } + + if (puiTagNum) + { + *puiTagNum = uiDictNum; + } + } + else + { + rc = RC_SET( NE_XFLM_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_XFLM_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 F_NameTable::addTag( + FLMUINT uiType, + FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiDataType, + FLMUNICODE * puzNamespace, + FLMBOOL bCheckDuplicates, + FLMBOOL bLimitNumToLoad) +{ + RCODE rc = NE_XFLM_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_XFLM_INVALID_PARM); + goto Exit; + } + + // Tag number of zero not allowed. + + if (!uiTagNum) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_EXISTS); + goto Exit; + } + + // Make sure that the tag type + number is not already used. + + if (findTagByTypeAndNum( uiType, uiTagNum, + &uiTagTypeAndNumTblInsertPos)) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + } + else + { + uiTagTypeAndNameTblInsertPos = + uiTagTypeAndNumTblInsertPos = m_uiNumTags; + m_bTablesSorted = FALSE; + } + + if (uiType == ELM_ELEMENT_TAG) + { + if (m_uiNumElementsLoaded >= MAX_ELEMENTS_TO_LOAD && + bLimitNumToLoad) + { + + // We purposely limit the number of elements that can be + // loaded into the table. + + m_bLoadedAllElements = FALSE; + goto Exit; // Will return NE_XFLM_OK + } + } + else if (uiType == ELM_ATTRIBUTE_TAG) + { + + if (m_uiNumAttributesLoaded >= MAX_ATTRIBUTES_TO_LOAD && + bLimitNumToLoad) + { + + // We purposely limit the number of elements that can be + // loaded into the table. + + m_bLoadedAllAttributes = FALSE; + goto Exit; // Will return NE_XFLM_OK + } + } + + // 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; + } + + if (uiType == ELM_ELEMENT_TAG) + { + m_uiNumElementsLoaded++; + } + else if (uiType == ELM_ATTRIBUTE_TAG) + { + m_uiNumAttributesLoaded++; + } + +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 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) + { + if (uiType == ELM_ELEMENT_TAG || uiType == ELM_ATTRIBUTE_TAG) + { + puzNamespace = pTagInfo->puzNamespace; + bMatchNamespace = TRUE; + } + else + { + bMatchNamespace = FALSE; // Really doesn't matter + puzNamespace = NULL; + } + + 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 +****************************************************************************/ +RCODE F_NameTable::cloneNameTable( + F_NameTable * pSrcNameTable + ) +{ + RCODE rc = NE_XFLM_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 (pTagInfo->uiType == ELM_ELEMENT_TAG || + pTagInfo->uiType == ELM_ATTRIBUTE_TAG) + { + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, + pTagInfo->uiDataType, + pTagInfo->puzNamespace, FALSE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, 0, NULL, FALSE))) + { + goto Exit; + } + } + } + + sortTags(); + +Exit: + + return( rc); +} + +/**************************************************************************** +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. +****************************************************************************/ +RCODE F_NameTable::importFromNameTable( + F_NameTable * pSrcNameTable + ) +{ + RCODE rc = NE_XFLM_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 (pTagInfo->uiType == ELM_ELEMENT_TAG || + pTagInfo->uiType == ELM_ATTRIBUTE_TAG) + { + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, + pTagInfo->uiDataType, + pTagInfo->puzNamespace, FALSE))) + { + if (rc != NE_XFLM_EXISTS) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + } + else + { + if (RC_BAD( rc = addTag( pTagInfo->uiType, pTagInfo->puzTagName, NULL, + pTagInfo->uiTagNum, 0, NULL, FALSE))) + { + if (rc != NE_XFLM_EXISTS) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + } + } + + sortTags(); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Increment use count on this object. +****************************************************************************/ +FLMUINT32 XFLMAPI F_NameTable::AddRef( void) +{ + return( (FLMUINT32)(ftkAtomicIncrement( &m_ui32RefCnt))); +} + +/**************************************************************************** +Desc: Decrement the use count and delete if use count goes to zero. +****************************************************************************/ +FLMUINT32 XFLMAPI F_NameTable::Release( void) +{ + FLMUINT32 ui32RefCnt; + + if ((ui32RefCnt = (FLMUINT32)ftkAtomicDecrement( &m_ui32RefCnt)) == 0) + { + delete this; + } + + return( ui32RefCnt); +} + +/**************************************************************************** +Desc: Get the name table for an FDB, if any. If it has no current + dictionary, get the name table for the FDB's FFILE's first dictionary, + if any. Increment the use count on any name table returned. + It is the caller's responsibility to release the name table when it + is done with it. +****************************************************************************/ +RCODE F_Db::getNameTable( + F_NameTable ** ppNameTable + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + + *ppNameTable = NULL; + if (m_pDict) + { + if ((*ppNameTable = m_pDict->getNameTable()) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_NAME_TABLE); + goto Exit; + } + (*ppNameTable)->AddRef(); + } + else + { + m_pDatabase->lockMutex(); + bMutexLocked = TRUE; + + if (m_pDatabase && m_pDatabase->m_pDictList) + { + if ((*ppNameTable = m_pDatabase->m_pDictList->getNameTable()) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_NAME_TABLE); + goto Exit; + } + (*ppNameTable)->AddRef(); + } + else + { + rc = RC_SET( NE_XFLM_NO_NAME_TABLE); + goto Exit; + } + } + +Exit: + + if (bMutexLocked) + { + m_pDatabase->unlockMutex(); + } + return( rc); +} + +/**************************************************************************** +Desc: Get the name for a dictionary item. +****************************************************************************/ +RCODE XFLMAPI F_Db::getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + char * pszName, + FLMUINT * puiNameBufSize, + char * pszNamespace, + FLMUINT * puiNamespaceBufSize + ) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + + if (RC_BAD(rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if (pszNamespace && + (uiDictType == ELM_ELEMENT_TAG || uiDictType == ELM_ATTRIBUTE_TAG)) + { + flmAssert( puiNamespaceBufSize); + } + else + { + flmAssert( !pszNamespace && !puiNamespaceBufSize); + pszNamespace = NULL; + puiNamespaceBufSize = NULL; + } + + if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( this, uiDictType, + uiDictNumber, NULL, + pszName, puiNameBufSize, NULL, + NULL, pszNamespace, + puiNamespaceBufSize, TRUE))) + { + goto Exit; + } + +Exit: + + if (pNameTable) + { + pNameTable->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Get the name for a dictionary item. +****************************************************************************/ +RCODE XFLMAPI F_Db::getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + FLMUNICODE * puzName, + FLMUINT * puiNameBufSize, + FLMUNICODE * puzNamespace, + FLMUINT * puiNamespaceBufSize + ) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + + if (RC_BAD(rc = getNameTable( &pNameTable))) + { + goto Exit; + } + + if (uiDictType == ELM_ELEMENT_TAG || uiDictType == ELM_ATTRIBUTE_TAG) + { + flmAssert( puiNamespaceBufSize); + } + else + { + flmAssert( !puiNamespaceBufSize); + puiNamespaceBufSize = NULL; + } + + if (RC_BAD( rc = pNameTable->getFromTagTypeAndNum( this, uiDictType, + uiDictNumber, puzName, + NULL, puiNameBufSize, NULL, + puzNamespace, NULL, + puiNamespaceBufSize, TRUE))) + { + goto Exit; + } + +Exit: + + if (pNameTable) + { + pNameTable->Release(); + } + + return( rc); +} diff --git a/version5/src/fnumber.cpp b/version5/src/fnumber.cpp new file mode 100644 index 0000000..ad76302 --- /dev/null +++ b/version5/src/fnumber.cpp @@ -0,0 +1,1001 @@ +//------------------------------------------------------------------------------ +// Desc: Routines that do conversions between internal number and numeric +// key format to platform number types. +// +// 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: fnumber.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#define DEFINE_NUMBER_MAXIMUMS +#include "flaimsys.h" + +/**************************************************************************** +Desc: Converts a UINT to its storage value +*****************************************************************************/ +RCODE FlmUINT2Storage( + FLMUINT uiNum, + FLMUINT * puiBufLen, // In (buffer size) / Out (bytes used) + FLMBYTE * pucBuf) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = flmNumber64ToStorage( uiNum, puiBufLen, + pucBuf, FALSE, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts an INT to its storage value +*****************************************************************************/ +RCODE FlmINT2Storage( + FLMINT iNum, + FLMUINT * puiBufLen, // In (buffer size) / Out (bytes used) + FLMBYTE * pucBuf) +{ + FLMBOOL bNeg = FALSE; + RCODE rc = NE_XFLM_OK; + + if( iNum < 0) + { + iNum = -iNum; + bNeg = TRUE; + } + + if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, + puiBufLen, pucBuf, bNeg, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a number to its storage or collation format +Notes: Changes to this code must also be made to flmUINT32ToStorage +*****************************************************************************/ +RCODE flmNumber64ToStorage( + FLMUINT64 ui64Num, + FLMUINT * puiBufLen, + FLMBYTE * pucBuf, + FLMBOOL bNegative, + FLMBOOL bCollation) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiByteCount = 0; + FLMUINT32 ui32Low = (FLMUINT32)ui64Num; + FLMUINT32 ui32High = (FLMUINT32)(ui64Num >> 32); + + if( *puiBufLen < FLM_MAX_NUM_BUF_SIZE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Negative flag should not be set if the number is 0 + + if( !ui64Num && bNegative) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Build the storage format, either for an index key (if bCollation + // is TRUE) or for a data element. + + if( !bCollation) + { + // Build the data storage representation of the number + // Numbers are stored in a little-endian format + + do + { + *pucBuf++ = (FLMBYTE)ui32Low; + ui32Low >>= 8; + uiByteCount++; + } while (ui32Low); + + if( ui32High) + { + for( ; uiByteCount < 4; uiByteCount++) + { + *pucBuf++ = 0; + } + + for( ; ui32High; uiByteCount++) + { + *pucBuf++ = (FLMBYTE)ui32High; + ui32High >>= 8; + } + } + + // If a number is negative, the high bit of the right-most + // byte needs to be set. If the value of the number is such + // that its positive representation already has the high-bit + // set, we need to store an additional sign byte. + + if( !bNegative) + { + if( *(pucBuf - 1) & 0x80) + { + *pucBuf++ = 0; + uiByteCount++; + } + } + else + { + if( (*(pucBuf - 1) & 0x80) == 0) + { + *(pucBuf - 1) |= 0x80; + } + else + { + *pucBuf++ = 0x80; + uiByteCount++; + } + } + } + else + { + FLMBYTE * pucStart = pucBuf++; + + if( ui32High) + { + if( ui32High & 0xFF000000) + { + *pucBuf++ = (FLMBYTE)(ui32High >> 24); + *pucBuf++ = (FLMBYTE)(ui32High >> 16); + *pucBuf++ = (FLMBYTE)(ui32High >> 8); + *pucBuf++ = (FLMBYTE)ui32High; + uiByteCount += 4; + } + else if( ui32High & 0x00FF0000) + { + *pucBuf++ = (FLMBYTE)(ui32High >> 16); + *pucBuf++ = (FLMBYTE)(ui32High >> 8); + *pucBuf++ = (FLMBYTE)ui32High; + uiByteCount += 3; + } + else if( ui32High & 0x0000FF00) + { + *pucBuf++ = (FLMBYTE)(ui32High >> 8); + *pucBuf++ = (FLMBYTE)ui32High; + uiByteCount += 2; + } + else if( ui32High) + { + *pucBuf++ = (FLMBYTE)ui32High; + uiByteCount++; + } + } + + if( ui32Low) + { + if( ui32Low & 0xFF000000) + { + *pucBuf++ = (FLMBYTE)(ui32Low >> 24); + *pucBuf++ = (FLMBYTE)(ui32Low >> 16); + *pucBuf++ = (FLMBYTE)(ui32Low >> 8); + *pucBuf++ = (FLMBYTE)ui32Low; + uiByteCount += 4; + } + else if( ui32Low & 0x00FF0000) + { + *pucBuf++ = (FLMBYTE)(ui32Low >> 16); + *pucBuf++ = (FLMBYTE)(ui32Low >> 8); + *pucBuf++ = (FLMBYTE)ui32Low; + uiByteCount += 3; + } + else if( ui32Low & 0x0000FF00) + { + *pucBuf++ = (FLMBYTE)(ui32Low >> 8); + *pucBuf++ = (FLMBYTE)ui32Low; + uiByteCount += 2; + } + else if( ui32Low) + { + *pucBuf++ = (FLMBYTE)ui32Low; + uiByteCount++; + } + } + else if( !ui32High) + { + *pucBuf++ = 0; + uiByteCount++; + } + + if( !bNegative) + { + // Positive numbers must collate after negative numbers, + // so all positive numbers will start with a byte + // in the range of 0xC8 - 0xCF. + + *pucStart = (FLMBYTE)(0xC8 + (uiByteCount - 1)); + uiByteCount++; + } + else + { + FLMBYTE * pucTmp = pucStart + 1; + + while( pucTmp < pucBuf) + { + *pucTmp = ~(*pucTmp); + pucTmp++; + } + + // Negative numbers must collate before positive numbers, + // so all negative numbers will start with a byte + // in the range of 0xC0 - 0xC7. + + *pucStart = (FLMBYTE)(0xC8 - uiByteCount); + uiByteCount++; + } + } + + // Set the number of bytes in the buffer before returning. + + *puiBufLen = uiByteCount; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a storage value back into a number +Notes: Changes to this code must also be made to flmStorage2Number64 +*****************************************************************************/ +RCODE flmStorage2Number( + FLMUINT uiType, + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT * puiNum, + FLMINT * piNum) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT uiNum = 0; + FLMBOOL bNeg = FALSE; + + if( !uiBufLen) + { + if( puiNum) + { + *puiNum = 0; + } + else + { + *piNum = 0; + } + goto Exit; + } + + if( !pucBuf) + { + rc = RC_SET( NE_XFLM_CONV_NULL_SRC); + goto Exit; + } + + switch( uiType) + { + case XFLM_NUMBER_TYPE : + { + // Make sure the number buffer does not exceed the + // max length. If there is an extra byte for the + // sign (byte 9) make sure it has a value of either + // 0x80 or 0 (by masking of the high bit). + + if( uiBufLen > FLM_MAX_NUM_BUF_SIZE || + (uiBufLen == FLM_MAX_NUM_BUF_SIZE && + (pucBuf[ uiBufLen - 1] & 0x7F) != 0)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + // Look at the high bit of the most-significant byte + // to determine if the number is signed + + if( pucBuf[ uiBufLen - 1] & 0x80) + { + if( puiNum) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + bNeg = TRUE; + } + + uiNum = pucBuf[ uiBufLen - 1] & 0x7F; + uiBufLen--; + + for( uiLoop = 1; uiLoop <= uiBufLen; uiLoop++) + { + if( gv_b32BitPlatform && (uiNum & 0xFF000000)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + uiNum = (uiNum << 8) + pucBuf[ uiBufLen - uiLoop]; + } + + break; + } + + case XFLM_TEXT_TYPE: + { + FLMBYTE ucNumBuf[ 64]; + FLMUINT uiNumBufLen = sizeof( ucNumBuf); + FLMBYTE * pucTmp; + + if( RC_BAD( rc = flmStorage2UTF8( XFLM_TEXT_TYPE, uiBufLen, pucBuf, + &uiNumBufLen, ucNumBuf))) + { + goto Exit; + } + + pucTmp = &ucNumBuf[ 0]; + if( *pucTmp == ASCII_DASH) + { + if( puiNum) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + bNeg = TRUE; + pucTmp++; + } + + while( *pucTmp) + { + if( *pucTmp < ASCII_ZERO || *pucTmp > ASCII_NINE) + { + break; + } + + if( uiNum > (~(FLMUINT)0) / 10) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + uiNum *= (FLMUINT)10; + + if( uiNum > (~(FLMUINT)0) - (FLMUINT)(*pucTmp - ASCII_ZERO)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + uiNum += (FLMUINT)(*pucTmp - ASCII_ZERO); + pucTmp++; + } + + break; + } + + default : + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + if( puiNum) + { + if( bNeg) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + + *puiNum = uiNum; + } + else + { + flmAssert( piNum); + + if( bNeg) + { + if( uiNum > gv_uiMaxSignedIntVal + 1) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + + *piNum = -(FLMINT)uiNum; + } + else + { + if( uiNum > gv_uiMaxSignedIntVal) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + *piNum = (FLMINT)uiNum; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a storage value back into a number +Notes: Changes to this code must also be made to flmStorage2Number +*****************************************************************************/ +RCODE flmStorage2Number64( + FLMUINT uiType, + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT64 * pui64Num, + FLMINT64 * pi64Num) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT64 ui64Num = 0; + FLMBOOL bNeg = FALSE; + + if( !uiBufLen) + { + if( pui64Num) + { + *pui64Num = 0; + } + else + { + *pi64Num = 0; + } + goto Exit; + } + + if( !pucBuf) + { + rc = RC_SET( NE_XFLM_CONV_NULL_SRC); + goto Exit; + } + + switch( uiType) + { + case XFLM_NUMBER_TYPE : + { + // Make sure the number buffer does not exceed the + // max length. If there is an extra byte for the + // sign (byte 9) make sure it has a value of either + // 0x80 or 0 (by masking of the high bit). + + if( uiBufLen > FLM_MAX_NUM_BUF_SIZE || + (uiBufLen == FLM_MAX_NUM_BUF_SIZE && + (pucBuf[ uiBufLen - 1] & 0x7F) != 0)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + // Look at the high bit of the most-significant byte + // to determine if the number is signed + + if( pucBuf[ uiBufLen - 1] & 0x80) + { + if( pui64Num) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + bNeg = TRUE; + } + + ui64Num = pucBuf[ uiBufLen - 1] & 0x7F; + uiBufLen--; + + for( uiLoop = 1; uiLoop <= uiBufLen; uiLoop++) + { + ui64Num = (ui64Num << 8) + pucBuf[ uiBufLen - uiLoop]; + } + + break; + } + + case XFLM_TEXT_TYPE : + { + FLMBYTE ucNumBuf[ 64]; + FLMUINT uiNumBufLen = sizeof( ucNumBuf); + FLMBYTE * pucTmp; + + if( RC_BAD( rc = flmStorage2UTF8( XFLM_TEXT_TYPE, uiBufLen, pucBuf, + &uiNumBufLen, ucNumBuf))) + { + goto Exit; + } + + pucTmp = &ucNumBuf[ 0]; + + if( *pucTmp == ASCII_DASH) + { + if( pui64Num) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + bNeg = TRUE; + pucTmp++; + } + + while( *pucTmp) + { + if( *pucTmp < ASCII_ZERO || *pucTmp > ASCII_NINE) + { + break; + } + + if( ui64Num > (~(FLMUINT64)0) / 10) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num *= (FLMUINT64)10; + + if( ui64Num > (~(FLMUINT64)0) - (FLMUINT64)(*pucTmp - ASCII_ZERO)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num += (FLMUINT64)(*pucTmp - ASCII_ZERO); + pucTmp++; + } + + break; + } + + default : + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + if( pui64Num) + { + *pui64Num = ui64Num; + } + else + { + flmAssert( pi64Num); + + if( bNeg) + { + if( ui64Num > gv_ui64MaxSignedIntVal + 1) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + + *pi64Num = -(FLMINT64)ui64Num; + } + else + { + if( ui64Num > gv_ui64MaxSignedIntVal) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + *pi64Num = (FLMINT64)ui64Num; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a numeric storage value into a collation value +*****************************************************************************/ +RCODE flmStorageNum2CollationNum( + const FLMBYTE * pucStorageBuf, + FLMUINT uiStorageLen, + FLMBYTE * pucCollBuf, + FLMUINT * puiCollLen) +{ + FLMUINT uiLoop; + FLMUINT uiOffset; + FLMUINT uiMaxLen = *puiCollLen; + FLMBYTE ucVal; + FLMBOOL bNegative = FALSE; + RCODE rc = NE_XFLM_OK; + + if( !pucStorageBuf || !uiStorageLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + // Look at the high bit of the most-significant byte + // to determine if the number is signed + + if( pucStorageBuf[ uiStorageLen - 1] & 0x80) + { + bNegative = TRUE; + } + + uiOffset = 1; + if( (ucVal = pucStorageBuf[ uiStorageLen - 1] & 0x7F) != 0 || + uiStorageLen == 1) // Handle the special case of zero + { + if( bNegative) + { + ucVal = ~ucVal; + } + + if( uiOffset >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucCollBuf[ uiOffset++] = ucVal; + } + uiStorageLen--; + + // Check for overflow + + if( uiOffset + uiStorageLen >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Map the little-endian storage format to the big-endian + // collation format + + if( !bNegative) + { + for( uiLoop = 1; uiLoop <= uiStorageLen; uiLoop++) + { + pucCollBuf[ uiOffset++] = pucStorageBuf[ uiStorageLen - uiLoop]; + } + } + else + { + for( uiLoop = 1; uiLoop <= uiStorageLen; uiLoop++) + { + pucCollBuf[ uiOffset++] = ~pucStorageBuf[ uiStorageLen - uiLoop]; + } + } + + flmAssert( uiOffset >= 2); + + // Store the numeric collation marker and byte count + + if( !bNegative) + { + // Positive numbers must collate after negative numbers, + // so all positive numbers will start with a byte + // in the range of 0xC8 - 0xCF. + + pucCollBuf[ 0] = (FLMBYTE)(0xC8 + (uiOffset - 2)); + } + else + { + // Negative numbers must collate before positive numbers, + // so all negative numbers will start with a byte + // in the range of 0xC0 - 0xC7. + + pucCollBuf[ 0] = (FLMBYTE)(0xC8 - (uiOffset - 1)); + } + + // Set the key length + + *puiCollLen = uiOffset; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a numeric collation value into a storage value +*****************************************************************************/ +RCODE flmCollationNum2StorageNum( + const FLMBYTE * pucCollBuf, + FLMUINT uiCollLen, + FLMBYTE * pucStorageBuf, + FLMUINT * puiStorageLen) +{ + FLMUINT uiLoop; + FLMUINT uiOffset; + FLMUINT uiMaxOffset = *puiStorageLen; + FLMUINT uiNumKeyBytes; + FLMBOOL bNegative = FALSE; + RCODE rc = NE_XFLM_OK; + + if( !pucCollBuf || !uiCollLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + // Make sure this looks like a valid numeric key piece + + if( (pucCollBuf[ 0] & 0xC0) != 0xC0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Get the byte count + + if( (uiNumKeyBytes = (FLMUINT)(pucCollBuf[ 0] & 0x0F)) >= 8) + { + uiNumKeyBytes -= 7; + } + else + { + uiNumKeyBytes = 8 - uiNumKeyBytes; + bNegative = TRUE; + } + pucCollBuf++; + uiCollLen--; + + // Make sure the buffer has at least the number of bytes + // we need + + if( uiCollLen != uiNumKeyBytes) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( uiNumKeyBytes >= uiMaxOffset) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Translate the collation value into a numeric value + + if( !bNegative) + { + for( uiLoop = 0; uiLoop < uiNumKeyBytes; uiLoop++) + { + pucStorageBuf[ uiNumKeyBytes - uiLoop - 1] = pucCollBuf[ uiLoop]; + } + } + else + { + for( uiLoop = 0; uiLoop < uiNumKeyBytes; uiLoop++) + { + pucStorageBuf[ uiNumKeyBytes - uiLoop - 1] = ~pucCollBuf[ uiLoop]; + } + } + + uiOffset = uiNumKeyBytes; + + // If a number is negative, the high bit of the right-most + // byte needs to be set. If the value of the number is such + // that its positive representation already has the high-bit + // set, we need to store an additional sign byte. + + if( !bNegative) + { + if( pucStorageBuf[ uiOffset - 1] & 0x80) + { + if( uiOffset >= uiMaxOffset) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucStorageBuf[ uiOffset++] = 0; + } + } + else + { + if( (pucStorageBuf[ uiOffset - 1] & 0x80) == 0) + { + pucStorageBuf[ uiOffset - 1] |= 0x80; + } + else + { + if( uiOffset >= uiMaxOffset) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pucStorageBuf[ uiOffset++] = 0x80; + } + } + + // Set the storage length + + *puiStorageLen = uiOffset; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a collation value (numeric only) to a number +*****************************************************************************/ +RCODE flmCollation2Number( + FLMUINT uiBufLen, + const FLMBYTE * pucBuf, + FLMUINT64 * pui64Num, + FLMBOOL * pbNeg, + FLMUINT * puiBytesProcessed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT uiNumBytes; + FLMUINT64 ui64Num; + FLMBOOL bNeg = FALSE; + + *pui64Num = 0; + + if( !uiBufLen) + { + goto Exit; + } + + if( !pucBuf) + { + rc = RC_SET( NE_XFLM_CONV_NULL_SRC); + goto Exit; + } + + // Make sure this looks like a valid numeric key piece + + if( (pucBuf[ 0] & 0xC0) != 0xC0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Get the byte count + + if( (uiNumBytes = (FLMUINT)(pucBuf[ 0] & 0x0F)) >= 8) + { + uiNumBytes -= 7; + } + else + { + uiNumBytes = 8 - uiNumBytes; + bNeg = TRUE; + } + pucBuf++; + uiBufLen--; + + // Make sure the buffer has at least the number of bytes + // we need + + if( uiBufLen < uiNumBytes) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Reconstruct the number + + ui64Num = 0; + if( !bNeg) + { + for( uiLoop = 0; uiLoop < uiNumBytes; uiLoop++) + { + ui64Num += (((FLMUINT64)pucBuf[ uiLoop]) << + (8 * ((uiNumBytes - uiLoop) - 1))); + } + } + else + { + for( uiLoop = 0; uiLoop < uiNumBytes; uiLoop++) + { + ui64Num += (((FLMUINT64)((FLMBYTE)~pucBuf[ uiLoop])) << + (8 * ((uiNumBytes - uiLoop) - 1))); + } + } + + *pui64Num = ui64Num; + + if( puiBytesProcessed) + { + *puiBytesProcessed = uiNumBytes + 1; + } + + if( pbNeg) + { + *pbNeg = bNeg; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmStorageNumberToNumber( + const FLMBYTE * pucNumBuf, + FLMUINT uiNumBufLen, + FLMUINT64 * pui64Number, + FLMBOOL * pbNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT64 ui64Num = 0; + FLMBOOL bNeg = FALSE; + + if( !uiNumBufLen) + { + goto Exit; + } + + // Make sure the number buffer does not exceed the + // max length. If there is an extra byte for the + // sign (byte 9) make sure it has a value of either + // 0x80 or 0 (by masking of the high bit). + + if( uiNumBufLen > FLM_MAX_NUM_BUF_SIZE || + (uiNumBufLen == FLM_MAX_NUM_BUF_SIZE && + (pucNumBuf[ uiNumBufLen - 1] & 0x7F) != 0)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + // Look at the high bit of the most-significant byte + // to determine if the number is signed + + if( pucNumBuf[ uiNumBufLen - 1] & 0x80) + { + bNeg = TRUE; + } + + ui64Num = pucNumBuf[ uiNumBufLen - 1] & 0x7F; + uiNumBufLen--; + + for( uiLoop = 1; uiLoop <= uiNumBufLen; uiLoop++) + { + ui64Num = (ui64Num << 8) + pucNumBuf[ uiNumBufLen - uiLoop]; + } + +Exit: + + *pui64Number = ui64Num; + *pbNeg = bNeg; + + return( rc); +} diff --git a/version5/src/fposix.cpp b/version5/src/fposix.cpp new file mode 100644 index 0000000..0996a66 --- /dev/null +++ b/version5/src/fposix.cpp @@ -0,0 +1,1676 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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(__GNUC__) && defined(__i386__) +__attribute__((always_inline)) +static FINLINE unsigned int atomic_inc(volatile unsigned int * p) +{ + unsigned int rv; + + __asm__ __volatile__( + "movl $1, %%eax\n\t" + "lock\n\t" + "xaddl %%eax, (%%ecx)\n\t" + "incl %%eax" + : "=a" (rv) + : "c" (p) + ); + + return rv; +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined(__GNUC__) && defined(__i386__) +FLMUINT32 ftkAtomicIncrement( + FLMUINT32 * pui32Target) +{ + return( atomic_inc( pui32Target)); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined(__GNUC__) && defined(__i386__) +__attribute__((always_inline)) +static FINLINE unsigned int atomic_dec(volatile unsigned int * p) +{ + unsigned int rv; + + __asm__ __volatile__( + "movl $-1, %%eax\n\t" + "lock\n\t" + "xaddl %%eax, (%%ecx)\n\t" + "decl %%eax" + : "=a" (rv) + : "c" (p) + ); // result left in eax + + return rv; +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined(__GNUC__) && defined(__i386__) +FLMUINT32 ftkAtomicDecrement( + FLMUINT32 * pui32Target) +{ + return( atomic_dec( pui32Target)); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined(__GNUC__) && defined(__i386__) +__attribute__((always_inline)) +static FINLINE unsigned int atomic_xchg( + volatile unsigned int * p, + unsigned int i) +{ + unsigned int rv; + + __asm__ __volatile__( + "xchgl %%eax, (%%ecx)" + : "=a" (rv) + : "c" (p), "a" (i) + ); // result left in eax, no buslock required for xchgl + + return rv; +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined(__GNUC__) && defined(__i386__) +FLMUINT32 ftkAtomicExchange( + FLMUINT32 * puiTarget, + FLMUINT32 ui32Value) +{ + return( atomic_xchg( puiTarget, ui32Value)); +} +#endif + + +#elif defined( FLM_WATCOM_NLM) + int fposixDummy(void) + { + return( 0); + } +#endif diff --git a/version5/src/fposix.h b/version5/src/fposix.h new file mode 100644 index 0000000..2d3e23a --- /dev/null +++ b/version5/src/fposix.h @@ -0,0 +1,270 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's POSIX +// FileHdl classes. +// +// 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: fposix.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FPOSIX_H +#define FPOSIX_H + +#ifdef FLM_UNIX + +class F_FileHdl : public IF_FileHdl, public XF_Base +{ +public: + + F_FileHdl(); + + ~F_FileHdl(); + + RCODE XFLMAPI Setup( + FLMUINT uiFileId); + + RCODE XFLMAPI Close( void); + + RCODE XFLMAPI Create( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE XFLMAPI CreateUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags); + + RCODE XFLMAPI Open( + const char * pszFileName, + FLMUINT uiIoFlags); + + RCODE XFLMAPI Flush( void); + + RCODE XFLMAPI Read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE XFLMAPI Seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset); + + RCODE XFLMAPI Size( + FLMUINT64 * pui64Size); + + RCODE XFLMAPI Tell( + FLMUINT64 * pui64Offset); + + RCODE XFLMAPI Truncate( + FLMUINT64 ui64Size); + + RCODE XFLMAPI Write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + const void * pvBuffer, + FLMUINT * puiBytesWritten); + + // Some I/O subsystems (such as direct IO) can only read and + // write sectors. If uiOffset is not on a sector boundary or + // uiLength is not an exact multiple of a sector size, the I/O system + // would have to try to read or write a partial sector - something that + // requires extra overhead, particularly for write operations - because + // in order to write a partial sector, the I/O subsystem first has to + // read the sector in to memory before writing it out in order to + // preserve the part of the sector that was not being written to. + + // The SectorRead and SectorWrite routines are provided to allow + // the caller to tell the I/O subsystem that it is OK to do full + // sector reads or writes if it needs to, because pvBuffer is + // guaranteed to be a multiple of 512 bytes big. If the I/O + // subsystem can only do sector reads and writes, it can use the + // extra buffer space in pvBuffer. When a program calls SectorWrite + // it is also telling the I/O subsystem that it does not need to + // read a partially written sector from disk before writing it out. + // It will be OK to write whatever data is in the pvBuffer to fill out + // the sector. + + RCODE XFLMAPI SectorRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesReadRV); + + RCODE XFLMAPI 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 XFLMAPI CanDoAsync( void); + + FINLINE FLMBOOL XFLMAPI UsingDirectIo( void) + { + return( m_bDoDirectIO); + } + + FINLINE void XFLMAPI setExtendSize( + FLMUINT uiExtendSize) + { + m_uiExtendSize = uiExtendSize; + } + + FINLINE void XFLMAPI setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) + { + m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; + } + + RCODE XFLMAPI Lock( void); + + RCODE XFLMAPI Unlock( void); + + FINLINE void XFLMAPI SetBlockSize( + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + FINLINE FLMUINT XFLMAPI 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); + + // The following are for every platform. + + F_FileHdl * m_pNext; // Next file handle in list + F_FileHdl * m_pPrev; // Prev file handle in list + FLMBOOL m_bInList; // Is this file handle in a list? + FLMBOOL m_bFileOpened; // Is the file currently opened/closed. + FLMUINT m_uiAvailTime; // Time when placed in avail list. + FLMUINT m_uiFileId; // FFILE Unique File Id + FLMBOOL m_bDeleteOnRelease; // Delete this file when it is released. + FLMBOOL m_bOpenedReadOnly; // Opened the file read only + FLMBOOL m_bOpenedExclusive; // Opened the file in exclusive mode + char * m_pszFileName; // File name for this FileHdl + + // Specific to this platform + + 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_FileHdlPage; + friend class F_FileHdlMgr; +}; + +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 // FLM_UNIX + +#endif // FPOSIX_H diff --git a/version5/src/fqeval.cpp b/version5/src/fqeval.cpp new file mode 100644 index 0000000..945d3ef --- /dev/null +++ b/version5/src/fqeval.cpp @@ -0,0 +1,2566 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for doing evaluation of query expressions. +// +// 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: fqeval.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fquery.h" + +FSTATIC RCODE fqApproxCompare( + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMINT * piResult); + +FSTATIC RCODE fqCompareBinary( + IF_OperandComparer * pOpComparer, + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMINT * piResult); + +FSTATIC RCODE fqCompareText( + IF_OperandComparer * pOpComparer, + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMUINT uiCompareRules, + FLMBOOL bOpIsMatch, + FLMUINT uiLanguage, + FLMINT * piResult); + +FSTATIC void fqOpUUBitAND( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUBitOR( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUBitXOR( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUSMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSSMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSUMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUSDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSSDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSUDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUSMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSSMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSUMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUSPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSSPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSUPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUUMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpUSMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSSMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FSTATIC void fqOpSUMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +typedef void FQ_OPERATION( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult); + +FQ_OPERATION * FQ_ArithOpTable[ + ((XFLM_LAST_ARITH_OP - XFLM_FIRST_ARITH_OP) + 1) * 4 ] = +{ +/* U = Unsigned S = Signed + U + U U + S + S + U S + S */ +/* BITAND */ fqOpUUBitAND, fqOpUUBitAND, + fqOpUUBitAND, fqOpUUBitAND, +/* BITOR */ fqOpUUBitOR, fqOpUUBitOR, + fqOpUUBitOR, fqOpUUBitOR, +/* BITXOR */ fqOpUUBitXOR, fqOpUUBitXOR, + fqOpUUBitXOR, fqOpUUBitXOR, +/* MULT */ fqOpUUMult, fqOpUSMult, + fqOpSUMult, fqOpSSMult, +/* DIV */ fqOpUUDiv, fqOpUSDiv, + fqOpSUDiv, fqOpSSDiv, +/* MOD */ fqOpUUMod, fqOpUSMod, + fqOpSUMod, fqOpSSMod, +/* PLUS */ fqOpUUPlus, fqOpUSPlus, + fqOpSUPlus, fqOpSSPlus, +/* MINUS */ fqOpUUMinus, fqOpUSMinus, + fqOpSUMinus, fqOpSSMinus +}; + +/*************************************************************************** +Desc: Returns a 64-bit unsigned integer +***************************************************************************/ +FINLINE FLMUINT64 fqGetUInt64( + FQVALUE * pValue) +{ + if (pValue->eValType == XFLM_UINT_VAL) + { + return( (FLMUINT64)pValue->val.uiVal); + } + else if( pValue->eValType == XFLM_UINT64_VAL) + { + return( pValue->val.ui64Val); + } + else if( pValue->eValType == XFLM_INT64_VAL) + { + if( pValue->val.i64Val >= 0) + { + return( (FLMUINT64)pValue->val.i64Val); + } + } + else if( pValue->eValType == XFLM_INT_VAL) + { + if( pValue->val.iVal >= 0) + { + return( (FLMUINT64)pValue->val.iVal); + } + } + + flmAssert( 0); + return( 0); +} + +/*************************************************************************** +Desc: Returns a 64-bit signed integer +***************************************************************************/ +FINLINE FLMINT64 fqGetInt64( + FQVALUE * pValue) +{ + if (pValue->eValType == XFLM_INT_VAL) + { + return( (FLMINT64)pValue->val.iVal); + } + else if( pValue->eValType == XFLM_INT64_VAL) + { + return( pValue->val.i64Val); + } + else if( pValue->eValType == XFLM_UINT_VAL) + { + return( (FLMINT64)pValue->val.uiVal); + } + else if( pValue->eValType == XFLM_UINT64_VAL) + { + if( pValue->val.ui64Val <= (FLMUINT64)FLM_MAX_INT64) + { + return( (FLMINT64)pValue->val.ui64Val); + } + } + + flmAssert( 0); + return( 0); +} + +/*************************************************************************** +Desc: Performs the bit and operation +***************************************************************************/ +FSTATIC void fqOpUUBitAND( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.uiVal = pLValue->val.uiVal & pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.ui64Val = + fqGetUInt64( pLValue) & fqGetUInt64( pRValue); + pResult->eValType = XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the bit or operation +***************************************************************************/ +FSTATIC void fqOpUUBitOR( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.uiVal = pLValue->val.uiVal | pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.ui64Val = + fqGetUInt64( pLValue) | fqGetUInt64( pRValue); + pResult->eValType = XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the bit xor operation +***************************************************************************/ +FSTATIC void fqOpUUBitXOR( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.uiVal = pLValue->val.uiVal ^ pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.ui64Val = + fqGetUInt64( pLValue) ^ fqGetUInt64( pRValue); + pResult->eValType = XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the multiply operation +***************************************************************************/ +FSTATIC void fqOpUUMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.uiVal = pLValue->val.uiVal * pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.ui64Val = + fqGetUInt64( pLValue) * fqGetUInt64( pRValue); + pResult->eValType = XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the multiply operation +***************************************************************************/ +FSTATIC void fqOpUSMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.iVal = (FLMINT)pLValue->val.uiVal * pRValue->val.iVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64) + fqGetUInt64( pLValue) * fqGetInt64( pRValue); + pResult->eValType = XFLM_INT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the multiply operation +***************************************************************************/ +FSTATIC void fqOpSSMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.iVal = pLValue->val.iVal * pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL + : XFLM_UINT_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64)(fqGetInt64( pLValue) * + fqGetInt64( pRValue)); + + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL + : XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the multiply operation +***************************************************************************/ +FSTATIC void fqOpSUMult( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.iVal = pLValue->val.iVal * + (FLMINT)pRValue->val.uiVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64) + (fqGetInt64( pLValue) * fqGetUInt64( pRValue)); + pResult->eValType = XFLM_INT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs the divide operation +***************************************************************************/ +FSTATIC void fqOpUUDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.uiVal) + { + pResult->val.uiVal = pLValue->val.uiVal / pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64RValue) + { + pResult->val.ui64Val = ui64LValue / ui64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the divide operation +***************************************************************************/ +FSTATIC void fqOpUSDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.iVal) + { + pResult->val.iVal = pLValue->val.uiVal / pRValue->val.iVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( i64RValue) + { + pResult->val.i64Val = ui64LValue / i64RValue; + pResult->eValType = XFLM_INT64_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the divide operation +***************************************************************************/ +FSTATIC void fqOpSSDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.iVal) + { + pResult->val.iVal = pLValue->val.iVal / pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( i64RValue) + { + pResult->val.i64Val = i64LValue / i64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the divide operation +***************************************************************************/ +FSTATIC void fqOpSUDiv( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.uiVal) + { + pResult->val.iVal = pLValue->val.iVal / pRValue->val.uiVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64RValue) + { + pResult->val.i64Val = i64LValue / ui64RValue; + pResult->eValType = XFLM_INT64_VAL; + } + else + { + pResult->val.uiVal = 0; // Divide by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the modulo operation +***************************************************************************/ +FSTATIC void fqOpUUMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.uiVal) + { + pResult->val.uiVal = pLValue->val.uiVal % pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64RValue) + { + pResult->val.ui64Val = ui64LValue % ui64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the modulo operation +***************************************************************************/ +FSTATIC void fqOpUSMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.iVal) + { + pResult->val.iVal = pLValue->val.uiVal % pRValue->val.iVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( i64RValue) + { + pResult->val.i64Val = ui64LValue % i64RValue; + pResult->eValType = XFLM_INT64_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the modulo operation +***************************************************************************/ +FSTATIC void fqOpSSMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.iVal) + { + pResult->val.iVal = pLValue->val.iVal % pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( i64RValue) + { + pResult->val.i64Val = i64LValue % i64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs the modulo operation +***************************************************************************/ +FSTATIC void fqOpSUMod( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.uiVal) + { + pResult->val.iVal = pLValue->val.iVal % pRValue->val.uiVal; + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64RValue) + { + pResult->val.i64Val = i64LValue % ui64RValue; + pResult->eValType = XFLM_INT64_VAL; + } + else + { + pResult->val.uiVal = 0; // MOD by ZERO case. + pResult->eValType = XFLM_MISSING_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs an addition operation +***************************************************************************/ +FSTATIC void fqOpUUPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.uiVal = pLValue->val.uiVal + pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.ui64Val = + fqGetUInt64( pLValue) + fqGetUInt64( pRValue); + pResult->eValType = XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs an addition operation +***************************************************************************/ +FSTATIC void fqOpUSPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( (pRValue->val.iVal >= 0) || + (pLValue->val.uiVal > gv_uiMaxSignedIntVal)) + { + pResult->val.uiVal = pLValue->val.uiVal + (FLMUINT)pRValue->val.iVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.iVal = (FLMINT)pLValue->val.uiVal + pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( (i64RValue >= 0) || (ui64LValue > gv_ui64MaxSignedIntVal)) + { + pResult->val.ui64Val = ui64LValue + (FLMUINT64)i64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64)ui64LValue + i64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs an addition operation +***************************************************************************/ +FSTATIC void fqOpSSPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + pResult->val.iVal = pLValue->val.iVal + pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + else + { + pResult->val.i64Val = + fqGetInt64( pLValue) + fqGetInt64( pRValue); + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } +} + +/*************************************************************************** +Desc: Performs an addition operation +***************************************************************************/ +FSTATIC void fqOpSUPlus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( (pLValue->val.iVal >= 0) || + (pRValue->val.uiVal > gv_uiMaxSignedIntVal)) + { + pResult->val.uiVal = (FLMUINT)pLValue->val.iVal + pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.iVal = pLValue->val.iVal + (FLMINT)pRValue->val.uiVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( (i64LValue >= 0) || (ui64RValue > gv_ui64MaxSignedIntVal)) + { + pResult->val.ui64Val = (FLMUINT64)i64LValue + ui64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.i64Val = i64LValue + (FLMINT64)ui64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs a subtraction operation +***************************************************************************/ +FSTATIC void fqOpUUMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pLValue->val.uiVal >= pRValue->val.uiVal) + { + pResult->val.uiVal = pLValue->val.uiVal - pRValue->val.uiVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.iVal = (FLMINT)(pLValue->val.uiVal - pRValue->val.uiVal); + pResult->eValType = XFLM_INT_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64LValue >= ui64RValue) + { + pResult->val.ui64Val = ui64LValue - ui64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64)(ui64LValue - ui64RValue); + pResult->eValType = XFLM_INT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs a subtraction operation +***************************************************************************/ +FSTATIC void fqOpUSMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.iVal < 0) + { + pResult->val.uiVal = pLValue->val.uiVal - pRValue->val.iVal; + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.iVal = (FLMINT)pLValue->val.uiVal - pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + } + else + { + FLMUINT64 ui64LValue = fqGetUInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( i64RValue < 0) + { + pResult->val.ui64Val = ui64LValue - i64RValue; + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.i64Val = (FLMINT64)ui64LValue - i64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs a subtraction operation +***************************************************************************/ +FSTATIC void fqOpSSMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if(( pLValue->val.iVal > 0) && ( pRValue->val.iVal < 0)) + { + pResult->val.uiVal = (FLMUINT)(pLValue->val.iVal - pRValue->val.iVal); + pResult->eValType = XFLM_UINT_VAL; + } + else + { + pResult->val.iVal = pLValue->val.iVal - pRValue->val.iVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMINT64 i64RValue = fqGetInt64( pRValue); + + if( (i64LValue > 0) && (i64RValue < 0)) + { + pResult->val.ui64Val = (FLMUINT64)( i64LValue - i64RValue); + pResult->eValType = XFLM_UINT64_VAL; + } + else + { + pResult->val.i64Val = i64LValue - i64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Performs a subtraction operation +***************************************************************************/ +FSTATIC void fqOpSUMinus( + FQVALUE * pLValue, + FQVALUE * pRValue, + FQVALUE * pResult) +{ + if( isNativeNum( pLValue->eValType) && + isNativeNum( pRValue->eValType)) + { + if( pRValue->val.uiVal > gv_uiMaxSignedIntVal) + { + pResult->val.iVal = (pLValue->val.iVal - gv_uiMaxSignedIntVal) - + (FLMINT)(pRValue->val.uiVal - gv_uiMaxSignedIntVal); + pResult->eValType = XFLM_INT_VAL; + } + else + { + pResult->val.iVal = pLValue->val.iVal - (FLMINT)pRValue->val.uiVal; + pResult->eValType = (pResult->val.iVal < 0) + ? XFLM_INT_VAL : XFLM_UINT_VAL; + } + } + else + { + FLMINT64 i64LValue = fqGetInt64( pLValue); + FLMUINT64 ui64RValue = fqGetUInt64( pRValue); + + if( ui64RValue > gv_ui64MaxSignedIntVal) + { + pResult->val.i64Val = (i64LValue - gv_ui64MaxSignedIntVal) - + (FLMINT64)(ui64RValue - gv_ui64MaxSignedIntVal); + pResult->eValType = XFLM_INT64_VAL; + } + else + { + pResult->val.i64Val = i64LValue - (FLMINT64)ui64RValue; + pResult->eValType = (pResult->val.i64Val < 0) + ? XFLM_INT64_VAL : XFLM_UINT64_VAL; + } + } +} + +/*************************************************************************** +Desc: Compare two entire strings. +****************************************************************************/ +RCODE fqCompareCollStreams( + F_CollIStream * pLStream, + F_CollIStream * pRStream, + FLMBOOL bOpIsMatch, + FLMUINT uiLanguage, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT16 ui16RCol; + FLMUINT16 ui16LCol; + FLMUINT16 ui16RSubCol; + FLMUINT16 ui16LSubCol; + FLMBYTE ucRCase; + FLMBYTE ucLCase; + F_CollStreamPos savedRPos; + F_CollStreamPos savedLPos; + F_CollStreamPos startLPos; + FLMUNICODE uLChar = 0; + FLMBOOL bLCharIsWild = FALSE; + FLMUNICODE uRChar = 0; + FLMBOOL bRCharIsWild = FALSE; + FLMBOOL bPrevLWasWild = FALSE; + FLMBOOL bPrevRWasWild = FALSE; + FLMBOOL bAllowTwoIntoOne; + + // If we are doing a "match" operation, we don't want two + // character sequences like Ch, ae, etc. turned into a single + // a single collation, because then matches that involve wildcards + // like "aetna == a*" would not match properly. + // When not doing a match operation, we WANT two character sequences + // turned into a single collation value so that we can know if + // something is > or <. When doing match operations, all we care + // about is if they are equal or not, so there is no need to look + // at double character collation properties. + + bAllowTwoIntoOne = bOpIsMatch ? FALSE : TRUE; + + for( ;;) + { +GetNextLChar: + + if( bLCharIsWild) + { + bPrevLWasWild = TRUE; + } + + pLStream->getCurrPosition( &startLPos); + if( RC_BAD( rc = pLStream->read( + bAllowTwoIntoOne, + &uLChar, &bLCharIsWild, &ui16LCol, &ui16LSubCol, &ucLCase))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + + // If the last character was a wildcard, we have a match! + + if( bPrevLWasWild) + { + *piResult = 0; + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = pRStream->read( + bAllowTwoIntoOne, + &uRChar, &bRCharIsWild, &ui16RCol, &ui16RSubCol, &ucRCase))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + *piResult = 0; + } + + goto Exit; + } + + // Break out when we hit a non-wild character + + if( !bRCharIsWild) + { + break; + } + } + + *piResult = -1; + } + + goto Exit; + } + + if( bLCharIsWild) + { + // Consume multiple wildcards + + if( bPrevLWasWild) + { + goto GetNextLChar; + } + + // See if we match anywhere on the remaining right string + + for( ;;) + { + pRStream->getCurrPosition( &savedRPos); + pLStream->getCurrPosition( &savedLPos); + + if( RC_BAD( rc = fqCompareCollStreams( pLStream, pRStream, + bOpIsMatch, uiLanguage, piResult))) + { + goto Exit; + } + + if( !(*piResult)) + { + goto Exit; + } + + if( RC_BAD( rc = pRStream->positionTo( &savedRPos))) + { + goto Exit; + } + + if( RC_BAD( rc = pRStream->read( + bAllowTwoIntoOne, + NULL, NULL, NULL, NULL, NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + if( RC_BAD( rc = pLStream->positionTo( &savedLPos))) + { + goto Exit; + } + } + + *piResult = 1; + goto Exit; + } + +GetNextRChar: + + if( bRCharIsWild) + { + bPrevRWasWild = TRUE; + } + + if( RC_BAD( rc = pRStream->read( + bAllowTwoIntoOne, + &uRChar, &bRCharIsWild, &ui16RCol, &ui16RSubCol, &ucRCase))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + + // If the last character was a wildcard, we have a match! + + if( bPrevRWasWild) + { + *piResult = 0; + } + else + { + *piResult = 1; + } + } + + goto Exit; + } + + if( bRCharIsWild) + { + if( bPrevRWasWild) + { + goto GetNextRChar; + } + + // See if we match anywhere on the remaining left string + + if( RC_BAD( rc = pLStream->positionTo( &startLPos))) + { + goto Exit; + } + + for( ;;) + { + pLStream->getCurrPosition( &savedLPos); + pRStream->getCurrPosition( &savedRPos); + + if( RC_BAD( rc = fqCompareCollStreams( pLStream, pRStream, + bOpIsMatch, uiLanguage, piResult))) + { + goto Exit; + } + + if( !(*piResult)) + { + goto Exit; + } + + if( RC_BAD( rc = pRStream->positionTo( &savedRPos))) + { + goto Exit; + } + + if( RC_BAD( rc = pLStream->positionTo( &savedLPos))) + { + goto Exit; + } + + // Skip the character we just processed + + if( RC_BAD( rc = pLStream->read( + bAllowTwoIntoOne, + NULL, NULL, NULL, NULL, NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + } + + *piResult = -1; + goto Exit; + } + + if( ui16LCol != ui16RCol) + { + *piResult = ui16LCol < ui16RCol ? -1 : 1; + goto Exit; + } + else if( ui16LSubCol != ui16RSubCol) + { + *piResult = ui16LSubCol < ui16RSubCol ? -1 : 1; + goto Exit; + } + else if( ucLCase != ucRCase) + { + // NOTE: If we are doing a case insensitive comparison, + // ucLCase and ucRCase should be equal (both will have been + // set to zero + + *piResult = ucLCase < ucRCase ? -1 : 1; + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Compare two entire strings. +****************************************************************************/ +FSTATIC RCODE fqCompareText( + IF_OperandComparer * pOpComparer, + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMUINT uiCompareRules, + FLMBOOL bOpIsMatch, + FLMUINT uiLanguage, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream bufferLStream; + IF_PosIStream * pLStream; + F_BufferIStream bufferRStream; + IF_PosIStream * pRStream; + F_CollIStream lStream; + F_CollIStream rStream; + + // Types must be text + + if (pLValue->eValType != XFLM_UTF8_VAL || + pRValue->eValType != XFLM_UTF8_VAL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Open the streams + + if( !(pLValue->uiFlags & VAL_IS_STREAM)) + { + if (RC_BAD( rc = bufferLStream.open( pLValue->val.pucBuf, + pLValue->uiDataLen))) + { + goto Exit; + } + + pLStream = &bufferLStream; + } + else + { + pLStream = pLValue->val.pIStream; + } + + if( !(pRValue->uiFlags & VAL_IS_STREAM)) + { + if( RC_BAD( rc = bufferRStream.open( pRValue->val.pucBuf, + pRValue->uiDataLen))) + { + goto Exit; + } + pRStream = &bufferRStream; + } + else + { + pRStream = pRValue->val.pIStream; + } + + if (pOpComparer) + { + rc = pOpComparer->compare( pLStream, pRStream, piResult); + goto Exit; + } + + // Open up the collated streams + + if (RC_BAD( rc = lStream.open( pLStream, FALSE, uiLanguage, uiCompareRules, + (bOpIsMatch && (pLValue->uiFlags & VAL_IS_CONSTANT)) ? TRUE : FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = rStream.open( pRStream, FALSE, uiLanguage, uiCompareRules, + (bOpIsMatch && (pRValue->uiFlags & VAL_IS_CONSTANT)) ? TRUE : FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = fqCompareCollStreams( &lStream, &rStream, + bOpIsMatch, uiLanguage, piResult))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Approximate compare - only works for strings right now. +****************************************************************************/ +FSTATIC RCODE fqApproxCompare( + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLMeta; + FLMUINT uiRMeta; + FLMUINT64 ui64StartPos; + F_BufferIStream bufferLStream; + IF_PosIStream * pLStream; + F_BufferIStream bufferRStream; + IF_PosIStream * pRStream; + + // Types must be text + + if (pLValue->eValType != XFLM_UTF8_VAL || + pRValue->eValType != XFLM_UTF8_VAL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Open the streams + + if (!(pLValue->uiFlags & VAL_IS_STREAM)) + { + if (RC_BAD( rc = bufferLStream.open( pLValue->val.pucBuf, + pLValue->uiDataLen))) + { + goto Exit; + } + + pLStream = &bufferLStream; + } + else + { + pLStream = pLValue->val.pIStream; + } + + if (!(pRValue->uiFlags & VAL_IS_STREAM)) + { + if( RC_BAD( rc = bufferRStream.open( pRValue->val.pucBuf, + pRValue->uiDataLen))) + { + goto Exit; + } + pRStream = &bufferRStream; + } + else + { + pRStream = pRValue->val.pIStream; + } + + if ((pLValue->uiFlags & VAL_IS_CONSTANT) || + !(pRValue->uiFlags & VAL_IS_CONSTANT)) + { + for( ;;) + { + if( RC_BAD( rc = flmGetNextMetaphone( pLStream, &uiLMeta))) + { + if( rc == NE_XFLM_EOF_HIT) + { + *piResult = 0; + rc = NE_XFLM_OK; + } + goto Exit; + } + + ui64StartPos = pRStream->getCurrPosition(); + + for( ;;) + { + if( RC_BAD( rc = flmGetNextMetaphone( pRStream, &uiRMeta))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + *piResult = -1; + } + + goto Exit; + } + + if( uiLMeta == uiRMeta) + { + break; + } + + } + + if( RC_BAD( rc = pRStream->positionTo( ui64StartPos))) + { + goto Exit; + } + } + } + else + { + for( ;;) + { + if( RC_BAD( rc = flmGetNextMetaphone( pRStream, &uiRMeta))) + { + if( rc == NE_XFLM_EOF_HIT) + { + *piResult = 0; + rc = NE_XFLM_OK; + } + goto Exit; + } + + ui64StartPos = pLStream->getCurrPosition(); + + for( ;;) + { + if( RC_BAD( rc = flmGetNextMetaphone( pLStream, &uiLMeta))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + *piResult = 1; + } + + goto Exit; + } + + if( uiLMeta == uiRMeta) + { + break; + } + + } + + if( RC_BAD( rc = pLStream->positionTo( ui64StartPos))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Performs binary comparison on two streams - may be text or binary, + it really doesn't matter. Returns XFLM_TRUE or XFLM_FALSE. +***************************************************************************/ +FSTATIC RCODE fqCompareBinary( + IF_OperandComparer * pOpComparer, + FQVALUE * pLValue, + FQVALUE * pRValue, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream bufferLStream; + IF_PosIStream * pLStream; + F_BufferIStream bufferRStream; + IF_PosIStream * pRStream; + FLMBYTE ucLByte; + FLMBYTE ucRByte; + FLMUINT uiOffset = 0; + FLMBOOL bLEmpty = FALSE; + + *piResult = 0; + + // Types must be binary + + if ( pLValue->eValType != XFLM_BINARY_VAL || + pRValue->eValType != XFLM_BINARY_VAL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Open the streams + + if( !(pLValue->uiFlags & VAL_IS_STREAM)) + { + if (RC_BAD( rc = bufferLStream.open( pLValue->val.pucBuf, + pLValue->uiDataLen))) + { + goto Exit; + } + + pLStream = &bufferLStream; + } + else + { + pLStream = pLValue->val.pIStream; + } + + if( !(pRValue->uiFlags & VAL_IS_STREAM)) + { + if( RC_BAD( rc = bufferRStream.open( pRValue->val.pucBuf, + pRValue->uiDataLen))) + { + goto Exit; + } + pRStream = &bufferRStream; + } + else + { + pRStream = pRValue->val.pIStream; + } + + if (pOpComparer) + { + rc = pOpComparer->compare( pLStream, pRStream, piResult); + goto Exit; + } + + for (;;) + { + if (RC_BAD( rc = flmReadStorageAsBinary( + pLStream, &ucLByte, 1, uiOffset, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + bLEmpty = TRUE; + } + else + { + goto Exit; + } + } + + if (RC_BAD( rc = flmReadStorageAsBinary( + pRStream, &ucRByte, 1, uiOffset, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + if( bLEmpty) + { + *piResult = 0; + } + else + { + *piResult = 1; + } + } + goto Exit; + } + else if( bLEmpty) + { + *piResult = -1; + goto Exit; + } + + if( ucLByte != ucRByte) + { + *piResult = ucLByte < ucRByte ? -1 : 1; + goto Exit; + } + + uiOffset++; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Compare two values. This routine assumes that pValue1 and pValue2 + are non-null. +***************************************************************************/ +RCODE fqCompare( + FQVALUE * pValue1, + FQVALUE * pValue2, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FLMUINT uiLanguage, + FLMINT * piCmp) +{ + RCODE rc = NE_XFLM_OK; + + // We have already called fqCanCompare, so no need to do it here + + switch (pValue1->eValType) + { + case XFLM_BOOL_VAL: + *piCmp = pValue1->val.eBool > pValue2->val.eBool + ? 1 + : pValue1->val.eBool < pValue2->val.eBool + ? -1 + : 0; + break; + case XFLM_UINT_VAL: + switch (pValue2->eValType) + { + case XFLM_UINT_VAL: + *piCmp = pValue1->val.uiVal > pValue2->val.uiVal + ? 1 + : pValue1->val.uiVal < pValue2->val.uiVal + ? -1 + : 0; + break; + case XFLM_UINT64_VAL: + *piCmp = (FLMUINT64)pValue1->val.uiVal > pValue2->val.ui64Val + ? 1 + : (FLMUINT64)pValue1->val.uiVal < pValue2->val.ui64Val + ? -1 + : 0; + break; + case XFLM_INT_VAL: + *piCmp = pValue2->val.iVal < 0 || + pValue1->val.uiVal > (FLMUINT)pValue2->val.iVal + ? 1 + : pValue1->val.uiVal < (FLMUINT)pValue2->val.iVal + ? -1 + : 0; + break; + case XFLM_INT64_VAL: + *piCmp = pValue2->val.i64Val < 0 || + (FLMUINT64)pValue1->val.uiVal > + (FLMUINT64)pValue2->val.i64Val + ? 1 + : (FLMUINT64)pValue1->val.uiVal < + (FLMUINT64)pValue2->val.i64Val + ? -1 + : 0; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + break; + case XFLM_UINT64_VAL: + switch (pValue2->eValType) + { + case XFLM_UINT_VAL: + *piCmp = pValue1->val.ui64Val > (FLMUINT64)pValue2->val.uiVal + ? 1 + : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.uiVal + ? -1 + : 0; + break; + case XFLM_UINT64_VAL: + *piCmp = pValue1->val.ui64Val > pValue2->val.ui64Val + ? 1 + : pValue1->val.ui64Val < pValue2->val.ui64Val + ? -1 + : 0; + break; + case XFLM_INT_VAL: + *piCmp = pValue2->val.iVal < 0 || + pValue1->val.ui64Val > (FLMUINT64)pValue2->val.iVal + ? 1 + : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.iVal + ? -1 + : 0; + break; + case XFLM_INT64_VAL: + *piCmp = pValue2->val.i64Val < 0 || + pValue1->val.ui64Val > (FLMUINT64)pValue2->val.i64Val + ? 1 + : pValue1->val.ui64Val < (FLMUINT64)pValue2->val.i64Val + ? -1 + : 0; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + break; + case XFLM_INT_VAL: + switch (pValue2->eValType) + { + case XFLM_UINT_VAL: + *piCmp = pValue1->val.iVal < 0 || + (FLMUINT)pValue1->val.iVal < pValue2->val.uiVal + ? -1 + : (FLMUINT)pValue1->val.iVal > pValue2->val.uiVal + ? 1 + : 0; + break; + case XFLM_UINT64_VAL: + *piCmp = pValue1->val.iVal < 0 || + (FLMUINT64)pValue1->val.iVal < pValue2->val.ui64Val + ? -1 + : (FLMUINT64)pValue1->val.iVal > pValue2->val.ui64Val + ? 1 + : 0; + break; + case XFLM_INT_VAL: + *piCmp = pValue1->val.iVal < pValue2->val.iVal + ? -1 + : pValue1->val.iVal > pValue2->val.iVal + ? 1 + : 0; + break; + case XFLM_INT64_VAL: + *piCmp = (FLMINT64)pValue1->val.iVal < pValue2->val.i64Val + ? -1 + : (FLMINT64)pValue1->val.iVal > pValue2->val.i64Val + ? 1 + : 0; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + break; + case XFLM_INT64_VAL: + switch (pValue2->eValType) + { + case XFLM_UINT_VAL: + *piCmp = pValue1->val.i64Val < 0 || + (FLMUINT64)pValue1->val.i64Val < + (FLMUINT64)pValue2->val.uiVal + ? -1 + : (FLMUINT64)pValue1->val.i64Val > + (FLMUINT64)pValue2->val.uiVal + ? 1 + : 0; + break; + case XFLM_UINT64_VAL: + *piCmp = pValue1->val.i64Val < 0 || + (FLMUINT64)pValue1->val.i64Val < pValue2->val.ui64Val + ? -1 + : (FLMUINT64)pValue1->val.i64Val > pValue2->val.ui64Val + ? 1 + : 0; + break; + case XFLM_INT_VAL: + *piCmp = pValue1->val.i64Val < (FLMINT64)pValue2->val.iVal + ? -1 + : pValue1->val.i64Val > (FLMINT64)pValue2->val.iVal + ? 1 + : 0; + break; + case XFLM_INT64_VAL: + *piCmp = pValue1->val.i64Val < pValue2->val.i64Val + ? -1 + : pValue1->val.i64Val > pValue2->val.i64Val + ? 1 + : 0; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + break; + case XFLM_BINARY_VAL: + if (RC_BAD( rc = fqCompareBinary( pOpComparer, pValue1, + pValue2, piCmp))) + { + goto Exit; + } + break; + case XFLM_UTF8_VAL: + if (RC_BAD( rc = fqCompareText( pOpComparer, + pValue1, pValue2, + uiCompareRules, FALSE, uiLanguage, piCmp))) + { + goto Exit; + } + break; + default: + break; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Do a comparison operator. +***************************************************************************/ +RCODE fqCompareOperands( + FLMUINT uiLanguage, + FQVALUE * pLValue, + FQVALUE * pRValue, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FLMBOOL bNotted, + XFlmBoolType * peBool) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iCmp; + + if (!pLValue || pLValue->eValType == XFLM_MISSING_VAL || + !pRValue || pRValue->eValType == XFLM_MISSING_VAL || + !fqCanCompare( pLValue, pRValue)) + { + *peBool = (bNotted ? XFLM_TRUE : XFLM_FALSE); + } + + // At this point, both operands are known to be present and are of + // types that can be compared. The comparison + // will therefore be performed according to the + // operator specified. + + else + { + switch (eOperator) + { + case XFLM_EQ_OP: + case XFLM_NE_OP: + if (pLValue->eValType == XFLM_UTF8_VAL || + pRValue->eValType == XFLM_UTF8_VAL) + { + if (RC_BAD( rc = fqCompareText( pOpComparer, pLValue, pRValue, + uiCompareRules, TRUE, uiLanguage, &iCmp))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = fqCompare( pLValue, pRValue, + uiCompareRules, pOpComparer, uiLanguage, &iCmp))) + { + goto Exit; + } + } + if (eOperator == XFLM_EQ_OP) + { + *peBool = (iCmp == 0 ? XFLM_TRUE : XFLM_FALSE); + } + else + { + *peBool = (iCmp != 0 ? XFLM_TRUE : XFLM_FALSE); + } + break; + + case XFLM_APPROX_EQ_OP: + if (RC_BAD( rc = fqApproxCompare( pLValue, pRValue, &iCmp))) + { + goto Exit; + } + *peBool = (iCmp == 0 ? XFLM_TRUE : XFLM_FALSE); + break; + + case XFLM_LT_OP: + if (RC_BAD( rc = fqCompare( pLValue, pRValue, + uiCompareRules, pOpComparer, uiLanguage, &iCmp))) + { + goto Exit; + } + *peBool = (iCmp < 0 ? XFLM_TRUE : XFLM_FALSE); + break; + + case XFLM_LE_OP: + if (RC_BAD( rc = fqCompare( pLValue, pRValue, + uiCompareRules, pOpComparer, uiLanguage, &iCmp))) + { + goto Exit; + } + *peBool = (iCmp <= 0 ? XFLM_TRUE : XFLM_FALSE); + break; + + case XFLM_GT_OP: + if (RC_BAD( rc = fqCompare( pLValue, pRValue, + uiCompareRules, pOpComparer, uiLanguage, &iCmp))) + { + goto Exit; + } + *peBool = (iCmp > 0 ? XFLM_TRUE : XFLM_FALSE); + break; + + case XFLM_GE_OP: + if (RC_BAD( rc = fqCompare( pLValue, pRValue, + uiCompareRules, pOpComparer, uiLanguage, &iCmp))) + { + goto Exit; + } + *peBool = (iCmp >= 0 ? XFLM_TRUE : XFLM_FALSE); + break; + + default: + *peBool = XFLM_UNKNOWN; + rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Do an arithmetic operator. +***************************************************************************/ +RCODE fqArithmeticOperator( + FQVALUE * pLValue, + FQVALUE * pRValue, + eQueryOperators eOperator, + FQVALUE * pResult) +{ + RCODE rc = NE_XFLM_OK; + FQ_OPERATION * fnOp; + FLMUINT uiOffset = 0; + + if( !isArithOp( eOperator)) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if (pLValue->eValType == XFLM_MISSING_VAL || + pRValue->eValType == XFLM_MISSING_VAL) + { + pResult->eValType = XFLM_MISSING_VAL; + goto Exit; + } + + if( isUnsigned( pLValue)) + { + if( isUnsigned( pRValue)) + { + uiOffset = 0; + } + else if( isSigned( pRValue)) + { + uiOffset = 1; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + else if( isSigned( pLValue)) + { + if( isUnsigned( pRValue)) + { + uiOffset = 2; + } + else if( isSigned( pRValue)) + { + uiOffset = 3; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + + fnOp = FQ_ArithOpTable[ ((((FLMUINT)eOperator) - + XFLM_FIRST_ARITH_OP) * 4) + uiOffset]; + fnOp( pLValue, pRValue, pResult); + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CollIStream::read( + FLMBOOL bAllowTwoIntoOne, + FLMUNICODE * puChar, + FLMBOOL * pbCharIsWild, + FLMUINT16 * pui16Col, + FLMUINT16 * pui16SubCol, + FLMBYTE * pucCase) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + FLMUINT16 ui16WpChar; + FLMUINT16 ui16NextWpChar; + FLMUINT16 ui16Col; + FLMUINT16 ui16SubCol; + FLMBOOL bTwoIntoOne; + FLMBYTE ucCase; + FLMBOOL bAsian; + FLMBOOL bLastCharWasSpace = FALSE; + FLMUINT64 ui64AfterLastSpacePos = 0; + FLMUINT64 ui64CurrCharPos = 0; + + if (pbCharIsWild) + { + *pbCharIsWild = FALSE; + } + + // Is this a double-byte (Asian) character set? + + bAsian = (m_uiLanguage >= FIRST_DBCS_LANG && m_uiLanguage <= LAST_DBCS_LANG) + ? TRUE + : FALSE; + + // Get the next character from the stream + +GetNextChar: + + ui16WpChar = 0; + ui16NextWpChar = 0; + ui16Col = 0; + ui16SubCol = 0; + bTwoIntoOne = FALSE; + ucCase = 0; + + if (m_uNextChar) + { + uChar = m_uNextChar; + m_uNextChar = 0; + } + else + { + ui64CurrCharPos = m_pIStream->getCurrPosition(); + if( RC_BAD( rc = readCharFromStream( &uChar))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + // If we were skipping spaces, we need to + // process a single space character, unless we are + // ignoring trailing white space. + + if (bLastCharWasSpace && + !(m_uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE)) + { + // bLastCharWasSpace flag can only be TRUE if either + // XFLM_COMP_IGNORE_TRAILING_SPACE is set or + // XFLM_COMP_COMPRESS_WHITESPACE is set. + + flmAssert( m_uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE); + uChar = ASCII_SPACE; + rc = NE_XFLM_OK; + goto Process_Char; + } + goto Exit; + } + } + + if ((uChar = flmConvertChar( uChar, m_uiCompareRules)) == 0) + { + goto GetNextChar; + } + + // Deal with spaces + + if (uChar == ASCII_SPACE) + { + if (m_uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) + { + bLastCharWasSpace = TRUE; + ui64AfterLastSpacePos = m_pIStream->getCurrPosition(); + goto GetNextChar; + } + else if (m_uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE) + { + if (!bLastCharWasSpace) + { + bLastCharWasSpace = TRUE; + + // Save where we are at so that if this doesn't turn out + // to be trailing spaces, we can restore this position. + + ui64AfterLastSpacePos = m_pIStream->getCurrPosition(); + } + goto GetNextChar; + } + } + else + { + if (m_uiCompareRules & XFLM_COMP_IGNORE_LEADING_SPACE) + { + m_ui64EndOfLeadingSpacesPos = ui64CurrCharPos; + m_uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); + } + + // If the last character was a space, we need to process it. + + if (bLastCharWasSpace) + { + + // Position back to after the last space, and process a space + // character. + + if (RC_BAD( rc = m_pIStream->positionTo( ui64AfterLastSpacePos))) + { + goto Exit; + } + + uChar = ASCII_SPACE; + bLastCharWasSpace = FALSE; + } + else if (uChar == ASCII_BACKSLASH) + { + // If wildcards are allowed, the backslash should be treated + // as an escape character, and the next character is the one + // we want. Otherwise, it should be treated as + // the actual character we want returned. + + if (m_bMayHaveWildCards) + { + + // Got a backslash. Means the next character is to be taken + // no matter what because it is escaped. + + if (RC_BAD( rc = readCharFromStream( &uChar))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + uChar = ASCII_BACKSLASH; + } + } + } + else if (uChar == ASCII_WILDCARD) + { + if (m_bMayHaveWildCards && pbCharIsWild) + { + *pbCharIsWild = TRUE; + } + } + } + +Process_Char: + + if (!bAsian) + { + + // Must check for double characters if non-US and non-Asian + // character set + + if (m_uiLanguage != XFLM_US_LANG) + { + if (RC_BAD( rc = flmWPCheckDoubleCollation( + m_pIStream, m_bUnicodeStream, bAllowTwoIntoOne, + &uChar, &m_uNextChar, &bTwoIntoOne, m_uiLanguage))) + { + goto Exit; + } + } + } + else + { + if (RC_BAD( rc = readCharFromStream( &m_uNextChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + m_uNextChar = 0; + } + else + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + } + } + + // Convert each character to its WP equivalent + + if (!flmUnicodeToWP( uChar, &ui16WpChar)) + { + ui16WpChar = 0; + } + + if (!flmUnicodeToWP( m_uNextChar, &ui16NextWpChar)) + { + ui16NextWpChar = 0; + } + + // If we have an unconvertible UNICODE character, the collation + // value for it will be COLS0 + + if (!ui16WpChar) + { + if (!bAsian) + { + ui16Col = COLS0; + } + else + { + if (uChar < 0x20) + { + ui16Col = 0xFFFF; + ui16SubCol = uChar; + } + else + { + ui16Col = uChar; + ui16SubCol = 0; + } + } + } + else + { + if (!bAsian) + { + ui16Col = flmWPGetCollation( ui16WpChar, m_uiLanguage); + if (bTwoIntoOne) + { + // Since two characters were merged into one, increment + // the collation value by one. In the case of something + // like 'ch', there is a collation value between 'c' and + // 'd'. flmWPGetCollation would have returned the + // collation value for 'c' ... incrementing by one gives + // us the proper collation value for 'ch' (i.e., the + // collation value between 'c' and 'd'). + + ui16Col++; + } + } + else + { + if (flmWPAsiaGetCollation( ui16WpChar, ui16NextWpChar, ui16Col, + &ui16Col, &ui16SubCol, &ucCase, !m_bCaseSensitive) == 2) + { + + // Next character was consumed by collation + + m_uNextChar = 0; + } + } + } + + if (pui16Col) + { + *pui16Col = ui16Col; + } + + // Consume m_uNextChar if two characters merged into one + + if (bTwoIntoOne) + { + m_uNextChar = 0; + } + + // Subcollation + + if( pui16SubCol) + { + if( uChar > 127 && !bAsian) + { + ui16SubCol = ui16WpChar + ? flmWPGetSubCol( ui16WpChar, ui16Col, m_uiLanguage) + : uChar; + + if( !m_bCaseSensitive) + { + // If the sub-collation value is the original + // character, it means that the collation could not + // distinguish the characters and sub-collation is being + // used to do it. However, this creates a problem when the + // characters are the same character except for case. In that + // scenario, we incorrectly return a not-equal when we are + // doing a case-insensitive comparison. So, at this point, + // we need to use the sub-collation for the upper-case of the + // character instead of the sub-collation for the character + // itself. + + if( ui16WpChar && ui16SubCol == ui16WpChar) + { + ui16SubCol = flmWPGetSubCol( + flmWPUpper( ui16WpChar), + ui16Col, m_uiLanguage); + } + } + } + + *pui16SubCol = ui16SubCol; + } + + // Case + + if( pucCase) + { + if (!m_bCaseSensitive) + { + *pucCase = 0; + } + else + { + if (!bAsian && ui16WpChar) + { + // flmWPIsUpper() returns FALSE if the character is lower or + // TRUE if the character is not lower case. + + if( flmWPIsUpper( ui16WpChar)) + { + if( bTwoIntoOne) + { + if( flmWPIsUpper( ui16NextWpChar)) + { + ucCase = 0x03; + } + else + { + ucCase = 0x10; + } + } + else + { + ucCase = 0x01; + } + } + } + *pucCase = ucCase; + } + } + + if (puChar) + { + *puChar = uChar; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DbSystem::compareUTF8Strings( + const FLMBYTE * pucLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMBYTE * pucRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream bufferLStream; + F_BufferIStream bufferRStream; + F_CollIStream lStream; + F_CollIStream rStream; + + if (RC_BAD( rc = bufferLStream.open( pucLString, uiLStrBytes))) + { + goto Exit; + } + + if( RC_BAD( rc = bufferRStream.open( pucRString, uiRStrBytes))) + { + goto Exit; + } + + if( RC_BAD( rc = lStream.open( &bufferLStream, FALSE, uiLanguage, + uiCompareRules, bLeftWild))) + { + goto Exit; + } + + if( RC_BAD( rc = rStream.open( &bufferRStream, FALSE, uiLanguage, + uiCompareRules, bRightWild))) + { + goto Exit; + } + + if( RC_BAD( rc = fqCompareCollStreams( &lStream, &rStream, + (bLeftWild || bRightWild) ? TRUE : FALSE, + uiLanguage, piResult))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DbSystem::compareUnicodeStrings( + const FLMUNICODE * puzLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMUNICODE * puzRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream bufferLStream; + F_BufferIStream bufferRStream; + F_CollIStream lStream; + F_CollIStream rStream; + + if( RC_BAD( rc = bufferLStream.open( (FLMBYTE *)puzLString, uiLStrBytes))) + { + goto Exit; + } + + if( RC_BAD( rc = bufferRStream.open( (FLMBYTE *)puzRString, uiRStrBytes))) + { + goto Exit; + } + + if( RC_BAD( rc = lStream.open( &bufferLStream, TRUE, uiLanguage, + uiCompareRules, bLeftWild))) + { + goto Exit; + } + + if( RC_BAD( rc = rStream.open( &bufferRStream, TRUE, uiLanguage, + uiCompareRules, bRightWild))) + { + goto Exit; + } + + if( RC_BAD( rc = fqCompareCollStreams( &lStream, &rStream, + (bLeftWild || bRightWild) ? TRUE : FALSE, uiLanguage, piResult))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::utf8IsSubStr( + const FLMBYTE * pszString, + const FLMBYTE * pszSubString, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMBOOL * pbExists) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iResult = 0; + FLMBYTE * pszSearch = NULL; + FLMUINT uiSubStringLen = f_strlen( pszSubString); + + if( RC_BAD( rc = f_alloc( uiSubStringLen + 3, &pszSearch))) + { + goto Exit; + } + + pszSearch[0] = '*'; + f_memcpy( &pszSearch[ 1], pszSubString, uiSubStringLen); + pszSearch[ uiSubStringLen + 1] = '*'; + pszSearch[ uiSubStringLen + 2] = '\0'; + + if( RC_BAD( rc = compareUTF8Strings( + pszString, f_strlen( pszString), FALSE, pszSearch, + uiSubStringLen + 2, TRUE, uiCompareRules, uiLanguage, &iResult))) + { + goto Exit; + } + + *pbExists = (iResult)?FALSE:TRUE; + +Exit: + + if( pszSearch) + { + f_free( &pszSearch); + } + + return( rc); +} diff --git a/version5/src/fqsort.cpp b/version5/src/fqsort.cpp new file mode 100644 index 0000000..49e2e2c --- /dev/null +++ b/version5/src/fqsort.cpp @@ -0,0 +1,2772 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for doing sorting in the F_Query class. +// +// Tabs: 3 +// +// Copyright (c) 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: fqsort.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fquery.h" + +FSTATIC RCODE fqGetDocId( + IXD * pIxd, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT64 * pui64DocId); + +/*************************************************************************** +Desc: Add a sort key to the query. +***************************************************************************/ +RCODE XFLMAPI F_Query::addSortKey( + void * pvSortKeyContext, + FLMBOOL bChildToContext, + FLMBOOL bElement, + FLMUINT uiDictNum, + FLMUINT uiCompareRules, + FLMUINT uiLimit, + FLMUINT uiKeyComponent, + FLMBOOL bSortDescending, + FLMBOOL bSortMissingHigh, + void ** ppvContext) +{ + RCODE rc = NE_XFLM_OK; + ICD * pSortIcd; + ICD * pSortIcdContext; + ICD * pTmpIcd; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (m_bOptimized) + { + rc = RC_SET( NE_XFLM_Q_TOO_LATE_TO_ADD_SORT_KEYS); + goto Exit; + } + + // Verify that the sort key component number is legal + + if (uiKeyComponent > XFLM_MAX_SORT_KEYS) + { + rc = RC_SET( NE_XFLM_Q_INVALID_SORT_KEY_COMPONENT); + goto Exit; + } + + // If we have not created an IXD, create one now. + + if (!m_pSortIxd) + { + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( IXD), (void **)&m_pSortIxd))) + { + goto Exit; + } + + // Assume single path - can be unset later on. + + m_pSortIxd->uiFlags |= IXD_SINGLE_PATH; + m_pSortIxd->uiCollectionNum = m_uiCollection; + } + + // Allocate an ICD structure. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( ICD), + (void **)&pSortIcd))) + { + goto Exit; + } + + pSortIcd->uiCdl = m_pSortIxd->uiNumIcds; + m_pSortIxd->uiNumIcds++; + pSortIcd->pIxd = m_pSortIxd; + pSortIcd->uiIndexNum = m_pSortIxd->uiIndexNum; + pSortIcd->uiDictNum = uiDictNum; + if (!bElement) + { + pSortIcd->uiFlags |= ICD_IS_ATTRIBUTE; + } + if ((pSortIcd->uiKeyComponent = uiKeyComponent) > 0) + { + pSortIcd->uiFlags |= (ICD_VALUE | ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET); + pSortIcd->uiCompareRules = uiCompareRules; + if (bSortDescending) + { + pSortIcd->uiFlags |= ICD_DESCENDING; + } + if (bSortMissingHigh) + { + pSortIcd->uiFlags |= ICD_MISSING_HIGH; + } + if (!uiLimit) + { + pSortIcd->uiLimit = ICD_DEFAULT_LIMIT; + } + else + { + pSortIcd->uiLimit = uiLimit; + } + + m_pSortIxd->uiNumKeyComponents++; + + // Link into list of key components where it goes. + + pTmpIcd = m_pSortIxd->pFirstKey; + while (pTmpIcd && + pSortIcd->uiKeyComponent > pTmpIcd->uiKeyComponent) + { + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + + // Cannot have two components with the same value. + + if (pTmpIcd && pSortIcd->uiKeyComponent == pTmpIcd->uiKeyComponent) + { + rc = RC_SET( NE_XFLM_Q_DUPLICATE_SORT_KEY_COMPONENT); + goto Exit; + } + + if ((pSortIcd->pNextKeyComponent = pTmpIcd) == NULL) + { + + // Link at end of list. + + if ((pSortIcd->pPrevKeyComponent = m_pSortIxd->pLastKey) != NULL) + { + m_pSortIxd->pLastKey->pNextKeyComponent = pSortIcd; + } + else + { + m_pSortIxd->pFirstKey = pSortIcd; + } + m_pSortIxd->pLastKey = pSortIcd; + } + else + { + + // Link in front of pTmpIcd + + flmAssert( pSortIcd->uiKeyComponent < pTmpIcd->uiKeyComponent); + if ((pSortIcd->pPrevKeyComponent = + pTmpIcd->pPrevKeyComponent) == NULL) + { + m_pSortIxd->pFirstKey = pSortIcd; + } + else + { + pTmpIcd->pPrevKeyComponent->pNextKeyComponent = pSortIcd; + } + pTmpIcd->pPrevKeyComponent = pSortIcd; + } + } + else + { + m_pSortIxd->uiNumContextComponents++; + if ((pSortIcd->pPrevKeyComponent = m_pSortIxd->pLastContext) == NULL) + { + m_pSortIxd->pFirstContext = pSortIcd; + } + m_pSortIxd->pLastContext = pSortIcd; + } + + if ((pSortIcdContext = (ICD *)(pvSortKeyContext)) != NULL) + { + flmAssert( m_pSortIxd->pIcdTree); + if (bChildToContext) + { + + // Attributes cannot be parents to another sort ICD. + + if (pSortIcdContext->uiFlags & ICD_IS_ATTRIBUTE) + { + rc = RC_SET( NE_XFLM_Q_SORT_KEY_CONTEXT_MUST_BE_ELEMENT); + goto Exit; + } + pSortIcd->pParent = pSortIcdContext; + if ((pSortIcd->pNextSibling = pSortIcdContext->pFirstChild) != NULL) + { + pSortIcd->pNextSibling->pPrevSibling = pSortIcd; + } + pSortIcdContext->pFirstChild = pSortIcd; + if (pSortIcdContext->pNextSibling || pSortIcd->pPrevSibling) + { + m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); + } + } + else + { + pSortIcd->pParent = pSortIcdContext->pParent; + pSortIcd->pPrevSibling = pSortIcdContext; + if ((pSortIcd->pNextSibling = pSortIcdContext->pNextSibling) != NULL) + { + pSortIcd->pNextSibling->pPrevSibling = pSortIcd; + } + pSortIcdContext->pNextSibling = pSortIcd; + if (pSortIcdContext->pFirstChild) + { + m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); + } + } + } + else if (m_pSortIxd->pIcdTree) + { + pSortIcdContext = m_pSortIxd->pIcdTree; + while (pSortIcdContext->pNextSibling) + { + if (pSortIcdContext->pFirstChild) + { + m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); + } + pSortIcdContext = pSortIcdContext->pNextSibling; + } + if (pSortIcdContext->pFirstChild) + { + m_pSortIxd->uiFlags &= (~(IXD_SINGLE_PATH)); + } + pSortIcdContext->pNextSibling = pSortIcd; + pSortIcd->pPrevSibling = pSortIcdContext; + } + else + { + m_pSortIxd->pIcdTree = pSortIcd; + } + + if (ppvContext) + { + *ppvContext = (void *)pSortIcd; + } + +Exit: + + m_rc = rc; + return( rc); +} + +/*************************************************************************** +Desc: Verify the sort keys, if any. This method is called during + optimization. +***************************************************************************/ +RCODE F_Query::verifySortKeys( void) +{ + RCODE rc = NE_XFLM_OK; + ICD * pSortIcd; + FLMUINT uiExpectedKeyComponent; + + // This routine should not be called if there were no sort keys specified. + + flmAssert( m_pSortIxd); + + if ((pSortIcd = m_pSortIxd->pFirstKey) == NULL) + { + + // No sort keys were really specified. The user called addSortKey + // without ever setting one of the sort key components. + + m_pSortIxd = NULL; + rc = RC_SET( NE_XFLM_Q_NO_SORT_KEY_COMPONENTS_SPECIFIED); + goto Exit; + } + + // Set the language for the sort keys + + m_pSortIxd->uiLanguage = m_pDb->m_pDatabase->m_uiDefaultLanguage; + + // Verify that we don't have any missing sort key components, + // and that we can get all of their data types. + + uiExpectedKeyComponent = 1; + while (pSortIcd) + { + if (pSortIcd->uiKeyComponent != uiExpectedKeyComponent) + { + rc = RC_SET( NE_XFLM_Q_MISSING_SORT_KEY_COMPONENT); + goto Exit; + } + + // Verify the dictionary number and get its data type. + + if (pSortIcd->uiDictNum != ELM_ROOT_TAG) + { + F_AttrElmInfo info; + + if (!(pSortIcd->uiFlags & ICD_IS_ATTRIBUTE)) + { + if (RC_BAD( rc = m_pDb->m_pDict->getElement( m_pDb, + pSortIcd->uiDictNum, &info))) + { + if (rc == NE_XFLM_BAD_ELEMENT_NUM) + { + rc = RC_SET( NE_XFLM_Q_INVALID_ELEMENT_NUM_IN_SORT_KEYS); + } + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDb->m_pDict->getAttribute( m_pDb, + pSortIcd->uiDictNum, &info))) + { + if (rc == NE_XFLM_BAD_ATTRIBUTE_NUM) + { + rc = RC_SET( NE_XFLM_Q_INVALID_ATTR_NUM_IN_SORT_KEYS); + } + goto Exit; + } + } + icdSetDataType( pSortIcd, info.m_uiDataType); + } + + // Go to the next sort key component + + pSortIcd = pSortIcd->pNextKeyComponent; + uiExpectedKeyComponent++; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Add the current document to the result set. +***************************************************************************/ +RCODE F_Query::addToResultSet( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [XFLM_MAX_SORT_KEYS * 3 + 20]; + FLMBYTE * pucTmp; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiIDLen; + + if (m_pSortIxd) + { + // This routine should only be called if we have sort keys. + + m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); + + // Call index document to generate the sort keys for the document. + + if (RC_BAD( rc = m_pDb->indexDocument( m_pSortIxd, (F_DOMNode *)m_pCurrDoc))) + { + goto Exit; + } + + // Take only the lowest sort key for the document. + + if (m_pDb->m_uiKrefCount) + { + KREF_ENTRY * pKref; + + pKref = m_pDb->m_pKrefTbl [0]; + if (RC_BAD( rc = m_pSortResultSet->addEntry( (FLMBYTE *)&pKref [1], + (FLMUINT)pKref->ui16KeyLen, TRUE))) + { + goto Exit; + } + } + else + { + uiKeyLen = 2 * m_pSortIxd->uiNumKeyComponents; + + // Generate an empty key and add to the result set. + + f_memset( ucKey, 0, uiKeyLen); + + // Put in document ID - so we can pull it out when traversing + // through the result set. + + if (RC_BAD( rc = m_pCurrDoc->getDocumentId( (IF_Db *)m_pDb, &ui64DocId))) + { + goto Exit; + } + pucTmp = &ucKey [uiKeyLen]; + uiIDLen = flmEncodeSEN( ui64DocId, &pucTmp); + + // Output a zero SEN (which is one byte) for all of the other node IDs. + + f_memset( &ucKey [uiKeyLen + uiIDLen], + 0, m_pSortIxd->uiNumKeyComponents); + uiIDLen += m_pSortIxd->uiNumKeyComponents; + uiKeyLen += uiIDLen; + + if (RC_BAD( rc = m_pSortResultSet->addEntry( ucKey, uiKeyLen, + TRUE))) + { + goto Exit; + } + } + + // Empty the table out for next document. + + m_pDb->m_pKrefPool->poolReset( NULL, TRUE); + m_pDb->m_uiKrefCount = 0; + m_pDb->m_uiTotalKrefBytes = 0; + } + else + { + FLMUINT32 ui32Count = (FLMUINT32)m_pSortResultSet->getCount(); + + // If there is no sort key, the m_bPositioningEnabled flag better + // be set - meaning we are just keeping a result set with the objects + // in the order we found them. + + flmAssert( m_bPositioningEnabled); + ui32Count++; + longToByte( ui32Count, ucKey); + pucTmp = &ucKey [4]; + if (RC_BAD( rc = m_pCurrDoc->getDocumentId( (IF_Db *)m_pDb, &ui64DocId))) + { + goto Exit; + } + uiIDLen = flmEncodeSEN( ui64DocId, &pucTmp); + if (RC_BAD( rc = m_pSortResultSet->addEntry( ucKey, uiIDLen + 4, + TRUE))) + { + goto Exit; + } + } + m_ui64RSDocsPassed++; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Destructor for query result set. +***************************************************************************/ +F_QueryResultSet::~F_QueryResultSet() +{ + if (m_pBTree) + { + m_pBTree->btClose(); + m_pBTree->Release(); + } + if (m_pResultSetDb) + { + F_DbSystem dbSystem; + + if (m_pResultSetDb->getTransType() != XFLM_NO_TRANS) + { + m_pResultSetDb->transAbort(); + } + m_pResultSetDb->Release(); + m_pResultSetDb = NULL; + (void)dbSystem.dbRemove( m_szResultSetDibName, NULL, NULL, TRUE); + } + + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } +} + +/*************************************************************************** +Desc: Initialize a query result set. +***************************************************************************/ +RCODE F_QueryResultSet::initResultSet( + FLMBOOL bUseIxCompareObj, + FLMBOOL bEnableEncryption) +{ + RCODE rc = NE_XFLM_OK; + XFLM_CREATE_OPTS createOpts; + F_DbSystem dbSystem; + FLMUINT uiNum = (FLMUINT)this; + + flmAssert( !m_pResultSetDb); + + // Create a mutex. + + if (RC_BAD( f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + for (;;) + { + + // Generate a random file name + + f_sprintf( m_szResultSetDibName, "%x.db", (unsigned)uiNum); + if (RC_OK( rc = dbSystem.dbCreate( m_szResultSetDibName, NULL, NULL, + NULL, NULL, &createOpts, TRUE, (IF_Db **)&m_pResultSetDb))) + { + break; + } + if (rc == NE_XFLM_FILE_EXISTS || rc == NE_XFLM_IO_ACCESS_DENIED) + { + + // Try again with a slightly altered number. + + uiNum -= 10; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + // Shouldn't have an RFL object - don't want anything + // logged by this database. + + flmAssert( !m_pResultSetDb->getDatabase()->m_pRfl); + + // Create a b-tree that will hold our result set data. + // Will always be #1. + // Should be one that keeps counts so we can do absolute positioning. + + if (RC_BAD( rc = m_pResultSetDb->m_pDatabase->lFileCreate( m_pResultSetDb, + &m_LFile, NULL, 1, XFLM_LF_INDEX, TRUE, FALSE, + (FLMUINT)(bEnableEncryption ? 1 : 0)))) + { + goto Exit; + } + + // Open a b-tree object to read from and write to the btree. + + if ((m_pBTree = f_new F_Btree) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Open the btree and use the specified collection. + + if (RC_BAD( rc = m_pBTree->btOpen( m_pResultSetDb, + &m_LFile, TRUE, FALSE, + bUseIxCompareObj + ? &m_compareObj + : (IF_ResultSetCompare *)NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Add an entry to the result set. +***************************************************************************/ +RCODE F_QueryResultSet::addEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + // May need to lock the mutex when adding an entry - this will hold off + // any other thread that may be trying to read the result set at the + // same time. + + if (bLockMutex) + { + lockMutex(); + } + + m_pBTree->btResetBtree(); + + if (RC_BAD( rc = m_pBTree->btInsertEntry( pucKey, uiKeyLen, uiKeyLen, NULL, + 0, TRUE, TRUE))) + { + goto Exit; + } + m_uiCount++; + m_bPositioned = FALSE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the first entry in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::getFirst( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + + if (RC_BAD( rc = m_pBTree->btFirstEntry( pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + + // Remember our current position, so we can reposition there if necessary. + + if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) + { + goto Exit; + } + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the last entry in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::getLast( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + if (RC_BAD( rc = m_pBTree->btLastEntry( pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + + // Remember our current position, so we can reposition there if necessary. + + if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) + { + goto Exit; + } + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the next entry from the current position in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::getNext( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + if (m_uiCurrPos == FLM_MAX_UINT) + { + if (RC_BAD( rc = getFirst( pucKey, uiKeyBufSize, puiKeyLen, FALSE))) + { + goto Exit; + } + } + else + { + + // m_bPositioned will be set to FALSE if addEntry is called - because + // that changes the positioning of the b-tree. If that happens, we need + // to reposition before calling btNextEntry. + + if (!m_bPositioned) + { + if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, + pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + } + if (RC_BAD( rc = m_pBTree->btNextEntry( pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + + // Remember our current position, so we can reposition there if necessary. + + if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) + { + goto Exit; + } + } + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the previous entry from the current position in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::getPrev( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + if (m_uiCurrPos == FLM_MAX_UINT) + { + if (RC_BAD( rc = getLast( pucKey, uiKeyBufSize, puiKeyLen, FALSE))) + { + goto Exit; + } + } + else + { + + // m_bPositioned will be set to FALSE if addEntry is called - because + // that changes the positioning of the b-tree. If that happens, we need + // to reposition before calling btPrevEntry. + + if (!m_bPositioned) + { + if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, + pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + } + if (RC_BAD( rc = m_pBTree->btPrevEntry( pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + + // Remember our current position, so we can reposition there if necessary. + + if (RC_BAD( rc = m_pBTree->btGetPosition( pucKey, *puiKeyLen, &m_uiCurrPos))) + { + goto Exit; + } + } + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the entry from the current position in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::getCurrent( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + if (m_uiCurrPos == FLM_MAX_UINT) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + else + { + if (RC_BAD( rc = m_pBTree->btPositionTo( m_uiCurrPos, + pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + } + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Position to a particular entry in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::positionToEntry( + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + F_DataVector * pSearchKey, + FLMUINT uiFlags, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucSearchKey [MAX_KEY_SIZ]; + FLMUINT uiSearchKeyLen = 0; + FLMUINT uiOriginalFlags; + FLMUINT uiIdMatchFlags = uiFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); + FLMBOOL bCompareDocId = FALSE; + FLMBOOL bCompareNodeIds = FALSE; + FLMUINT uiPos; + + if (bLockMutex) + { + lockMutex(); + } + + if (uiFlags & XFLM_FIRST || (!pSearchKey && + !(uiFlags & XFLM_FIRST) && !(uiFlags & XFLM_LAST))) + { + uiOriginalFlags = uiFlags = XFLM_FIRST; + } + else if (uiFlags & XFLM_LAST) + { + uiOriginalFlags = uiFlags = XFLM_LAST; + } + else + { + uiOriginalFlags = uiFlags; + if( !(uiIdMatchFlags & XFLM_MATCH_IDS)) + { + flmAssert( !(uiFlags & XFLM_KEY_EXACT)); + if (uiFlags & XFLM_EXCL) + { + uiFlags = XFLM_EXCL; + } + else if (uiFlags & XFLM_EXACT) + { + uiOriginalFlags = XFLM_EXACT | XFLM_KEY_EXACT; + uiFlags = XFLM_INCL; + } + else + { + uiFlags = XFLM_INCL; + } + } + else + { + if (uiFlags & XFLM_EXACT) + { + flmAssert( !(uiFlags & XFLM_KEY_EXACT)); + uiFlags = XFLM_EXACT; + } + else if (uiFlags & XFLM_EXCL) + { + uiFlags = XFLM_EXCL; + } + else + { + uiFlags = XFLM_INCL; + } + } + + if (RC_BAD( rc = pSearchKey->outputKey( m_pIxd, uiIdMatchFlags, + ucSearchKey, sizeof( ucSearchKey), &uiSearchKeyLen, SEARCH_KEY_FLAG))) + { + goto Exit; + } + + // If we are not matching on the IDs and this is an XFLM_EXCL + // search, tack on a 0xFF for the IDs, which should get us past + // all keys that match. We need to turn on the match IDs flags + // in this case so that the comparison routine will match on the + // 0xFF. + + if (!uiIdMatchFlags && (uiFlags & XFLM_EXCL)) + { + ucSearchKey [uiSearchKeyLen++] = 0xFF; + bCompareDocId = TRUE; + bCompareNodeIds = TRUE; + } + else + { + if (uiIdMatchFlags & XFLM_MATCH_IDS) + { + bCompareNodeIds = TRUE; + bCompareDocId = TRUE; + } + else if (uiIdMatchFlags & XFLM_MATCH_DOC_ID) + { + bCompareDocId = TRUE; + } + } + } + + m_compareObj.setCompareNodeIds( bCompareNodeIds); + m_compareObj.setCompareDocId( bCompareDocId); + m_compareObj.setSearchKey( pSearchKey); + + // Search the for the key + + if (uiSearchKeyLen) + { + f_memcpy( pucKey, ucSearchKey, uiSearchKeyLen); + } + *puiKeyLen = uiSearchKeyLen; + + // NOTE: We don't pass m_uiCurrPos into btLocateEntry, because we may have to + // test below to see if we really got to something we can stay on. + // m_uiCurrPos should remain unchanged if we did not. + + if (RC_BAD( rc = m_pBTree->btLocateEntry( + pucKey, uiKeyBufSize, puiKeyLen, uiFlags, &uiPos, + NULL, NULL, NULL))) + { + if (rc == NE_XFLM_EOF_HIT && uiOriginalFlags & XFLM_EXACT) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + } + goto Exit; + } + + // See if we are in the same key + + if (uiOriginalFlags & XFLM_KEY_EXACT) + { + FLMINT iTmpCmp; + + if (RC_BAD( rc = ixKeyCompare( m_pSrcDb, m_pIxd, + pSearchKey, NULL, NULL, + (uiIdMatchFlags == XFLM_MATCH_DOC_ID) ? TRUE : FALSE, + FALSE, pucKey, *puiKeyLen, + ucSearchKey, uiSearchKeyLen, &iTmpCmp))) + { + goto Exit; + } + + if (iTmpCmp != 0) + { + rc = (uiOriginalFlags & (XFLM_INCL | XFLM_EXCL)) + ? RC_SET( NE_XFLM_EOF_HIT) + : RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + } + + m_bPositioned = TRUE; + m_uiCurrPos = uiPos; + +Exit: + + m_compareObj.setSearchKey( NULL); + m_compareObj.setCompareNodeIds( FALSE); + m_compareObj.setCompareDocId( FALSE); + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Position to a particular entry in the result set. +***************************************************************************/ +RCODE F_QueryResultSet::positionToEntry( + FLMUINT uiPosition, + FLMBYTE * pucKey, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMBOOL bLockMutex) +{ + RCODE rc = NE_XFLM_OK; + + if (bLockMutex) + { + lockMutex(); + } + if (uiPosition >= m_uiCount) + { + rc = RC_SET( NE_XFLM_Q_INVALID_POSITION); + goto Exit; + } + if (RC_BAD( rc = m_pBTree->btPositionTo( uiPosition, + pucKey, uiKeyBufSize, puiKeyLen))) + { + goto Exit; + } + m_uiCurrPos = uiPosition; + m_bPositioned = TRUE; + +Exit: + + if (bLockMutex) + { + unlockMutex(); + } + return( rc); +} + +/*************************************************************************** +Desc: Determine how much time is remaining on timer. +***************************************************************************/ +FINLINE RCODE getRemainingTimeMilli( + FLMUINT uiStartTimeTU, + FLMUINT uiTimeLimitTU, + FLMUINT * puiRemainingTimeMilli) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurrTimeTU; + FLMUINT uiElapsedTimeTU; + FLMUINT uiRemainingTimeTU; + + uiCurrTimeTU = FLM_GET_TIMER(); + uiElapsedTimeTU = FLM_ELAPSED_TIME( uiCurrTimeTU, uiStartTimeTU); + if (uiElapsedTimeTU >= uiTimeLimitTU) + { + rc = RC_SET( NE_XFLM_TIMEOUT); + goto Exit; + } + else + { + uiRemainingTimeTU = uiTimeLimitTU - uiElapsedTimeTU; + FLM_TIMER_UNITS_TO_MILLI( uiRemainingTimeTU, *puiRemainingTimeMilli); + } +Exit: + return( rc); +} + +/*************************************************************************** +Desc: Create and initialize the query result set. +***************************************************************************/ +RCODE F_Query::createResultSet( void) +{ + RCODE rc = NE_XFLM_OK; + + // Create a result set for the sort keys + + if ((m_pSortResultSet = f_new F_QueryResultSet) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = m_pSortResultSet->initResultSet( + m_pSortIxd ? TRUE : FALSE, + m_bEncryptResultSet))) + { + goto Exit; + } + + m_ui64RSDocsRead = 0; + m_ui64RSDocsPassed = 0; + if (!m_pSortIxd) + { + m_bEntriesAlreadyInOrder = TRUE; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Build the query result set. +***************************************************************************/ +RCODE F_Query::buildResultSet( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMUINT uiNumToWaitFor) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + RS_WAITER * pWaiter; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bDoneBuilding = FALSE; + FLMBOOL bNotifyWaiters = FALSE; + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + m_pSortResultSet->lockMutex(); + bMutexLocked = TRUE; + + // See if we have already built enough of the result set to get what + // we need from it. + + if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) + { + goto Exit; + } + + // Only one thread at a time can be actually be building the result set. + // This for loop waits for such a thread to build what it needs, or, if + // there is no thread running and the result set is not populated yet, + // it will build the result set out to the point it needs. + + for (;;) + { + + // If no other thread is building the result set, we can take over and + // do it. Otherwise, we will wait for that thread. + + if (!m_uiBuildThreadId) + { + m_uiBuildThreadId = f_threadId(); + bNotifyWaiters = TRUE; + break; + } + + // Thread is currently building the result set. It cannot + // be our thread. + + flmAssert( m_uiBuildThreadId != f_threadId()); + + // Just need to wait now. + + if (RC_BAD( rc = waitResultSetBuild( pDb, uiTimeLimit, uiNumToWaitFor))) + { + goto Exit; + } + + // See if the other thread built enough of the result set to get what + // we need from it. + + if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) + { + goto Exit; + } + + // At this point, we know that the result set is not built out far enough + // for us to get what we need, so we need to adjust our timeout. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + } + + if (bMutexLocked) + { + m_pSortResultSet->unlockMutex(); + bMutexLocked = FALSE; + } + + for (;;) + { + if (m_bStopBuildingResultSet) + { + bDoneBuilding = TRUE; + rc = RC_SET( NE_XFLM_USER_ABORT); + break; + } + if (RC_BAD( rc = getNext( pDb, &pNode, uiRemainingTimeMilli, 0, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + bDoneBuilding = TRUE; + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + // See how much time is left, if we are operating with a time limit. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + + // See if any of the waiters timed out. + + checkResultSetWaiters( NE_XFLM_OK); + if (m_pSortResultSet->getCount() >= uiNumToWaitFor) + { + break; + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (!bMutexLocked) + { + m_pSortResultSet->lockMutex(); + bMutexLocked = TRUE; + } + + if (!m_bResultSetPopulated && bDoneBuilding) + { + m_bResultSetPopulated = TRUE; + m_bPositioningEnabled = TRUE; + if (RC_OK( rc)) + { + if (m_pQueryStatus) + { + flmAssert( m_ui64RSDocsPassed == (FLMUINT64)m_pSortResultSet->getCount()); + rc = m_pQueryStatus->resultSetComplete( + m_ui64RSDocsRead, m_ui64RSDocsPassed); + } + } + } + + if (bNotifyWaiters) + { + + // Notify any waiters that are waiting. + + pWaiter = m_pFirstWaiter; + m_pFirstWaiter = NULL; + while (pWaiter) + { + F_SEM hESem = pWaiter->hESem; + + // Set waiter's return code to whatever we are returning. + + *(pWaiter->pRc) = rc; + + // Must get next waiter before signaling semaphore. + + pWaiter = pWaiter->pNext; + f_semSignal( hESem); + } + } + + // Must set m_uiBuildThreadId to 0 inside the + // mutex lock, because stopResultSetBuild() locks the mutex + // one last time to ensure that this method is no longer + // accessing anything inside the F_Query object. That is + // because it is fair game to destroy the F_Query object upon + // returning from this method. + // IMPORTANT NOTE: NOTHING SHOULD BE ACCESSED INSIDE THE F_Query + // OBJECT AFTER THE MUTEX IS UNLOCKED. + + m_uiBuildThreadId = 0; + + if (bMutexLocked) + { + m_pSortResultSet->unlockMutex(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Stop building the result set. +***************************************************************************/ +void XFLMAPI F_Query::stopBuildingResultSet( void) +{ + if (m_pSortResultSet) + { + m_pSortResultSet->lockMutex(); + + // If we have a thread currently building the + // result set, signal it to stop. + + if (m_uiBuildThreadId) + { + m_bStopBuildingResultSet = TRUE; + (void)waitResultSetBuild( m_pDb, 0, FLM_MAX_UINT); + + // waitResultSetBuild may temporarily unlock the + // mutex, but it will always be relocked upon + // returning. When we return this time, + // m_uiBuildThreadId should be zero. + + flmAssert( !m_uiBuildThreadId); + } + else + { + m_bResultSetPopulated = TRUE; + } + m_pSortResultSet->unlockMutex(); + } +} + +/*************************************************************************** +Desc: Build the query result set. This is the method that applications + can call. It implies enabling of positioning. +***************************************************************************/ +RCODE XFLMAPI F_Query::buildResultSet( + IF_Db * pDb, + FLMUINT uiTimeLimit) +{ + RCODE rc = NE_XFLM_OK; + + m_pDb = (F_Db *)pDb; + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + m_bPositioningEnabled = TRUE; + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + else if (!m_pSortResultSet) + { + + // Cannot build a result set for a query that was not + // set up to use result sets. + + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // If the query can never evaluate to TRUE, return EOF without + // doing anything. + + if (m_bEmpty) + { + m_eState = XFLM_QUERY_AT_BOF; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + + // At this point, we should have a result set created. + + flmAssert( m_pSortResultSet); + + // Build the entire result set. + + if (RC_BAD( rc = buildResultSet( pDb, uiTimeLimit, FLM_MAX_UINT))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Extract the document ID from the key. +***************************************************************************/ +FSTATIC RCODE fqGetDocId( + IXD * pIxd, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen, + FLMUINT64 * pui64DocId) +{ + RCODE rc = NE_XFLM_OK; + const FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; + + if (pIxd) + { + ICD * pIcd = pIxd->pFirstKey; + FLMUINT uiComponentLen; + + // Skip past all key components. Document ID will be right after them. + + while (pIcd && pucKey < pucKeyEnd) + { + if (pucKey + 2 > pucKeyEnd) + { + rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); + goto Exit; + } + uiComponentLen = getKeyComponentLength( pucKey); + pucKey += (2 + uiComponentLen); + pIcd = pIcd->pNextKeyComponent; + } + + if (pucKey >= pucKeyEnd) + { + rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); + goto Exit; + } + + // Should be positioned on document ID + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, pui64DocId))) + { + goto Exit; + } + } + else + { + + // If no sort index was defined, the key is a 4 byte sequence number, + // followed by the document id stored as a SEN. This is done so that + // it can be sorted with a memcmp. + + pucKey += 4; + if (pucKey >= pucKeyEnd) + { + rc = RC_SET( NE_XFLM_BAD_COLLATED_KEY); + goto Exit; + } + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, pui64DocId))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Check result set waiters to see if any should be notified that they + are done waiting or that they timed out or that there was an error. +***************************************************************************/ +void F_Query::checkResultSetWaiters( + RCODE rc) +{ + RS_WAITER * pWaiter; + FLMUINT uiCurrTime; + F_SEM hESem; + + if (m_pSortResultSet && !m_bResultSetPopulated && m_pFirstWaiter) + { + uiCurrTime = FLM_GET_TIMER(); + m_pSortResultSet->lockMutex(); + pWaiter = m_pFirstWaiter; + while (pWaiter) + { + + // First see if their count has been satisfied. + + if (RC_BAD( rc) || + m_pSortResultSet->getCount() >= pWaiter->uiNumToWaitFor) + { + hESem = pWaiter->hESem; + *(pWaiter->pRc) = rc; + + // Unlink the waiter from the list before signaling. + + if (pWaiter->pNext) + { + pWaiter->pNext->pPrev = pWaiter->pPrev; + } + if (pWaiter->pPrev) + { + pWaiter->pPrev->pNext = pWaiter->pNext; + } + else + { + m_pFirstWaiter = pWaiter->pNext; + } + pWaiter = pWaiter->pNext; + f_semSignal( hESem); + } + else if (pWaiter->uiTimeLimit && + FLM_ELAPSED_TIME( uiCurrTime, pWaiter->uiWaitStartTime) > + pWaiter->uiTimeLimit) + { + hESem = pWaiter->hESem; + *(pWaiter->pRc) = RC_SET( NE_XFLM_TIMEOUT); + + // Unlink the waiter from the list before signaling. + + if (pWaiter->pNext) + { + pWaiter->pNext->pPrev = pWaiter->pPrev; + } + if (pWaiter->pPrev) + { + pWaiter->pPrev->pNext = pWaiter->pNext; + } + else + { + m_pFirstWaiter = pWaiter->pNext; + } + pWaiter = pWaiter->pNext; + f_semSignal( hESem); + } + else + { + pWaiter = pWaiter->pNext; + } + } + m_pSortResultSet->unlockMutex(); + } +} + +/*************************************************************************** +Desc: Wait for the result set to get completely built. This routine assumes + that the result set mutex has already been locked. +***************************************************************************/ +RCODE F_Query::waitResultSetBuild( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMUINT uiNumToWaitFor) +{ + RCODE rc = NE_XFLM_OK; + RCODE TempRc; + RS_WAITER waiter; + FLMBOOL bMutexLocked = TRUE; + + // See if we are still building the result set, or if our + // count has been reached. + + if (m_pSortResultSet->getCount() >= uiNumToWaitFor || m_bResultSetPopulated) + { + goto Exit; + } + + // This should only be called if there is actually a thread currently + // building the result set. + + flmAssert( m_uiBuildThreadId && m_uiBuildThreadId != f_threadId()); + + waiter.uiThreadId = f_threadId(); + waiter.hESem = ((F_Db *)pDb)->m_hWaitSem; + waiter.pRc = &rc; + + if (uiTimeLimit) + { + waiter.uiWaitStartTime = FLM_GET_TIMER(); + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, waiter.uiTimeLimit); + } + else + { + waiter.uiWaitStartTime = 0; + waiter.uiTimeLimit = 0; + } + + waiter.uiNumToWaitFor = uiNumToWaitFor; + waiter.pPrev = NULL; + + if ((waiter.pNext = m_pFirstWaiter) != NULL) + { + m_pFirstWaiter->pPrev = &waiter; + } + + m_pFirstWaiter = &waiter; + rc = NE_XFLM_FAILURE; + m_pSortResultSet->unlockMutex(); + bMutexLocked = FALSE; + + if (RC_BAD( TempRc = f_semWait( waiter.hESem, F_SEM_WAITFOREVER))) + { + flmAssert( 0); + rc = TempRc; + } + else + { + + // Process that signaled us better set the rc to something + // besides NE_XFLM_FAILURE. + + if( rc == NE_XFLM_FAILURE) + { + flmAssert( 0); + } + } + +Exit: + + if (!bMutexLocked) + { + m_pSortResultSet->lockMutex(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the first item in the result set. +***************************************************************************/ +RCODE F_Query::getFirstFromResultSet( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiNumToWaitFor = 1; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + for (;;) + { + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // If the entries are not in order, we must wait for the entire result + // set to be populated. NOTE: If we are not sorting, they are, + // by definition, in order - we order them with a sequence number. + + if (!m_bEntriesAlreadyInOrder) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + + // If there are no entries in the result set yet, in order to get + // the first entry we must at least wait for the first one to + // appear. + + else if (m_pSortResultSet->getCount() < uiNumToWaitFor) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + uiNumToWaitFor))) + { + goto Exit; + } + } + } + + if (uiNumToWaitFor == 1) + { + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->getFirst( ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pSortResultSet->getNext( ucKey, sizeof( ucKey), + &uiKeyLen, + m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + } + + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + break; + } + + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // See how much time is left, if we are operating with a time limit. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + uiNumToWaitFor++; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the last item in the result set. +***************************************************************************/ +RCODE F_Query::getLastFromResultSet( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + FLMBOOL bGetLast = TRUE; + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + for (;;) + { + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // When getting the last entry, we must always wait for the result set + // to become fully populated. + + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (bGetLast) + { + if (RC_BAD( rc = m_pSortResultSet->getLast( ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pSortResultSet->getPrev( ucKey, sizeof( ucKey), + &uiKeyLen, + m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + break; + } + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // See how much time is left, if we are operating with a time limit. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + bGetLast = FALSE; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next item from the result set. +***************************************************************************/ +RCODE F_Query::getNextFromResultSet( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + FLMUINT uiNumSkipped; + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + if (!puiNumSkipped) + { + + // puiNumSkipped has to be non-NULL so it can be incremented only + // if uiNumToSkip > 1 + + if (uiNumToSkip > 1) + { + uiNumSkipped = 0; + puiNumSkipped = &uiNumSkipped; + } + } + else + { + *puiNumSkipped = 0; + } + for (;;) + { + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // If the entries are not in order, we must wait for the entire result + // set to be populated. NOTE: If we are not sorting, they are, + // by definition, in order - we order them with a sequence number. + + if (!m_bEntriesAlreadyInOrder) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + else + { + FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); + + // See if we have enough entries in the result set to position + // to the next entry. + + // If we are not yet positioned (uiCurrPos == FLM_MAX_UINT), + // we need to have at least one item in the result set. + // Otherwise, we need to have one more beyond uiCurrPos - which + // is (uiCurrPos + 1) + 1 --> uiCurrPos + 2. + + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + (uiCurrPos == FLM_MAX_UINT) ? 1 : uiCurrPos + 2))) + { + goto Exit; + } + } + } + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->getNext( ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip > 1) + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped < uiNumToSkip) + { + continue; + } + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + break; + } + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // See how much time is left, if we are operating with a time limit. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the previous item in the result set. +***************************************************************************/ +RCODE F_Query::getPrevFromResultSet( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + FLMUINT uiNumSkipped; + + if (!puiNumSkipped) + { + + // puiNumSkipped has to be non-NULL so it can be incremented only + // if uiNumToSkip > 1 + + if (uiNumToSkip > 1) + { + uiNumSkipped = 0; + puiNumSkipped = &uiNumSkipped; + } + } + else + { + *puiNumSkipped = 0; + } + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // If the entries are not in order, we must wait for the entire result + // set to be populated. NOTE: If we are not sorting, they are, + // by definition, in order - we order them with a sequence number. + + if (!m_bEntriesAlreadyInOrder) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + + // If there are no entries in the result set yet, in order to get + // the first entry we must at least wait for the first one to + // appear. + + else + { + FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); + + // See if we have enough entries in the result set to position + // to the previous entry. We should never be positioned beyond + // the number that are currently in there. So, at most, we should + // only have to wait for one to appear. + + if (uiCurrPos == FLM_MAX_UINT) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, 1))) + { + goto Exit; + } + } + else + { + + // Better never be beyond the current count. + + flmAssert( uiCurrPos < m_pSortResultSet->getCount()); + } + } + } + + for (;;) + { + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->getPrev( ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip > 1) + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped < uiNumToSkip) + { + continue; + } + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_OK( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + break; + } + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // See how much time is left, if we are operating with a time limit. + + if (uiTimeLimit) + { + if (RC_BAD( rc = getRemainingTimeMilli( uiStartTimeTU, uiTimeLimitTU, + &uiRemainingTimeMilli))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the current item in the result set. +***************************************************************************/ +RCODE F_Query::getCurrentFromResultSet( + IF_Db * ifpDb, + IF_DOMNode ** ppNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // If the entries are not in order, we must wait for the entire result + // set to be populated. But that means that we have never positioned + // anywhere yet, so we should return an error. + + if (!m_bEntriesAlreadyInOrder) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + goto Exit; + } + + // If there are no entries in the result set yet, in order to get + // the first entry we must at least wait for the first one to + // appear. + + else + { + FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); + + // It is an error to call this if we have not yet positioned + // anywhere in the result set. + + if (uiCurrPos == FLM_MAX_UINT) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + goto Exit; + } + else + { + + // Better never be beyond the current count. + + flmAssert( uiCurrPos < m_pSortResultSet->getCount()); + } + } + } + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->getCurrent( ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get previous node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::positionTo( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiPosition + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + + // If we have a result set, or are in the middle of creating it, + // we don't want to change the member variables because a background + // thread may be using them. + + if (!m_pSortResultSet) + { + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + // If we are not creating a result set, this is not a positionable + // query. + + if (!m_pSortResultSet) + { + rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); + goto Exit; + } + } + + // If we are in the middle of populating the result set, we may need to wait + // for entries to come into the result set. + + if (!m_bResultSetPopulated) + { + + // If the entries are not in order, we must wait for the entire result + // set to be populated before we can determine what will be + // the nth entry. NOTE: If we are not sorting, they are, + // by definition, in order - we order them with a sequence number. + + if (!m_bEntriesAlreadyInOrder) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + else + { + FLMUINT uiCurrPos = m_pSortResultSet->getCurrPos(); + + // See if we have the nth entry yet. + + if (uiCurrPos == FLM_MAX_UINT || + m_pSortResultSet->getCount() < uiPosition + 1) + { + // Must wait for uiPosition + 1, because uiPosition is zero based. + + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + uiPosition + 1))) + { + goto Exit; + } + } + } + } + + if (m_pSortIxd) + { + m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); + } + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->positionToEntry( uiPosition, + ucKey, sizeof( ucKey), + &uiKeyLen, m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get previous node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::positionTo( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + IF_DataVector * pSearchKey, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT64 ui64DocId; + FLMUINT uiStartTimeTU = 0; + FLMUINT uiRemainingTimeMilli = 0; + FLMUINT uiTimeLimitTU = 0; + + // If we have a result set, or are in the middle of creating it, + // we don't want to change the member variables because a background + // thread may be using them. + + if (!m_pSortResultSet) + { + + // NOTE: uiTimeLimit will always be zero when building the result + // set in another thread. + + if (uiTimeLimit) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, uiTimeLimitTU); + uiStartTimeTU = FLM_GET_TIMER(); + uiRemainingTimeMilli = uiTimeLimit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + // If no sort keys were specified, we cannot do this kind of + // positioning. + + if (!m_pSortIxd) + { + rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); + goto Exit; + } + + if (!m_pSortResultSet) + { + //visit: Need to allow something here for indexes where we really don't + //care about a result set. Positioning to a particular key is still possible + //in that case. + rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); + goto Exit; + } + } + + // If we are in the middle of populating the result set, and the entries + // are not in order, we need to wait for all entries to + // come into the result set. + //VISIT: Should we just wait for the entire result set even if the entries + //are in order? + + if (!m_bResultSetPopulated && !m_bEntriesAlreadyInOrder) + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiRemainingTimeMilli, + FLM_MAX_UINT))) + { + goto Exit; + } + } + + // Need to set the result set's index and db. + + m_pSortResultSet->setIxInfo( m_pDb, m_pSortIxd); + + // If the result set is not yet populated, we need to lock the mutex + // before accessing it - because another thread is trying to populate it. + + if (RC_BAD( rc = m_pSortResultSet->positionToEntry( ucKey, sizeof( ucKey), + &uiKeyLen, (F_DataVector *)pSearchKey, uiFlags, + m_bResultSetPopulated ? FALSE : TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = fqGetDocId( m_pSortIxd, ucKey, uiKeyLen, &ui64DocId))) + { + goto Exit; + } + if (RC_BAD( rc = ifpDb->getNode( m_uiCollection, ui64DocId, ppNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get current position. +***************************************************************************/ +RCODE XFLMAPI F_Query::getPosition( + IF_Db * ifpDb, + FLMUINT * puiPosition + ) +{ + RCODE rc = NE_XFLM_OK; + + // If we do not have a result set, we will not be positioned anywhere. + + if (!m_pSortResultSet) + { + m_pDb = (F_Db *)ifpDb; + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + if (!m_pSortResultSet) + { + rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); + goto Exit; + } + *puiPosition = 0; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + + if ((*puiPosition = m_pSortResultSet->getCurrPos()) == FLM_MAX_UINT) + { + *puiPosition = 0; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get count. Only works for queries that are building result sets. +***************************************************************************/ +RCODE XFLMAPI F_Query::getCounts( + IF_Db * ifpDb, + FLMUINT uiTimeLimit, + FLMBOOL bPartialCountOk, + FLMUINT * puiReadCount, + FLMUINT * puiPassedCount, + FLMUINT * puiPositionableToCount, + FLMBOOL * pbDoneBuildingResultSet + ) +{ + RCODE rc = NE_XFLM_OK; + + // If we do not have a result set, we will not have a count. + + if (!m_pSortResultSet) + { + m_pDb = (F_Db *)ifpDb; + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + if (!m_pSortResultSet) + { + rc = RC_SET( NE_XFLM_Q_NON_POSITIONABLE_QUERY); + goto Exit; + } + } + + // If the result set is not yet populated, we need to let it become + // completely populated before we return a count. + + if (!m_bResultSetPopulated) + { + if (bPartialCountOk) + { + if (m_bEntriesAlreadyInOrder) + { + *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); + } + else + { + *puiPassedCount = m_pSortResultSet->getCount(); + *puiPositionableToCount = 0; + } + if (pbDoneBuildingResultSet) + { + *pbDoneBuildingResultSet = FALSE; + } + } + else + { + if (RC_BAD( rc = buildResultSet( ifpDb, uiTimeLimit, FLM_MAX_UINT))) + { + goto Exit; + } + *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); + if (pbDoneBuildingResultSet) + { + *pbDoneBuildingResultSet = TRUE; + } + } + } + else + { + *puiPositionableToCount = *puiPassedCount = m_pSortResultSet->getCount(); + if (pbDoneBuildingResultSet) + { + *pbDoneBuildingResultSet = TRUE; + } + } + + // Always set *puiReadCount last, in case it is being incremented on + // another thread. It will be ok for it to advance a little after having + // set the other two counters. It would look weird, however, if it was + // set first, and the other two counters ended up being greater than + // this one. + + *puiReadCount = (FLMUINT)m_ui64RSDocsRead; + +Exit: + + return( rc); +} + diff --git a/version5/src/fquery.cpp b/version5/src/fquery.cpp new file mode 100644 index 0000000..f2af386 --- /dev/null +++ b/version5/src/fquery.cpp @@ -0,0 +1,14317 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for F_Query class. +// +// 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: fquery.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fquery.h" +#include "fscursor.h" +#include "fdynsset.h" + +#define MIN_OPT_COST 8 + +static FLMUINT uiPrecedenceTable[ XFLM_RPAREN_OP - XFLM_AND_OP + 1] = +{ + 2, // XFLM_AND_OP + 1, // XFLM_OR_OP + 10, // XFLM_NOT_OP + 6, // XFLM_EQ_OP + 6, // XFLM_NE_OP + 6, // XFLM_APPROX_EQ_OP + 7, // XFLM_LT_OP + 7, // XFLM_LE_OP + 7, // XFLM_GT_OP + 7, // XFLM_GE_OP + 5, // XFLM_BITAND_OP + 3, // XFLM_BITOR_OP + 4, // XFLM_BITXOR_OP + 9, // XFLM_MULT_OP + 9, // XFLM_DIV_OP + 9, // XFLM_MOD_OP + 8, // XFLM_PLUS_OP + 8, // XFLM_MINUS_OP + 10, // XFLM_NEG_OP + 0, // XFLM_LPAREN_OP + 0 // XFLM_RPAREN_OP +}; + +FINLINE FLMUINT getPrecedence( + eQueryOperators eOperator) +{ + return( uiPrecedenceTable [eOperator - XFLM_AND_OP]); +} + +F_Pool::POOL_STATS gv_QueryPoolStats = {0,0}; + +// Local Function Prototypes + +FSTATIC void fqUnlinkFromParent( + FQNODE * pQNode); + +FSTATIC void fqLinkFirstChild( + FQNODE * pParent, + FQNODE * pChild); + +FSTATIC void fqLinkLastChild( + FQNODE * pParent, + FQNODE * pChild); + +FSTATIC void fqReplaceNode( + FQNODE * pNodeToReplace, + FQNODE * pReplacementNode); + +FSTATIC RCODE fqGetPosition( + FQVALUE * pQValue, + FLMUINT * puiPos); + +FSTATIC RCODE fqCompareValues( + FQVALUE * pValue1, + FLMBOOL bInclusive1, + FLMBOOL bNullIsLow1, + FQVALUE * pValue2, + FLMBOOL bInclusive2, + FLMBOOL bNullIsLow2, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piCmp); + +FSTATIC RCODE fqCheckUnionPredicates( + CONTEXT_PATH * pContextPath, + FLMUINT uiLanguage, + PATH_PRED * pPred); + +FSTATIC void fqClipContext( + OP_CONTEXT * pContext); + +FSTATIC void fqImportChildContexts( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext); + +FSTATIC void fqImportContextPaths( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext); + +FSTATIC void fqImportContext( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext); + +FSTATIC void fqMergeContexts( + FQNODE * pQNode, + OP_CONTEXT * pDestContext); + +FSTATIC void fqCheckPathMatch( + XPATH_COMPONENT * pXPathContextComponent, + XPATH_COMPONENT * pXPathComponent); + +FSTATIC FQNODE * fqEvalLogicalOperands( + FQNODE * pQNode); + +FSTATIC FQNODE * fqClipNotNode( + FQNODE * pQNode, + FQNODE ** ppExpr); + +FSTATIC RCODE fqGetNodeIdValue( + FQVALUE * pQValue); + +FSTATIC FLMBOOL haveChildKeyComponents( + ICD * pParentIcd); + +FSTATIC RCODE fqEvalOperator( + FLMUINT uiLanguage, + FQNODE * pQNode); + +FSTATIC void fqResetIterator( + FQNODE * pQNode, + FLMBOOL bFullRelease, + FLMBOOL bUseKeyNodes); + +FSTATIC RCODE fqGetValueFromNode( + F_Db * pDb, + IF_DOMNode * pNode, + FQVALUE * pQValue, + FLMUINT uiMetaDataType); + +FSTATIC void fqResetQueryTree( + FQNODE * pQueryTree, + FLMBOOL bUseKeyNodes, + FLMBOOL bResetAllXPaths); + +FSTATIC RCODE fqTryEvalOperator( + FLMUINT uiLanguage, + FQNODE ** ppCurrNode); + +FSTATIC FQNODE * fqBackupTree( + FQNODE * pCurrNode, + FLMBOOL * pbGetNodeValue); + +FSTATIC void fqReleaseQueryExpr( + FQNODE * pQNode); + +FSTATIC FLMBOOL fqTestValue( + FQNODE * pQueryExpr); + +FSTATIC RCODE fqGetValueFromKey( + FLMUINT uiDataType, + F_DataVector * pKey, + FQVALUE * pQValue, + FLMBYTE ** ppucValue, + FLMUINT uiValueBufSize); + +FSTATIC RCODE fqPredCompare( + FLMUINT uiLanguage, + PATH_PRED * pPred, + FQVALUE * pQValue, + FLMBOOL * pbPasses); + +FSTATIC void fqMarkXPathNodeListPassed( + PATH_PRED * pPred); + +FSTATIC int nodeIdCompareFunc( + void * pvData1, + void * pvData2, + void * pvUserData); + +/*************************************************************************** +Desc: Constructor +***************************************************************************/ +F_Query::F_Query() +{ + m_Pool.smartPoolInit( &gv_QueryPoolStats); + m_uiLanguage = XFLM_US_LANG; + m_uiCollection = XFLM_DATA_COLLECTION; + initVars(); +} + +/*************************************************************************** +Desc: Destructor +***************************************************************************/ +F_Query::~F_Query() +{ + clearQuery(); + m_Pool.poolFree(); +} + +/*************************************************************************** +Desc: Reset function +***************************************************************************/ +void F_Query::clearQuery( void) +{ + stopBuildingResultSet(); + resetQuery(); + + if (m_pDatabase) + { + m_pDatabase->lockMutex(); + + // Unlink the query from the list off of the F_Database object. + + if (m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + else + { + m_pDatabase->m_pFirstQuery = m_pNext; + } + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + else + { + m_pDatabase->m_pLastQuery = m_pPrev; + } + m_pDatabase->unlockMutex(); + } + + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + + if (m_ppObjectList) + { + while (m_uiObjectCount) + { + m_uiObjectCount--; + m_ppObjectList [m_uiObjectCount]->Release(); + m_ppObjectList [m_uiObjectCount] = NULL; + } + f_free( &m_ppObjectList); + } + + if (m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } + + if (m_pFSIndexCursor) + { + m_pFSIndexCursor->Release(); + m_pFSIndexCursor = NULL; + } + + // Clear all of the predicate cursors that may still be + // laying around. + + if (m_pQuery && m_pQuery->pContext) + { + OP_CONTEXT * pContext = m_pQuery->pContext; + CONTEXT_PATH * pContextPath; + PATH_PRED * pPred; + + for (;;) + { + + // Clear predicates of the context we are in. + + pContextPath = pContext->pFirstPath; + while (pContextPath) + { + pPred = pContextPath->pFirstPred; + while (pPred) + { + + // Predicate should only have either an index cursor + // or a collection cursor or an app. predicate. + + if (pPred->pFSIndexCursor) + { + flmAssert( !pPred->pFSCollectionCursor); + flmAssert( !pPred->pNodeSource); + pPred->pFSIndexCursor->Release(); + } + else if (pPred->pFSCollectionCursor) + { + flmAssert( !pPred->pFSIndexCursor); + flmAssert( !pPred->pNodeSource); + pPred->pFSCollectionCursor->Release(); + } + else if (pPred->pNodeSource) + { + flmAssert( !pPred->pFSIndexCursor); + flmAssert( !pPred->pFSCollectionCursor); + pPred->pNodeSource->releaseResources(); + } + pPred = pPred->pNext; + } + pContextPath = pContextPath->pNext; + } + + if (pContext->pFirstChild) + { + pContext = pContext->pFirstChild; + continue; + } + + // Go to sibling context, if any + + while (!pContext->pNextSib) + { + if ((pContext = pContext->pParent) == NULL) + { + break; + } + } + + // If pContext is NULL at this point, there are no + // more contexts. + + if (!pContext) + { + break; + } + pContext = pContext->pNextSib; + + // There has to have been a sibling context at this point. + + flmAssert( pContext); + } + } + if (m_pSortResultSet) + { + m_pSortResultSet->Release(); + m_pSortResultSet = NULL; + } + if (m_pQueryStatus) + { + m_pQueryStatus->Release(); + m_pQueryStatus = NULL; + } + if (m_pQueryValidator) + { + m_pQueryValidator->Release(); + m_pQueryValidator = NULL; + } + initVars(); +} + +/*************************************************************************** +Desc: Initialize all the member variables. +***************************************************************************/ +void F_Query::initVars( void) +{ + m_rc = NE_XFLM_OK; + m_bScan = FALSE; + m_bScanIndex = FALSE; + m_bResetAllXPaths = FALSE; + m_pFSIndexCursor = NULL; + m_bEmpty = FALSE; + m_pSortIxd = NULL; + m_pSortResultSet = NULL; + m_pFirstWaiter = NULL; + m_bStopBuildingResultSet = FALSE; + m_uiBuildThreadId = 0; + m_bPositioningEnabled = FALSE; + m_bResultSetPopulated = FALSE; + m_bEntriesAlreadyInOrder = FALSE; + m_bEncryptResultSet = FALSE; + m_eState = XFLM_QUERY_NOT_POSITIONED; + m_pQueryStatus = NULL; + m_pQueryValidator = NULL; + m_pDatabase = NULL; + m_pPrev = NULL; + m_pNext = NULL; + m_bOptimized = FALSE; + m_pCurrContext = NULL; + m_pCurrContextPath = NULL; + m_pCurrPred = NULL; + m_pExprXPathSource = NULL; + m_pQuery = NULL; + m_pCurrOpt = NULL; + m_pDb = NULL; + m_pCurExprState = NULL; + m_pCurrDoc = NULL; + m_pCurrNode = NULL; + m_ppObjectList = NULL; + m_uiObjectListSize = 0; + m_uiObjectCount = 0; + m_Pool.poolReset( NULL); + m_bRemoveDups = FALSE; + m_pDocIdSet = NULL; + m_uiIndex = 0; + m_bIndexSet = FALSE; + m_uiTimeLimit = 0; + m_uiStartTime = 0; +} + +/*************************************************************************** +Desc: Allocate a new expression evaluation state structure +***************************************************************************/ +RCODE F_Query::allocExprState( void) +{ + RCODE rc = NE_XFLM_OK; + EXPR_STATE * pExprState; + + if (!m_pCurExprState || !m_pCurExprState->pNext) + { + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( EXPR_STATE), + (void **)&pExprState))) + { + goto Exit; + } + if ((pExprState->pPrev = m_pCurExprState) != NULL) + { + m_pCurExprState->pNext = pExprState; + } + m_pCurExprState = pExprState; + } + else + { + EXPR_STATE * pSaveNext; + EXPR_STATE * pSavePrev; + + m_pCurExprState = m_pCurExprState->pNext; + + // Zero out everything except for the prev and next pointers + + pSaveNext = m_pCurExprState->pNext; + pSavePrev = m_pCurExprState->pPrev; + f_memset( m_pCurExprState, 0, sizeof( EXPR_STATE)); + m_pCurExprState->pNext = pSaveNext; + m_pCurExprState->pPrev = pSavePrev; + } + m_pCurExprState->uiNumExprNeeded = 1; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Unlinks a node from its parent and siblings. This routine assumes + that the test has already been made that the node has a parent. +***************************************************************************/ +FSTATIC void fqUnlinkFromParent( + FQNODE * pQNode + ) +{ + flmAssert( pQNode->pParent); + if (pQNode->pPrevSib) + { + pQNode->pPrevSib->pNextSib = pQNode->pNextSib; + } + else + { + pQNode->pParent->pFirstChild = pQNode->pNextSib; + } + if (pQNode->pNextSib) + { + pQNode->pNextSib->pPrevSib = pQNode->pPrevSib; + } + else + { + pQNode->pParent->pLastChild = pQNode->pPrevSib; + } + + pQNode->pParent = NULL; + pQNode->pPrevSib = NULL; + pQNode->pNextSib = NULL; +} + +/*************************************************************************** +Desc: Links one FQNODE as the first child of another. Will unlink the + child node from any parent it may be linked to. +***************************************************************************/ +FSTATIC void fqLinkFirstChild( + FQNODE * pParent, + FQNODE * pChild + ) +{ + + // If necessary, unlink the child from parent and siblings + + if (pChild->pParent) + { + fqUnlinkFromParent( pChild); + } + + // Link child as the first child to parent + + pChild->pParent = pParent; + pChild->pPrevSib = NULL; + if ((pChild->pNextSib = pParent->pFirstChild) != NULL) + { + pChild->pNextSib->pPrevSib = pChild; + } + else + { + pParent->pLastChild = pChild; + } + pParent->pFirstChild = pChild; +} + +/*************************************************************************** +Desc: Links one FQNODE as the last child of another. Will unlink the + child node from any parent it may be linked to. +***************************************************************************/ +FSTATIC void fqLinkLastChild( + FQNODE * pParent, + FQNODE * pChild + ) +{ + + // If necessary, unlink the child from parent and siblings + + if (pChild->pParent) + { + fqUnlinkFromParent( pChild); + } + + // Link child as the last child to parent + + pChild->pParent = pParent; + pChild->pNextSib = NULL; + if ((pChild->pPrevSib = pParent->pLastChild) != NULL) + { + pChild->pPrevSib->pNextSib = pChild; + } + else + { + pParent->pFirstChild = pChild; + } + pParent->pLastChild = pChild; +} + +/**************************************************************************** +Desc: Replace one node with another node in the tree. +****************************************************************************/ +FSTATIC void fqReplaceNode( + FQNODE * pNodeToReplace, + FQNODE * pReplacementNode + ) +{ + FQNODE_p pParentNode; + FLMBOOL bLinkAsFirst = (pNodeToReplace->pNextSib) ? TRUE : FALSE; + + if (pReplacementNode->pParent) + { + fqUnlinkFromParent( pReplacementNode); + } + if ((pParentNode = pNodeToReplace->pParent) != NULL) + { + fqUnlinkFromParent( pNodeToReplace); + if (bLinkAsFirst) + { + fqLinkFirstChild( pParentNode, pReplacementNode); + } + else + { + fqLinkLastChild( pParentNode, pReplacementNode); + } + } +} + +/*************************************************************************** +Desc: Allocate a value node +***************************************************************************/ +RCODE F_Query::allocValueNode( + FLMUINT uiValLen, + eValTypes eValType, + FQNODE ** ppQNode + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (!m_pCurExprState) + { + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + } + + if (m_pCurExprState->bExpectingLParen) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); + goto Exit; + } + + if (!expectingOperand()) + { + rc = RC_SET( NE_XFLM_Q_UNEXPECTED_VALUE); + goto Exit; + } + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), + (void **)ppQNode))) + { + goto Exit; + } + pQNode = *ppQNode; + pQNode->eNodeType = FLM_VALUE_NODE; + pQNode->currVal.eValType = eValType; + pQNode->currVal.uiDataLen = uiValLen; + pQNode->currVal.uiFlags = VAL_IS_CONSTANT; + + // For string and binary data, allocate a buffer. + + if (uiValLen && + (eValType == XFLM_UTF8_VAL || eValType == XFLM_BINARY_VAL)) + { + if (RC_BAD( rc = m_Pool.poolAlloc( uiValLen, + (void **)&pQNode->currVal.val.pucBuf))) + { + goto Exit; + } + } + + if (m_pCurExprState->pCurOperatorNode) + { + fqLinkLastChild( m_pCurExprState->pCurOperatorNode, pQNode); + } + else + { + flmAssert( !m_pCurExprState->pExpr); + m_pCurExprState->pExpr = pQNode; + } + m_pCurExprState->bExpectingOperator = TRUE; + m_pCurExprState->pLastNode = pQNode; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Adds a unicode value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addUnicodeValue( + const FLMUNICODE * puzVal) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + FLMUINT uiValLen; + FLMUINT uiCharCount; + FLMUINT uiSenLen; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (RC_BAD( rc = flmUnicode2Storage( puzVal, 0, NULL, + &uiValLen, &uiCharCount))) + { + goto Exit; + } + + if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_UTF8_VAL, &pQNode))) + { + goto Exit; + } + + if (uiValLen) + { + FLMBOOL bHaveWildCards; + const FLMUNICODE * puzTmp; + + // See if there are wildcards + + puzTmp = puzVal; + bHaveWildCards = FALSE; + while (*puzTmp) + { + if (*puzTmp == ASCII_BACKSLASH) + { + + // Skip over the next character no matter what + // because it is escaped. + + puzTmp++; + if (*puzTmp == 0) + { + break; + } + } + else if (*puzTmp == ASCII_WILDCARD) + { + bHaveWildCards = TRUE; + break; + } + puzTmp++; + } + + if (RC_BAD( rc = flmUnicode2Storage( puzVal, uiCharCount, + pQNode->currVal.val.pucBuf, + &pQNode->currVal.uiDataLen, &uiCharCount))) + { + goto Exit; + } + + // Skip past the SEN + + if (RC_BAD( rc = flmGetCharCountFromStorageBuf( + (const FLMBYTE **)&pQNode->currVal.val.pucBuf, + pQNode->currVal.uiDataLen, NULL, &uiSenLen))) + { + goto Exit; + } + + pQNode->currVal.uiDataLen -= uiSenLen; + if (bHaveWildCards) + { + pQNode->currVal.uiFlags |= VAL_HAS_WILDCARDS; + } + } + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds a UTF8 value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addUTF8Value( + const char * pszVal, + FLMUINT uiUTF8Len) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + FLMUINT uiValLen; + FLMUINT uiSenLen; + FLMBYTE * pucEnd = NULL; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (RC_BAD( rc = flmUTF8ToStorage( + (FLMBYTE *)pszVal, uiUTF8Len, NULL, &uiValLen))) + { + goto Exit; + } + + if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_UTF8_VAL, &pQNode))) + { + goto Exit; + } + + if (uiValLen) + { + FLMBOOL bHaveWildCards; + const FLMBYTE * pszTmp; + FLMUNICODE uzChar; + + // See if there are wildcards + + pszTmp = (FLMBYTE *)pszVal; + if (uiUTF8Len) + { + pucEnd = (FLMBYTE *)pszVal + uiUTF8Len; + } + bHaveWildCards = FALSE; + for (;;) + { + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pszTmp, pucEnd, &uzChar))) + { + goto Exit; + } + + if (uzChar == ASCII_BACKSLASH) + { + + // Skip over the next character no matter what + // because it is escaped. + + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pszTmp, pucEnd, &uzChar))) + { + goto Exit; + } + if (!uzChar) + { + break; + } + } + else if (uzChar == ASCII_WILDCARD) + { + bHaveWildCards = TRUE; + break; + } + + if (!uzChar) + { + break; + } + } + + if (RC_BAD( rc = flmUTF8ToStorage( (FLMBYTE *)pszVal, + uiUTF8Len, (FLMBYTE *)pQNode->currVal.val.pucBuf, + &pQNode->currVal.uiDataLen))) + { + goto Exit; + } + + // Skip past the SEN + + if (RC_BAD( rc = flmGetCharCountFromStorageBuf( + (const FLMBYTE **)&pQNode->currVal.val.pucBuf, + pQNode->currVal.uiDataLen, NULL, &uiSenLen))) + { + goto Exit; + } + + pQNode->currVal.uiDataLen -= uiSenLen; + if (bHaveWildCards) + { + pQNode->currVal.uiFlags |= VAL_HAS_WILDCARDS; + } + } + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds a binary value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addBinaryValue( + const void * pvVal, + FLMUINT uiValLen) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( uiValLen, XFLM_BINARY_VAL, &pQNode))) + { + goto Exit; + } + + if (uiValLen) + { + f_memcpy( pQNode->currVal.val.pucBuf, pvVal, uiValLen); + } + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds a UINT value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addUINTValue( + FLMUINT uiVal + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( 0, XFLM_UINT_VAL, &pQNode))) + { + goto Exit; + } + pQNode->currVal.val.uiVal = uiVal; + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds an INT value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addINTValue( + FLMINT iVal + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( 0, XFLM_INT_VAL, &pQNode))) + { + goto Exit; + } + pQNode->currVal.val.iVal = iVal; + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds a UINT64 value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addUINT64Value( + FLMUINT64 ui64Val + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( 0, XFLM_UINT64_VAL, &pQNode))) + { + goto Exit; + } + pQNode->currVal.val.ui64Val = ui64Val; + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds an INT64 value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addINT64Value( + FLMINT64 i64Val + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( 0, XFLM_INT64_VAL, &pQNode))) + { + goto Exit; + } + pQNode->currVal.val.i64Val = i64Val; + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Adds a BOOL value to the query criteria. +***************************************************************************/ +RCODE XFLMAPI F_Query::addBoolean( + FLMBOOL bVal, + FLMBOOL bUnknown + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + + if (RC_BAD( rc = allocValueNode( 0, XFLM_BOOL_VAL, &pQNode))) + { + goto Exit; + } + pQNode->currVal.val.eBool = (XFlmBoolType)(bUnknown + ? XFLM_UNKNOWN + : (XFlmBoolType)(bVal + ? XFLM_TRUE + : XFLM_FALSE)); + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Add an XPATH component +***************************************************************************/ +RCODE XFLMAPI F_Query::addXPathComponent( + eXPathAxisTypes eXPathAxis, + eDomNodeType eNodeType, + FLMUINT uiDictNum, + IF_QueryNodeSource * pNodeSource + ) +{ + RCODE rc = NE_XFLM_OK; + XPATH_COMPONENT * pXPathComponent; + FXPATH * pXPath; + FQNODE * pQNode; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (!m_pCurExprState) + { + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + } + + // Must be expecting an operand, or the last node + // must be an XPATH + + if (!expectingOperand() && + m_pCurExprState->pLastNode->eNodeType != FLM_XPATH_NODE) + { + rc = RC_SET( NE_XFLM_Q_UNEXPECTED_XPATH_COMPONENT); + goto Exit; + } + + // If axis is META_AXIS, verify that the specific type of meta data + // requested is valid. + + if (eXPathAxis == META_AXIS) + { + switch (uiDictNum) + { + case XFLM_META_NODE_ID: + case XFLM_META_DOCUMENT_ID: + case XFLM_META_PARENT_ID: + case XFLM_META_FIRST_CHILD_ID: + case XFLM_META_LAST_CHILD_ID: + case XFLM_META_NEXT_SIBLING_ID: + case XFLM_META_PREV_SIBLING_ID: + case XFLM_META_VALUE: + break; + default: + rc = RC_SET( NE_XFLM_Q_INVALID_META_DATA_TYPE); + goto Exit; + } + } + + // Allocate an XPATH component + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( XPATH_COMPONENT), + (void **)&pXPathComponent))) + { + goto Exit; + } + pXPathComponent->eNodeType = eNodeType; + pXPathComponent->eXPathAxis = eXPathAxis; + pXPathComponent->uiDictNum = uiDictNum; + pXPathComponent->pNodeSource = pNodeSource; + if (m_pCurExprState->pPrev && + m_pCurExprState->pXPathComponent) + { + + // pXPathContext is the XPATH component context for this + // XPATH component. This may be used in optimization. + + pXPathComponent->pXPathContext = m_pCurExprState->pXPathComponent; + } + + // If we are not expecting an operand, then the last component + // has to be an XPATH node. + + if (!expectingOperand()) + { + pXPath = m_pCurExprState->pLastNode->nd.pXPath; + } + else + { + + // Need to allocate a node and an XPATH + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), + (void **)&pQNode))) + { + goto Exit; + } + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FXPATH), + (void **)&pXPath))) + { + goto Exit; + } + pQNode->eNodeType = FLM_XPATH_NODE; + pQNode->nd.pXPath = pXPath; + + // Link this node into the expression + + if (m_pCurExprState->pCurOperatorNode) + { + fqLinkLastChild( m_pCurExprState->pCurOperatorNode, pQNode); + } + else + { + flmAssert( !m_pCurExprState->pExpr); + m_pCurExprState->pExpr = pQNode; + } + m_pCurExprState->bExpectingOperator = TRUE; + m_pCurExprState->pLastNode = pQNode; + } + pXPathComponent->pXPathNode = m_pCurExprState->pLastNode; + + // Link the new XPATH component into the XPATH as the last + // component. + + if ((pXPathComponent->pPrev = pXPath->pLastComponent) != NULL) + { + pXPath->pLastComponent->pNext = pXPathComponent; + } + else + { + pXPath->pFirstComponent = pXPathComponent; + } + pXPath->pLastComponent = pXPathComponent; + + if (pNodeSource) + { + if (RC_BAD( rc = objectAddRef( pNodeSource))) + { + goto Exit; + } + } + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Keep track of objects supplied by the application that we use + for callbacks, etc. +***************************************************************************/ +RCODE F_Query::objectAddRef( + XF_RefCount * pObject + ) +{ + RCODE rc = NE_XFLM_OK; + + // If object list is full, make room for 20 more + + if (m_uiObjectCount == m_uiObjectListSize) + { + if (RC_BAD( rc = f_realloc( sizeof( XFLMIUnknown *) * + (m_uiObjectListSize + 20), + &m_ppObjectList))) + { + goto Exit; + } + m_uiObjectListSize += 20; + } + m_ppObjectList [m_uiObjectCount++] = pObject; + pObject->AddRef(); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get a context position from a value. It must be a positive + integer. Anything else will cause an error to be returned. +***************************************************************************/ +FSTATIC RCODE fqGetPosition( + FQVALUE * pQValue, + FLMUINT * puiPos + ) +{ + RCODE rc = NE_XFLM_OK; + + switch (pQValue->eValType) + { + case XFLM_UINT_VAL: + if (!pQValue->val.uiVal) + { + rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); + goto Exit; + } + *puiPos = pQValue->val.uiVal; + break; + case XFLM_UINT64_VAL: + if (!pQValue->val.ui64Val || + pQValue->val.ui64Val > (FLMUINT64)(~((FLMUINT)0))) + { + rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); + goto Exit; + } + *puiPos = (FLMUINT)pQValue->val.ui64Val; + break; + case XFLM_INT_VAL: + if (pQValue->val.iVal <= 0) + { + rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); + goto Exit; + } + *puiPos = (FLMUINT)pQValue->val.iVal; + break; + case XFLM_INT64_VAL: + if (pQValue->val.i64Val <= 0 || + pQValue->val.i64Val > (FLMINT64)(~((FLMUINT)0))) + { + rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); + goto Exit; + } + *puiPos = (FLMUINT)pQValue->val.i64Val; + break; + default: + rc = RC_SET( NE_XFLM_Q_INVALID_CONTEXT_POS); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Determine if an XPATH component has a test for context position. +***************************************************************************/ +FINLINE FLMBOOL hasContextPosTest( + XPATH_COMPONENT * pXPathComponent + ) +{ + return( pXPathComponent->pContextPosExpr || + pXPathComponent->uiContextPosNeeded + ? TRUE + : FALSE); +} + +/*************************************************************************** +Desc: Add an operator to the query expression +***************************************************************************/ +RCODE XFLMAPI F_Query::addOperator( + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + FQEXPR * pQExpr; + FQNODE * pParentNode; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (!m_pCurExprState) + { + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + } + + // If we are expecting a left paren (for a function), that is + // the only thing that is acceptable at this point. + + if (m_pCurExprState->bExpectingLParen && eOperator != XFLM_LPAREN_OP) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); + goto Exit; + } + + switch (eOperator) + { + case XFLM_LPAREN_OP: + + // If the operator is a left paren, increment the nesting level + + if (expectingOperator()) + { + rc = RC_SET( NE_XFLM_Q_UNEXPECTED_LPAREN); + goto Exit; + } + m_pCurExprState->uiNestLevel++; + m_pCurExprState->bExpectingLParen = FALSE; + goto Exit; + + case XFLM_RPAREN_OP: + if (expectingOperand()) + { + rc = RC_SET( NE_XFLM_Q_UNEXPECTED_RPAREN); + goto Exit; + } + if (!m_pCurExprState->uiNestLevel) + { + rc = RC_SET( NE_XFLM_Q_UNMATCHED_RPAREN); + goto Exit; + } + m_pCurExprState->uiNestLevel--; + + // See if this is the right paren to a function call + + if (!m_pCurExprState->uiNestLevel && parsingFunction()) + { + + // If we have a valid expression, link it to the + // function node as its last parameter. + + if (m_pCurExprState->pExpr) + { + m_pCurExprState->uiNumExpressions++; + + // uiNumExprNeeded might be zero. + + if (m_pCurExprState->uiNumExpressions > + m_pCurExprState->uiNumExprNeeded) + { + rc = RC_SET( NE_XFLM_Q_INVALID_NUM_FUNC_ARGS); + goto Exit; + } + + // Allocate an expression node and link it to the + // function. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQEXPR), + (void **)&pQExpr))) + { + goto Exit; + } + if ((pQExpr->pPrev = + m_pCurExprState->pQFunction->pLastArg) != NULL) + { + pQExpr->pPrev->pNext = pQExpr; + } + else + { + m_pCurExprState->pQFunction->pFirstArg = pQExpr; + } + m_pCurExprState->pQFunction->pLastArg = pQExpr; + pQExpr->pExpr = m_pCurExprState->pExpr; + if (RC_BAD( rc = getPredicates( &pQExpr->pExpr, NULL, + m_pCurExprState->pXPathComponent))) + { + goto Exit; + } + + // If this is a user-defined function, make sure the + // expression parameter (there should only be one) is + // an XPATH expression, and that the getPredicates call + // didn't eliminate the expression. + + if (m_pCurExprState->pQFunction->pFuncObj) + { + if (!pQExpr->pExpr || + pQExpr->pExpr->eNodeType != FLM_XPATH_NODE) + { + rc = RC_SET( NE_XFLM_Q_INVALID_FUNC_ARG); + goto Exit; + } + } + } + + // See if we got the required number of arguments for + // the function + + if (m_pCurExprState->uiNumExpressions < + m_pCurExprState->uiNumExprNeeded) + { + rc = RC_SET( NE_XFLM_Q_INVALID_NUM_FUNC_ARGS); + goto Exit; + } + + // Return to the former context. + + m_pCurExprState = m_pCurExprState->pPrev; + } + goto Exit; + + case XFLM_NEG_OP: + case XFLM_NOT_OP: + if (expectingOperator()) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERATOR); + goto Exit; + } + break; + + case XFLM_COMMA_OP: + + // In order for a comma to be legal, the following conditions + // must be met: + // 1) Must be inside a function + // 2) Must need at least two arguments for the function + // 3) Must not already have enough arguments + // 4) Must be at nesting level 1 + // 5) Must be expecting an operator + // 6) Must have a non-empty expression we can link to + // function node. + + if (!parsingFunction() || + m_pCurExprState->uiNumExprNeeded < 2 || + m_pCurExprState->uiNumExpressions < + m_pCurExprState->uiNumExprNeeded - 1 || + m_pCurExprState->uiNestLevel > 1 || + expectingOperand() || + !m_pCurExprState->pExpr) + { + rc = RC_SET( NE_XFLM_Q_UNEXPECTED_COMMA); + goto Exit; + } + m_pCurExprState->uiNumExpressions++; + + // Allocate an expression node and link it to the + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQEXPR), + (void **)&pQExpr))) + { + goto Exit; + } + if ((pQExpr->pPrev = + m_pCurExprState->pQFunction->pLastArg) != NULL) + { + pQExpr->pPrev->pNext = pQExpr; + } + else + { + m_pCurExprState->pQFunction->pFirstArg = pQExpr; + } + m_pCurExprState->pQFunction->pLastArg = pQExpr; + if (RC_BAD( rc = getPredicates( &pQExpr->pExpr, NULL, + m_pCurExprState->pXPathComponent))) + { + goto Exit; + } + + // Reset the expression + + m_pCurExprState->pExpr = NULL; + m_pCurExprState->pCurOperatorNode = NULL; + + // The following conditions better already be set + + flmAssert( expectingOperand()); + flmAssert( !m_pCurExprState->bExpectingLParen); + flmAssert( m_pCurExprState->uiNestLevel == 1); + goto Exit; + + case XFLM_LBRACKET_OP: + + // Last node has to be an XPATH node + + if (m_pCurExprState->pLastNode->eNodeType != FLM_XPATH_NODE) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_LBRACKET); + goto Exit; + } + + // Save the last node into pQNode, because when we call allocExprState + // we will no longer be able to get at it. + + pQNode = m_pCurExprState->pLastNode; + + // Cannot add expressions if the last component already has + // an expression to test context position. + + if (hasContextPosTest( pQNode->nd.pXPath->pLastComponent)) + { + rc = RC_SET( NE_XFLM_Q_NEW_EXPR_NOT_ALLOWED); + goto Exit; + } + + // Always allocate a new expression state for an xpath expression + + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + + m_pCurExprState->pXPathComponent = pQNode->nd.pXPath->pLastComponent; + + goto Exit; + + case XFLM_RBRACKET_OP: + + // Right bracket is only allowed if we are parsing an + // xpath expression and we are at nesting level zero and + // we are not expecting an operand + + if (!parsingXPathExpr() || + m_pCurExprState->uiNestLevel || + (expectingOperand() && m_pCurExprState->pExpr)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_RBRACKET); + goto Exit; + } + + // If we have a non-empty expression, link it to the + // list of xpath component expressions off of the xpath + // component. + + if (m_pCurExprState->pExpr) + { + + // If the XPATH component does not have any expression yet, + // make this expression its expression. Otherwise, AND + // this expression to the existing expression. + + if (m_pCurExprState->pExpr->eNodeType == FLM_VALUE_NODE) + { + if (RC_BAD( rc = fqGetPosition( &m_pCurExprState->pExpr->currVal, + &m_pCurExprState->pXPathComponent->uiContextPosNeeded))) + { + goto Exit; + } + } + else if (m_pCurExprState->pExpr->eNodeType == FLM_OPERATOR_NODE && + isArithOp( m_pCurExprState->pExpr->nd.op.eOperator)) + { + m_pCurExprState->pXPathComponent->pContextPosExpr = + m_pCurExprState->pExpr; + if (RC_BAD( rc = getPredicates( + &m_pCurExprState->pXPathComponent->pContextPosExpr, + NULL, + m_pCurExprState->pXPathComponent))) + { + goto Exit; + } + + // If, after the optimization, we are left with a constant, + // NULL out pContextPosExpr and put it into uiContextPosNeeded. + + if (m_pCurExprState->pXPathComponent->pContextPosExpr->eNodeType == + FLM_VALUE_NODE) + { + if (RC_BAD( rc = fqGetPosition( + &m_pCurExprState->pXPathComponent->pContextPosExpr->currVal, + &m_pCurExprState->pXPathComponent->uiContextPosNeeded))) + { + goto Exit; + } + m_pCurExprState->pXPathComponent->pContextPosExpr = NULL; + } + } + else if (!m_pCurExprState->pXPathComponent->pExpr) + { + m_pCurExprState->pXPathComponent->pExpr = + m_pCurExprState->pExpr; + if (RC_BAD( rc = getPredicates( + &m_pCurExprState->pXPathComponent->pExpr, + NULL, + m_pCurExprState->pXPathComponent))) + { + goto Exit; + } + } + else + { + + // Create an AND node and link the existing expression with + // this new expression as children of this new AND node. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), + (void **)&pQNode))) + { + goto Exit; + } + pQNode->eNodeType = FLM_OPERATOR_NODE; + pQNode->nd.op.eOperator = XFLM_AND_OP; + + fqLinkFirstChild( pQNode, + m_pCurExprState->pXPathComponent->pExpr); + fqLinkLastChild( pQNode, m_pCurExprState->pExpr); + m_pCurExprState->pXPathComponent->pExpr = pQNode; + + // Set up a context node for the new AND node. + // If the left operand's context (which would have been + // set up previously by a call to getPredicates) is + // an intersect context, we can point this node + // right at it, and make the context's root node this + // new node. Otherwise, we have to create a new context + // and link the left operand's context to it. + + if (pQNode->pFirstChild->pContext->bIntersect) + { + pQNode->pContext = pQNode->pFirstChild->pContext; + pQNode->pContext->pQRootNode = pQNode; + } + else + { + if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) + { + goto Exit; + } + + // Put first child's context as child of this node's + // context. + + pQNode->pContext->pFirstChild = pQNode->pFirstChild->pContext; + pQNode->pContext->pLastChild = pQNode->pFirstChild->pContext; + pQNode->pFirstChild->pContext->pParent = pQNode->pContext; + } + + // Get the predicates of ONLY the right-hand side of the + // tree - because we haven't gotten its predicates yet, but + // the left hand side has already been done. + + if (RC_BAD( rc = getPredicates( + &m_pCurExprState->pXPathComponent->pExpr, + pQNode->pLastChild, + m_pCurExprState->pXPathComponent))) + { + goto Exit; + } + } + } + + // Return to the former context. + + m_pCurExprState = m_pCurExprState->pPrev; + + goto Exit; + + default: + + if (expectingOperand()) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERAND); + goto Exit; + } + if (!isLegalOperator( eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERATOR); + goto Exit; + } + + break; + } + + // Cannot set both XFLM_COMP_COMPRESS_WHITESPACE and XFLM_COMP_NO_WHITESPACE + // in comparison rules. Also, cannot set XFLM_COMP_IGNORE_LEADING_SPACE or + // XFLM_COMP_IGNORE_TRAILING_SPACE with XFLM_COMP_NO_WHITESPACE. + + if ((uiCompareRules & XFLM_COMP_NO_WHITESPACE) && + (uiCompareRules & (XFLM_COMP_COMPRESS_WHITESPACE | + XFLM_COMP_IGNORE_LEADING_SPACE | + XFLM_COMP_IGNORE_TRAILING_SPACE))) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_Q_ILLEGAL_COMPARE_RULES); + goto Exit; + } + + // Make a QNODE and find a place for it in the query tree + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), + (void **)&pQNode))) + { + goto Exit; + } + pQNode->eNodeType = FLM_OPERATOR_NODE; + pQNode->nd.op.eOperator = eOperator; + pQNode->nd.op.uiCompareRules = uiCompareRules; + pQNode->nd.op.pOpComparer = pOpComparer; + pQNode->uiNestLevel = m_pCurExprState->uiNestLevel; + + // If this is the first operator in the query, set the current operator + // to it and graft in the current operand as its child. + + if (!m_pCurExprState->pExpr) + { + m_pCurExprState->pExpr = pQNode; + m_pCurExprState->pCurOperatorNode = pQNode; + goto Exit; + } + + // Go up the stack until an operator whose nest level or precedence is < + // this one's is encountered, then link this one in as the last child + + pParentNode = m_pCurExprState->pCurOperatorNode; + while (pParentNode && + (pParentNode->uiNestLevel > pQNode->uiNestLevel || + (pParentNode->uiNestLevel == pQNode->uiNestLevel && + getPrecedence( pParentNode->nd.op.eOperator) >= + getPrecedence( eOperator)))) + { + pParentNode = pParentNode->pParent; + } + if (!pParentNode) + { + if (m_pCurExprState->pExpr) + { + fqLinkLastChild( pQNode, m_pCurExprState->pExpr); + } + m_pCurExprState->pExpr = pQNode; + } + else if (eOperator == XFLM_NOT_OP || eOperator == XFLM_NEG_OP) + { + + // Need to treat NOT and NEG as if they were operands. + + // Parent better be an operator. + + flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); + +#ifdef FLM_DEBUG + if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || + pParentNode->nd.op.eOperator == XFLM_NOT_OP) + { + + // Must have no children. + + flmAssert( pParentNode->pFirstChild == NULL); + } + else + { + + // Must only have one or zero children. + + flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); + } +#endif + + fqLinkLastChild( pParentNode, pQNode); + flmAssert( !m_pCurExprState->bExpectingOperator); + } + else + { + + // Parent better be an operator. + + flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); + + // Unlink last child of parent node and replace with this + // new node. The parent node better already have the correct + // number of children, or we are not parsing correctly. + + flmAssert( pParentNode->pFirstChild); + if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || + pParentNode->nd.op.eOperator == XFLM_NOT_OP) + { + + // Better only be one child. + + flmAssert( !pParentNode->pFirstChild->pNextSib); + + fqLinkLastChild( pQNode, pParentNode->pFirstChild); + } + else + { + + // Better only be two child nodes + + flmAssert( pParentNode->pFirstChild->pNextSib == + pParentNode->pLastChild); + fqLinkLastChild( pQNode, pParentNode->pLastChild); + } + fqLinkLastChild( pParentNode, pQNode); + } + + m_pCurExprState->pCurOperatorNode = pQNode; + m_pCurExprState->bExpectingOperator = FALSE; + m_pCurExprState->pLastNode = pQNode; + + if (pOpComparer) + { + if (RC_BAD( rc = objectAddRef( pOpComparer))) + { + goto Exit; + } + } + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Add a function to the query expression +***************************************************************************/ +RCODE XFLMAPI F_Query::addFunction( + eQueryFunctions eFunction, + IF_QueryValFunc * pFuncObj, + FLMBOOL bHaveXPathExpr) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode; + FQFUNCTION * pQFunction; + FQNODE * pParentNode; + XPATH_COMPONENT * pSaveXPathComponent; + + // If an error has already occurred, cannot add more to query. + + if (RC_BAD( rc = m_rc)) + { + goto Exit; + } + + if (!m_pCurExprState) + { + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + } + + // Must be expecting an operand + + if (expectingOperator()) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_OPERATOR); + goto Exit; + } + + // Allocate a function node + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), + (void **)&pQNode))) + { + goto Exit; + } + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQFUNCTION), + (void **)&pQFunction))) + { + goto Exit; + } + pQNode->nd.pQFunction = pQFunction; + pQNode->eNodeType = FLM_FUNCTION_NODE; + pQFunction->eFunction = eFunction; + pQFunction->pFuncObj = pFuncObj; + + // See if this is the first node in the expression. + + if (!m_pCurExprState->pExpr) + { + m_pCurExprState->pExpr = pQNode; + } + else + { + pParentNode = m_pCurExprState->pCurOperatorNode; + flmAssert( pParentNode); + + // Parent better be an operator, and better have room for + // function to be linked as one of its operands. + + flmAssert( pParentNode->eNodeType == FLM_OPERATOR_NODE); + + if (pParentNode->nd.op.eOperator == XFLM_NEG_OP || + pParentNode->nd.op.eOperator == XFLM_NOT_OP) + { + + // Better not have any children yet. + + flmAssert( !pParentNode->pFirstChild); + } + else + { + + // Better only have one child node. + + flmAssert( pParentNode->pFirstChild == pParentNode->pLastChild); + } + fqLinkLastChild( pParentNode, pQNode); + } + m_pCurExprState->pLastNode = pQNode; + pSaveXPathComponent = m_pCurExprState->pXPathComponent; + + // Always allocate a new expression state for a function + + if (RC_BAD( rc = allocExprState())) + { + goto Exit; + } + + // First thing we expect in this state is a left paren + + m_pCurExprState->bExpectingLParen = TRUE; + m_pCurExprState->pQFunction = pQFunction; + m_pCurExprState->pXPathComponent = pSaveXPathComponent; + if (pFuncObj) + { + if (RC_BAD( rc = objectAddRef( pFuncObj))) + { + goto Exit; + } + + // In this case, the expression must return a node. + + m_pCurExprState->uiNumExprNeeded = bHaveXPathExpr ? (FLMUINT)1 : (FLMUINT)0; + } + else + { + +//visit +// m_pCurExprState->uiNumExprNeeded = ??? - number specified by the function. + } + + // Parent state needs to be expecting an operator when we come out from + // parsing the function. + + m_pCurExprState->pPrev->bExpectingOperator = TRUE; + +Exit: + + m_rc = rc; + + return( rc); +} + +/*************************************************************************** +Desc: Compare two values +***************************************************************************/ +FSTATIC RCODE fqCompareValues( + FQVALUE * pValue1, + FLMBOOL bInclusive1, + FLMBOOL bNullIsLow1, + FQVALUE * pValue2, + FLMBOOL bInclusive2, + FLMBOOL bNullIsLow2, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piCmp) +{ + RCODE rc = NE_XFLM_OK; + + // We have already called fqCanCompare, so no need to do it here + + if (!pValue1) + { + if (!pValue2) + { + if (bNullIsLow2) + { + *piCmp = (FLMINT)(bNullIsLow1 ? 0 : 1); + } + else + { + *piCmp = (FLMINT)(bNullIsLow1 ? -1 : 0); + } + } + else + { + *piCmp = (FLMINT)(bNullIsLow1 ? -1 : 1); + } + goto Exit; + } + else if (!pValue2) + { + *piCmp = (FLMINT)(bNullIsLow2 ? 1 : -1); + goto Exit; + } + + if (RC_BAD( rc = fqCompare( pValue1, pValue2, + uiCompareRules, NULL, uiLanguage, piCmp))) + { + goto Exit; + } + + // If everything else is equal, the last distinguisher + // is the inclusive flags and which side of the + // value we are on if we are exclusive which is indicated + // by the bNullIsLow flags + + if (*piCmp == 0) + { + if (bInclusive1 != bInclusive2) + { + if (bNullIsLow1) + { + if (bNullIsLow2) + { + // *--> v1 + // o--> v2 v1 < v2 + + // o--> v1 + // *--> v2 v1 > v2 + + *piCmp = bInclusive1 ? -1 : 1; + } + else + { + // *--> v1 + // v2 <--o v1 > v2 + + // o--> v1 + // v2 <--* v1 > v2 + + *piCmp = 1; + } + } + else + { + if (bNullIsLow2) + { + // v1 <--* + // o--> v2 v1 < v2 + + // v1 <--o + // *--> v2 v1 < v2 + + *piCmp = -1; + } + else + { + // v1 <--* + // v2 <--o v1 > v2 + + // v1 <--o + // v2 <--* v1 < v2 + + *piCmp = bInclusive1 ? 1 : -1; + } + } + } + else if (!bInclusive1) + { + + // bInclusive2 is also FALSE + + if (bNullIsLow1) + { + if (!bNullIsLow2) + { + // o--> v1 + // v2 <--o v1 > v2 + *piCmp = 1; + } +// else +// { + // o--> v1 + // o--> v2 v1 == v2 + // *piCmp = 0; +// } + } + else + { + if (bNullIsLow2) + { + + // v1 <--o + // o--> v2 v1 < v2 + + *piCmp = -1; + } +// else +// { + // v1 <--o + // v2 <--o v1 == v2 + // *piCmp = 0; +// } + } + } +// else +// { + // bInclusive1 == TRUE && bInclusive2 == TRUE + // else case covers the cases where + // both are inclusive, in which case it + // doesn't matter which is low and which + // is high + + // v1 <--* + // *--> v2 v1 == v2 + + // v1 <--* + // v2 <--* v1 == v2 + + // *--> v1 + // v2 <--* v1 == v2 + + // *--> v1 + // *--> v2 v1 == v2 + +// } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Intersect a predicate into a context path. +***************************************************************************/ +RCODE F_Query::intersectPredicates( + CONTEXT_PATH * pContextPath, + FQNODE * pXPathNode, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue, + FLMBOOL * pbClipContext + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pPred; + FLMINT iCmp; + FLMBOOL bDoMatch; + + if (!pQValue || pQValue->eValType != XFLM_UTF8_VAL) + { + bDoMatch = FALSE; + + // Comparison rules don't matter for anything that is + // not text, so we normalize them to zero, so the test + // below to see if the comparison rule is the same as + // the comparison rule of the operator will work. + + uiCompareRules = 0; + } + else + { + bDoMatch = (eOperator == XFLM_EQ_OP && + (pQValue->uiFlags & VAL_IS_CONSTANT) && + (pQValue->uiFlags & VAL_HAS_WILDCARDS)) + ? TRUE + : FALSE; + } + + if ((pPred = pContextPath->pFirstPred) != NULL) + { + if (eOperator == XFLM_EXISTS_OP) + { + + // An exists operator will either + // merge with an existing predicate or + // cancel the whole thing out as an + // empty result. + + // If this predicate is not-exists, another + // predicate ANDed with this one can never + // return a result that will match, unless + // that predicate is also a not-exists, in + // which case, we simply combine this one + // with that one. + + if (bNotted) + { + if (pPred->eOperator != XFLM_EXISTS_OP || + !pPred->bNotted) + { + *pbClipContext = TRUE; + } + } + goto Exit; + } + else if (pPred->eOperator == XFLM_EXISTS_OP) + { + + // If the first predicate is an exists operator + // it will be the only one, because otherwise + // it will have been merged with another operator + // in the code just above. + + flmAssert( !pPred->pNext); + + // If the predicate is notted, another predicate + // ANDed with this one can never return a result. + + if (pPred->bNotted) + { + *pbClipContext = TRUE; + } + else + { + + // Change the predicate to the current + // operator. + + pPred->eOperator = eOperator; + pPred->pFromValue = pQValue; + pPred->bNotted = bNotted; + } + goto Exit; + } + else if ((eOperator == XFLM_EQ_OP && !bDoMatch) || + eOperator == XFLM_LE_OP || + eOperator == XFLM_LT_OP || + eOperator == XFLM_GE_OP || + eOperator == XFLM_GT_OP) + { + + // If there is range operator, there + // should only be one of them with the + // same compare rules, because they + // will all always be merged with these operators + // or they will cancel to yield an empty result + // when doing intersections. + + while (pPred) + { + if (pPred->eOperator == XFLM_RANGE_OP && + pPred->uiCompareRules == uiCompareRules) + { + FQVALUE * pFromValue; + FQVALUE * pUntilValue; + FLMBOOL bInclFrom; + FLMBOOL bInclUntil; + + pFromValue = (eOperator == XFLM_EQ_OP || + eOperator == XFLM_GE_OP || + eOperator == XFLM_GT_OP) + ? pQValue + : NULL; + pUntilValue = (eOperator == XFLM_EQ_OP || + eOperator == XFLM_LE_OP || + eOperator == XFLM_LT_OP) + ? pQValue + : NULL; + bInclFrom = (FLMBOOL)(eOperator == XFLM_EQ_OP || + eOperator == XFLM_GE_OP + ? TRUE + : FALSE); + bInclUntil = (FLMBOOL)(eOperator == XFLM_EQ_OP || + eOperator == XFLM_LE_OP + ? TRUE + : FALSE); + + // If the value type is not compatible with the predicate's + // value type, we cannot do the comparison, and there is + // no intersection. + + if (!fqCanCompare( pQValue, pPred->pFromValue) || + !fqCanCompare( pQValue, pPred->pUntilValue)) + { + *pbClipContext = TRUE; + } + else if (RC_BAD( rc = fqCompareValues( pFromValue, + bInclFrom, TRUE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp > 0) + { + + // From value is greater than predicate's from value. + // If the from value is also greater than the predicate's + // until value, we have no intersection. + + if (RC_BAD( rc = fqCompareValues( pFromValue, + bInclFrom, TRUE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + *pbClipContext = TRUE; + } + else + { + pPred->pFromValue = pFromValue; + pPred->bInclFrom = bInclFrom; + } + } + else if (RC_BAD( rc = fqCompareValues( pUntilValue, + bInclUntil, FALSE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp < 0) + { + + // Until value is less than predicate's until value. If the + // until value is also less than predicate's from value, we + // have no intersection. + + if (RC_BAD( rc = fqCompareValues( pUntilValue, + bInclUntil, FALSE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp < 0) + { + *pbClipContext = TRUE; + } + else + { + pPred->pUntilValue = pUntilValue; + pPred->bInclUntil = bInclUntil; + } + } + goto Exit; + } + pPred = pPred->pNext; + } + } + } + + // Add a new predicate to the context path + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( PATH_PRED), + (void **)&pPred))) + { + goto Exit; + } + pPred->uiCompareRules = uiCompareRules; + pPred->pOpComparer = pOpComparer; + + // Link the predicate as the last predicate for the path + + if ((pPred->pPrev = pContextPath->pLastPred) != NULL) + { + pPred->pPrev->pNext = pPred; + } + else + { + pContextPath->pFirstPred = pPred; + } + pContextPath->pLastPred = pPred; + + // Set other items in the predicate. + + pPred->pContextNode = pContextNode; + pPred->bNotted = bNotted; + switch (eOperator) + { + case XFLM_EXISTS_OP: + case XFLM_NE_OP: + pPred->eOperator = eOperator; + pPred->pFromValue = pQValue; + break; + case XFLM_APPROX_EQ_OP: + pPred->eOperator = eOperator; + pPred->pFromValue = pQValue; + pPred->bInclFrom = TRUE; + pPred->bInclUntil = TRUE; + break; + case XFLM_EQ_OP: + if (bDoMatch) + { + pPred->eOperator = XFLM_MATCH_OP; + pPred->pFromValue = pQValue; + } + else + { + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = pQValue; + pPred->bInclFrom = TRUE; + pPred->bInclUntil = TRUE; + } + break; + case XFLM_LE_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = NULL; + pPred->pUntilValue = pQValue; + pPred->bInclUntil = TRUE; + break; + case XFLM_LT_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = NULL; + pPred->pUntilValue = pQValue; + pPred->bInclUntil = FALSE; + break; + case XFLM_GE_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = NULL; + pPred->bInclFrom = TRUE; + break; + case XFLM_GT_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = NULL; + pPred->bInclFrom = FALSE; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + if (RC_OK( rc) && !(*pbClipContext)) + { + PATH_PRED_NODE * pPathPredNode; + + if (RC_OK( rc = m_Pool.poolCalloc( sizeof( PATH_PRED_NODE), + (void **)&pPathPredNode))) + { + pPathPredNode->pXPathNode = pXPathNode; + pPathPredNode->pNext = pPred->pXPathNodeList; + pPred->pXPathNodeList = pPathPredNode; + } + } + + return( rc); +} + +/*************************************************************************** +Desc: Check to see if any predicates need to be unioned with the passed + in predicate. If so, perform the union. +***************************************************************************/ +FSTATIC RCODE fqCheckUnionPredicates( + CONTEXT_PATH * pContextPath, + FLMUINT uiLanguage, + PATH_PRED * pPred + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pMergePred; + FLMINT iCmp; + FLMBOOL bDidOverlap; + + pMergePred = pContextPath->pFirstPred; + + // This should only be done on predicates that have a range + // operator. + + flmAssert( pPred->eOperator == XFLM_RANGE_OP); + while (pMergePred) + { + bDidOverlap = FALSE; + if (pMergePred != pPred && + pMergePred->eOperator == XFLM_RANGE_OP && + pMergePred->uiCompareRules == pPred->uiCompareRules) + { + + // If the value type is not compatible with the predicate's + // value type, we cannot do the comparison, and there is + // no overlap. + + if (!fqCanCompare( pMergePred->pFromValue, pPred->pFromValue) || + !fqCanCompare( pMergePred->pFromValue, pPred->pUntilValue) || + !fqCanCompare( pMergePred->pUntilValue, pPred->pFromValue) || + !fqCanCompare( pMergePred->pUntilValue, pPred->pUntilValue)) + { + // Nothing to do here + } + else if (RC_BAD( rc = fqCompareValues( pMergePred->pFromValue, + pMergePred->bInclFrom, TRUE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + pPred->uiCompareRules, uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp >= 0) + { + + // From value is greater than or equal to the predicate's + // from value. + // If the from value is also less than or equal to the + // predicate's until value, we have an overlap. + + if (RC_BAD( rc = fqCompareValues( pMergePred->pFromValue, + pMergePred->bInclFrom, TRUE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + pPred->uiCompareRules, uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp <= 0) + { + + // If the until value is greater than the predicate's + // until value, change the predicate's until value. + + if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, + pMergePred->bInclUntil, FALSE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + pPred->uiCompareRules, uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + pPred->pUntilValue = pMergePred->pUntilValue; + pPred->bInclUntil = pMergePred->bInclUntil; + } + bDidOverlap = TRUE; + } + } + + // At this point we already know that the from value is + // less than the predicate's from value. + // See if the until value is greater than or equal + // to the from value. If it is we have an overlap. + + else if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, + pMergePred->bInclUntil, FALSE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + pPred->uiCompareRules, uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp >= 0) + { + + // Until value is greater than or equal to the predicate's + // from value, so we definitely have an overlap. We + // already know that the from value is less than the + // predicate's from value, so we will change that for sure. + + pPred->pFromValue = pMergePred->pFromValue; + pPred->bInclFrom = pMergePred->bInclFrom; + + // See if the until value is greater than the + // predicate's until value, in which case we need to + // change the predicate's until value. + + if (RC_BAD( rc = fqCompareValues( pMergePred->pUntilValue, + pMergePred->bInclUntil, FALSE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + pPred->uiCompareRules, uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + pPred->pUntilValue = pMergePred->pUntilValue; + pPred->bInclUntil = pMergePred->bInclUntil; + } + bDidOverlap = TRUE; + } + } + + // If the predicates overlapped, remove pMergePred from the list + // of predicates. But move its list of predicate nodes into the + // list off of pPred. + + if (bDidOverlap) + { + PATH_PRED_NODE * pPathPredNode; + + // Merge the predicate node lists, if any - into pPred's list. + + if ((pPathPredNode = pPred->pXPathNodeList) != NULL) + { + while (pPathPredNode->pNext) + { + pPathPredNode = pPathPredNode->pNext; + } + pPathPredNode->pNext = pMergePred->pXPathNodeList; + } + else + { + pPred->pXPathNodeList = pMergePred->pXPathNodeList; + } + + // Remove pMergePred from the list + + if (pMergePred->pPrev) + { + pMergePred->pPrev->pNext = pMergePred->pNext; + } + else + { + pContextPath->pFirstPred = pMergePred->pNext; + } + if (pMergePred->pNext) + { + pMergePred->pNext->pPrev = pMergePred->pPrev; + + // Set up so we are on the next node + + pMergePred = pMergePred->pNext; + } + else + { + pContextPath->pLastPred = pMergePred->pPrev; + } + } + else + { + pMergePred = pMergePred->pNext; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Union a predicate into a context path. +***************************************************************************/ +RCODE F_Query::unionPredicates( + CONTEXT_PATH * pContextPath, + FQNODE * pXPathNode, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pPred; + FLMINT iCmp; + FLMBOOL bDoMatch; + FLMBOOL bDidOverlap = FALSE; + + if (!pQValue || pQValue->eValType != XFLM_UTF8_VAL) + { + bDoMatch = FALSE; + + // Comparison rules don't matter for anything that is + // not text, so we normalize them to zero, so the test + // below to see if the comparison rule is the same as + // the comparison rule of the operator will work. + + uiCompareRules = 0; + } + else + { + bDoMatch = (eOperator == XFLM_EQ_OP && + (pQValue->uiFlags & VAL_IS_CONSTANT) && + (pQValue->uiFlags & VAL_HAS_WILDCARDS)) + ? TRUE + : FALSE; + } + + if ((pPred = pContextPath->pFirstPred) != NULL) + { + if (eOperator == XFLM_EXISTS_OP || eOperator == XFLM_NE_OP) + { + + // See if there is another operator that is an exact + // match of this one. + + while (pPred) + { + if (pPred->eOperator == eOperator) + { + if ((bNotted && pPred->bNotted) || + (!bNotted && !pPred->bNotted)) + { + + // Perfect match - no need to do any more. + + goto Exit; + } + } + pPred = pPred->pNext; + } + } + else if ((eOperator == XFLM_EQ_OP && !bDoMatch) || + eOperator == XFLM_LE_OP || + eOperator == XFLM_LT_OP || + eOperator == XFLM_GE_OP || + eOperator == XFLM_GT_OP) + { + + // See if the operator overlaps with another range operator + + while (pPred) + { + if (pPred->eOperator == XFLM_RANGE_OP && + pPred->uiCompareRules == uiCompareRules) + { + FQVALUE * pFromValue; + FQVALUE * pUntilValue; + FLMBOOL bInclFrom; + FLMBOOL bInclUntil; + + pFromValue = (eOperator == XFLM_EQ_OP || + eOperator == XFLM_GE_OP || + eOperator == XFLM_GT_OP) + ? pQValue + : NULL; + pUntilValue = (eOperator == XFLM_EQ_OP || + eOperator == XFLM_LE_OP || + eOperator == XFLM_LT_OP) + ? pQValue + : NULL; + bInclFrom = (FLMBOOL)(eOperator == XFLM_EQ_OP || + eOperator == XFLM_GE_OP + ? TRUE + : FALSE); + bInclUntil = (FLMBOOL)(eOperator == XFLM_EQ_OP || + eOperator == XFLM_LE_OP + ? TRUE + : FALSE); + + // If the value type is not compatible with the predicate's + // value type, we cannot do the comparison, and there is + // no overlap. + + if (!fqCanCompare( pQValue, pPred->pFromValue) || + !fqCanCompare( pQValue, pPred->pUntilValue)) + { + // Nothing to do here + } + else if (RC_BAD( rc = fqCompareValues( pFromValue, + bInclFrom, TRUE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp >= 0) + { + + // From value is greater than or equal to the predicate's + // from value. + // If the from value is also less than or equal to the + // predicate's until value, we have an overlap. + + if (RC_BAD( rc = fqCompareValues( pFromValue, + bInclFrom, TRUE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp <= 0) + { + + // If the until value is greater than the predicate's + // until value, change the predicate's until value. + + if (RC_BAD( rc = fqCompareValues( pUntilValue, + bInclUntil, FALSE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + pPred->pUntilValue = pUntilValue; + pPred->bInclUntil = bInclUntil; + } + bDidOverlap = TRUE; + goto Exit; + } + } + + // At this point we already know that the from value is + // less than the predicate's from value. + // See if the until value is greater than or equal + // to the from value. If it is we have an overlap. + + else if (RC_BAD( rc = fqCompareValues( pUntilValue, + bInclUntil, FALSE, + pPred->pFromValue, pPred->bInclFrom, TRUE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + else if (iCmp >= 0) + { + + // Until value is greater than or equal to the predicate's + // from value, so we definitely have an overlap. We + // already know that the from value is less than the + // predicate's from value, so we will change that for sure. + + pPred->pFromValue = pFromValue; + pPred->bInclFrom = bInclFrom; + + // See if the until value is greater than the + // predicate's until value, in which case we need to + // change the predicate's until value. + + if (RC_BAD( rc = fqCompareValues( pUntilValue, + bInclUntil, FALSE, + pPred->pUntilValue, pPred->bInclUntil, FALSE, + uiCompareRules, m_uiLanguage, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + pPred->pUntilValue = pUntilValue; + pPred->bInclUntil = bInclUntil; + } + bDidOverlap = TRUE; + goto Exit; + } + } + pPred = pPred->pNext; + } + } + } + + // Add a new predicate to the context path + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( PATH_PRED), + (void **)&pPred))) + { + goto Exit; + } + pPred->uiCompareRules = uiCompareRules; + pPred->pOpComparer = pOpComparer; + + // Link the predicate as the last predicate for the path + + if ((pPred->pPrev = pContextPath->pLastPred) != NULL) + { + pPred->pPrev->pNext = pPred; + } + else + { + pContextPath->pFirstPred = pPred; + } + pContextPath->pLastPred = pPred; + + // Set other items in the predicate. + + pPred->pContextNode = pContextNode; + pPred->bNotted = bNotted; + switch (eOperator) + { + case XFLM_EXISTS_OP: + case XFLM_NE_OP: + pPred->eOperator = eOperator; + pPred->pFromValue = pQValue; + break; + case XFLM_APPROX_EQ_OP: + pPred->eOperator = eOperator; + pPred->pFromValue = pQValue; + pPred->bInclFrom = TRUE; + pPred->bInclUntil = TRUE; + break; + case XFLM_EQ_OP: + if (bDoMatch) + { + pPred->eOperator = XFLM_MATCH_OP; + pPred->pFromValue = pQValue; + } + else + { + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = pQValue; + pPred->bInclFrom = TRUE; + pPred->bInclUntil = TRUE; + } + break; + case XFLM_LE_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = NULL; + pPred->pUntilValue = pQValue; + pPred->bInclUntil = TRUE; + break; + case XFLM_LT_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = NULL; + pPred->pUntilValue = pQValue; + pPred->bInclUntil = FALSE; + break; + case XFLM_GE_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = NULL; + pPred->bInclFrom = TRUE; + break; + case XFLM_GT_OP: + pPred->eOperator = XFLM_RANGE_OP; + pPred->pFromValue = pQValue; + pPred->pUntilValue = NULL; + pPred->bInclFrom = FALSE; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + if (RC_OK( rc)) + { + PATH_PRED_NODE * pPathPredNode; + + if (RC_OK( rc = m_Pool.poolCalloc( sizeof( PATH_PRED_NODE), + (void **)&pPathPredNode))) + { + pPathPredNode->pXPathNode = pXPathNode; + pPathPredNode->pNext = pPred->pXPathNodeList; + pPred->pXPathNodeList = pPathPredNode; + + if (bDidOverlap) + { + rc = fqCheckUnionPredicates( pContextPath, m_uiLanguage, pPred); + } + } + } + + return( rc); +} + +/*************************************************************************** +Desc: Prune a context out of the context tree. +***************************************************************************/ +FSTATIC void fqClipContext( + OP_CONTEXT * pContext + ) +{ + + // If this context had a parent, we can unlink it from + // its parent. + + if (pContext->pParent) + { + if (pContext->pPrevSib) + { + pContext->pPrevSib->pNextSib = pContext->pNextSib; + } + else + { + pContext->pParent->pFirstChild = pContext->pNextSib; + } + if (pContext->pNextSib) + { + pContext->pNextSib->pPrevSib = pContext->pPrevSib; + } + else + { + pContext->pParent->pLastChild = pContext->pPrevSib; + } + } +} + +/*************************************************************************** +Desc: Add a predicate to a context. +***************************************************************************/ +RCODE F_Query::addPredicateToContext( + OP_CONTEXT * pContext, + XPATH_COMPONENT * pXPathContext, + XPATH_COMPONENT * pXPathComp, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FQNODE * pContextNode, + FLMBOOL bNotted, + FQVALUE * pQValue, + FLMBOOL * pbClipContext, + FQNODE ** ppQNode + ) +{ + RCODE rc = NE_XFLM_OK; + CONTEXT_PATH * pContextPath = NULL; + + *pbClipContext = FALSE; + + // Better be the leaf component of the XPATH + + flmAssert( !pXPathComp->pNext); + + // Convert the constant value in a node id predicate to + // a 64 bit unsigned value. + + if (eOperator != XFLM_EXISTS_OP && pXPathComp->eXPathAxis == META_AXIS) + { + if (RC_BAD( rc = fqGetNodeIdValue( pQValue))) + { + goto Exit; + } + } + + // See if we can find a matching XPATH component. If not, + // create a new one. NOTE: We can only match if the axis + // is the SELF_AXIS, or the XPATH component is an attribute. + // If the XPATH component is an attribute, it is guaranteed + // there there will only be one instance of the attribute + // Note also that the matching is only done if we are in the + // context of another XPATH component. + + if (pXPathContext && !pXPathComp->pPrev && !pXPathComp->pNodeSource) + { + if (pXPathComp->eXPathAxis == META_AXIS) + { + pContextPath = pContext->pFirstPath; + while (pContextPath) + { + if (!pContextPath->pXPathComponent->pPrev && + pContextPath->pXPathComponent->uiDictNum == + pXPathComp->uiDictNum && + pContextPath->pXPathComponent->eXPathAxis == META_AXIS) + { + break; + } + pContextPath = pContextPath->pNext; + } + } + else if (pXPathComp->eNodeType == ELEMENT_NODE || + pXPathComp->eNodeType == DATA_NODE) + { + if (pXPathComp->eXPathAxis == SELF_AXIS) + { + pContextPath = pContext->pFirstPath; + while (pContextPath) + { + if (!pContextPath->pXPathComponent->pPrev && + pContextPath->pXPathComponent->eXPathAxis == SELF_AXIS && + pContextPath->pXPathComponent->uiDictNum == + pXPathComp->uiDictNum && + pContextPath->pXPathComponent->eNodeType == + pXPathComp->eNodeType) + { + break; + } + pContextPath = pContextPath->pNext; + } + } + } + else if (pXPathComp->eNodeType == ATTRIBUTE_NODE) + { + pContextPath = pContext->pFirstPath; + while (pContextPath) + { + if (!pContextPath->pXPathComponent->pPrev && + pContextPath->pXPathComponent->uiDictNum == + pXPathComp->uiDictNum && + pContextPath->pXPathComponent->eNodeType == ATTRIBUTE_NODE) + { + break; + } + pContextPath = pContextPath->pNext; + } + } + } + + // If we did not find one, allocate it. + + if (!pContextPath) + { + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CONTEXT_PATH), + (void **)&pContextPath))) + { + goto Exit; + } + + // Link the new context path into the context as its last path + + if ((pContextPath->pPrev = pContext->pLastPath) != NULL) + { + pContextPath->pPrev->pNext = pContextPath; + } + else + { + pContext->pFirstPath = pContextPath; + } + pContext->pLastPath = pContextPath; + pContextPath->pXPathComponent = pXPathComp; + } + + // See if this operator can be merged with another one. + + if (pContext->bIntersect) + { + if (RC_BAD( rc = intersectPredicates( pContextPath, *ppQNode, + eOperator, + uiCompareRules, pOpComparer, + pContextNode, bNotted, pQValue, pbClipContext))) + { + goto Exit; + } + + // If we get a false result, then we know that the + // intersection of predicates is creating a situation where + // it can never be true, so we will turn the root node of + // the context into a XFLM_BOOL_VAL node with a FALSE value. + // The branch of the query tree represented underneath this + // node will have been cut off. The caller must account for this. + + if (*pbClipContext) + { + FQNODE * pRootNode = pContext->pQRootNode; + + pRootNode->eNodeType = FLM_VALUE_NODE; + pRootNode->pFirstChild = NULL; + pRootNode->pLastChild = NULL; + pRootNode->pContext = pContext->pParent; + pRootNode->currVal.eValType = XFLM_BOOL_VAL; + pRootNode->currVal.uiFlags = VAL_IS_CONSTANT; + pRootNode->currVal.val.eBool = XFLM_FALSE; + *ppQNode = pRootNode; + + // Clip this context out of the context tree. + // Don't want to travel down this path when optimizing. + + fqClipContext( pContext); + } + } + else + { + if (RC_BAD( rc = unionPredicates( pContextPath, *ppQNode, + eOperator, + uiCompareRules, pOpComparer, + pContextNode, bNotted, pQValue))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Import child context from pSrcContext into pDestContext. +***************************************************************************/ +FSTATIC void fqImportChildContexts( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext) +{ + OP_CONTEXT * pTmpContext; + + if ((pTmpContext = pSrcContext->pFirstChild) != NULL) + { + + // Change all of the parent pointers of the child contexts to + // point to pDestContext; + + while (pTmpContext) + { + flmAssert( (!pTmpContext->bIntersect && pDestContext->bIntersect) || + (pTmpContext->bIntersect && !pDestContext->bIntersect)); + pTmpContext->pParent = pDestContext; + pTmpContext = pTmpContext->pNextSib; + } + + // Link all of pSrcContext's children as children of pDestContext. + + if ((pSrcContext->pFirstChild->pPrevSib = + pDestContext->pLastChild) != NULL) + { + pDestContext->pLastChild->pNextSib = pSrcContext->pFirstChild; + } + else + { + pDestContext->pFirstChild = pSrcContext->pFirstChild; + } + pDestContext->pLastChild = pSrcContext->pLastChild; + pSrcContext->pFirstChild = NULL; + pSrcContext->pLastChild = NULL; + } +} + +/*************************************************************************** +Desc: Import context paths from pSrcContext into pDestContext. +***************************************************************************/ +FSTATIC void fqImportContextPaths( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext) +{ + if (pSrcContext->pFirstPath) + { + if ((pSrcContext->pFirstPath->pPrev = + pDestContext->pLastPath) != NULL) + { + pDestContext->pLastPath->pNext = pSrcContext->pFirstPath; + } + else + { + pDestContext->pFirstPath = pSrcContext->pFirstPath; + } + pDestContext->pLastPath = pSrcContext->pLastPath; + pSrcContext->pFirstPath = NULL; + pSrcContext->pLastPath = NULL; + } +} + +/*************************************************************************** +Desc: Import pSrcContext into pDestContext. +***************************************************************************/ +FSTATIC void fqImportContext( + OP_CONTEXT * pDestContext, + OP_CONTEXT * pSrcContext) +{ + + // Merge all of the child contexts from pSrcContext into pDestContext + + fqImportChildContexts( pDestContext, pSrcContext); + + // Merge all of the paths from pSrcContext into pDestContext + + fqImportContextPaths( pDestContext, pSrcContext); +} + +/*************************************************************************** +Desc: Merge the context of pQNode, if any, into pDestContext +***************************************************************************/ +FSTATIC void fqMergeContexts( + FQNODE * pQNode, + OP_CONTEXT * pDestContext + ) +{ + OP_CONTEXT * pSrcContext = pQNode->pContext; + + // At this point, pQNode MUST have a context to merge in, and + // pDestContext MUST be an intersect context. + + flmAssert( pSrcContext && pDestContext->bIntersect); + + // The context we are merging should not have any parent context. + // It should be a root context. + + flmAssert( !pSrcContext->pNextSib && !pSrcContext->pPrevSib && + !pSrcContext->pParent); + + // Root node of context should be same as pQNode. + + flmAssert( pSrcContext->pQRootNode == pQNode); + + if (pSrcContext->bIntersect) + { + + // If the context to be merged is an intersect context (AND), + // we take its child OP_CONTEXTs (which should all be non-intersect) + // and make them children of the destination context. We also + // move its predicates into the predicate list of the destination + // context. + + fqImportContext( pDestContext, pSrcContext); + } + else + { + + // If the context to be merged is a non-intersect context (OR) + // we just put the whole thing in as a child of the destination + // context. + + if ((pSrcContext->pPrevSib = pDestContext->pLastChild) != NULL) + { + pSrcContext->pPrevSib->pNextSib = pSrcContext; + } + else + { + pDestContext->pFirstChild = pSrcContext; + } + pDestContext->pLastChild = pSrcContext; + pSrcContext->pParent = pDestContext; + } + pQNode->pContext = pDestContext; +} + +/*************************************************************************** +Desc: Create a new context for predicates +***************************************************************************/ +RCODE F_Query::createOpContext( + OP_CONTEXT * pParentContext, + FLMBOOL bIntersect, + FQNODE * pQRootNode + ) +{ + RCODE rc = NE_XFLM_OK; + OP_CONTEXT * pContext; + + // Allocate a new context and link it in as a child + // to the current context. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( OP_CONTEXT), + (void **)&pContext))) + { + goto Exit; + } + pQRootNode->pContext = pContext; + pContext->pQRootNode = pQRootNode; + pContext->bIntersect = bIntersect; + pContext->bMustScan = FALSE; + pContext->uiCost = (FLMUINT)(bIntersect + ? ~((FLMUINT)0) + : 0); + if ((pContext->pParent = pParentContext) != NULL) + { + if ((pContext->pPrevSib = + pParentContext->pLastChild) != NULL) + { + pParentContext->pLastChild->pNextSib = pContext; + } + else + { + pParentContext->pFirstChild = pContext; + } + pParentContext->pLastChild = pContext; + + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Check to see if an XPATH component matches the XPATH context + component we are inside of. +***************************************************************************/ +FSTATIC void fqCheckPathMatch( + XPATH_COMPONENT * pXPathContextComponent, + XPATH_COMPONENT * pXPathComponent + ) +{ + + // If this is a self:: axis, and we are in the context of another + // xpath component, see if we match the context xpath component + + if (pXPathContextComponent && + pXPathContextComponent->uiDictNum && + (pXPathContextComponent->eNodeType == ELEMENT_NODE || + pXPathContextComponent->eNodeType == DATA_NODE || + pXPathContextComponent->eNodeType == ATTRIBUTE_NODE || + pXPathContextComponent->eXPathAxis == META_AXIS) && + pXPathComponent->eXPathAxis == SELF_AXIS && + !pXPathComponent->pNext && + ((pXPathComponent->eNodeType == pXPathContextComponent->eNodeType && + !pXPathComponent->uiDictNum) || + pXPathComponent->eNodeType == ANY_NODE_TYPE)) + { + pXPathComponent->uiDictNum = pXPathContextComponent->uiDictNum; + pXPathComponent->eNodeType = pXPathContextComponent->eNodeType; + if( pXPathContextComponent->eXPathAxis == META_AXIS) + { + pXPathComponent->eXPathAxis = META_AXIS; + } + } +} + +/*************************************************************************** +Desc: Get predicates for the XPATH node (pQNode) that was passed in. +***************************************************************************/ +RCODE F_Query::getPathPredicates( + FQNODE * pParentNode, + FQNODE ** ppQNode, + XPATH_COMPONENT * pXPathContext + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode = *ppQNode; + FXPATH * pXPath = pQNode->nd.pXPath; + XPATH_COMPONENT * pXPathComp = pXPath->pFirstComponent; + OP_CONTEXT * pContext; + FQNODE * pContextNode; + FLMBOOL bHadComponentExpressions = FALSE; + FLMBOOL bClippedContext; + + // pXPathContext is the XPATH component context for this + // XPATH component. This may be used in optimization. + // It should have already been set up. + + flmAssert( pXPathComp->pXPathContext == pXPathContext); + + fqCheckPathMatch( pXPathContext, pXPathComp); + + // See if any of the XPATH components have expressions ([]). If they + // do, we want to AND this XPATH expression with them into + // an intersect context. If we are not currently in an intersect + // context, we need to create one. + + pContext = NULL; + pContextNode = pXPathContext ? pXPathContext->pXPathNode : NULL; + while (pXPathComp) + { + if (pXPathComp->pExpr && !pContext) + { + bHadComponentExpressions = TRUE; + + // If we have not yet determined the context for the + // XPATH components to be merged into, do it now. + + if (pParentNode) + { + pContext = pQNode->pContext = pParentNode->pContext; + + // If the context is not an intersect context, we need to + // create a new one that is, and link it as the last child + // of the current context. + + if (!pContext->bIntersect) + { + if (RC_BAD( rc = createOpContext( pParentNode->pContext, + TRUE, pQNode))) + { + goto Exit; + } + pContext = pQNode->pContext; + } + } + else + { + + // We set the bIntersect flag to TRUE for this context, + // even though it is not an AND operator. This is done + // because we know there will only ever be one predicate + // in this context, and therefore, if it is ever merged + // into another "intersect" context, we want the predicate + // merged in by itself instead of the entire context. + // See fqMergeContexts. + + if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) + { + goto Exit; + } + pContext = pQNode->pContext; + } + break; + } + pXPathComp = pXPathComp->pNext; + } + + // Create a context if one was not created above. + + if (!pContext) + { + if (pParentNode) + { + pContext = pQNode->pContext = pParentNode->pContext; + } + else + { + + // We set the bIntersect flag to TRUE for this context, + // even though it is not an AND operator. This is done + // because we know there will only ever be one predicate + // in this context, and therefore, if it is ever merged + // into another "intersect" context, we want the predicate + // merged in by itself instead of the entire context. + // See fqMergeContexts. + + if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) + { + goto Exit; + } + pContext = pQNode->pContext; + } + } + else + { + flmAssert( pQNode->pContext == pContext); + } + + // Create predicate, if possible + + // Find the terminating XPATH component. + + pXPathComp = pXPath->pLastComponent; + + // See if we can create a predicate from this component. + + if (pXPathComp->pNodeSource) + { + + // Create an exists predicate + + if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, + pXPathComp, + XFLM_EXISTS_OP, 0, NULL, + pContextNode, pQNode->bNotted, NULL, + &bClippedContext, &pQNode))) + { + goto Exit; + } + + // Context should never be clipped because it shouldn't ever + // merge with another predicate. + + flmAssert( !bClippedContext); + } + else if (pXPathComp->uiDictNum && + (pXPathComp->eXPathAxis == META_AXIS || + pXPathComp->eNodeType == ELEMENT_NODE || + pXPathComp->eNodeType == DATA_NODE || + pXPathComp->eNodeType == ATTRIBUTE_NODE)) + { + if (pParentNode) + { + if (isLogicalOp( pParentNode->nd.op.eOperator)) + { + + // Create an exists predicate + + if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, + pXPathComp, + XFLM_EXISTS_OP, 0, NULL, + pContextNode, pQNode->bNotted, NULL, + &bClippedContext, &pQNode))) + { + goto Exit; + } + + // If adding this predicate would cause a false result, + // the root node of the context will have been turned into + // a boolean value with a FALSE value. There is no need to + // process any more of this branch, because this branch will + // have been cut off. pQNode will have been altered. + + if (bClippedContext) + { + goto Exit; + } + } + else if (isCompareOp( pParentNode->nd.op.eOperator)) + { + + // Sibling must be a value node + + if (pQNode->pNextSib) + { + flmAssert( !pQNode->pPrevSib); + if (pQNode->pNextSib->eNodeType == FLM_VALUE_NODE) + { + + // Create a compare predicate. + + if (RC_BAD( rc = addPredicateToContext( pContext, + pXPathContext, + pXPathComp, + pParentNode->nd.op.eOperator, + pParentNode->nd.op.uiCompareRules, + pParentNode->nd.op.pOpComparer, + pContextNode, pQNode->bNotted, + &pQNode->pNextSib->currVal, + &bClippedContext, &pQNode))) + { + goto Exit; + } + + // If adding this predicate would cause a false + // result, the root node of the context will + // have been turned into a boolean value with a + // FALSE value. There is no need to process any + // more of this branch, because this branch will + // have been cut off. pQNode will have been altered. + + if (bClippedContext) + { + goto Exit; + } + } + else + { + + // We have a comparison operation that cannot be + // optimized. If this is a non-intersect context, + // there is no point in doing optimizations. + // If it is an intersect context, other predicates + // may come along that can be used to optimize. + // We won't know until we have processed all of + // them. + + if (!pContext->bIntersect) + { + pContext->bForceOptToScan = TRUE; + } + } + } + else + { + flmAssert( pQNode->pPrevSib); + if (pQNode->pPrevSib->eNodeType == FLM_VALUE_NODE) + { + FQNODE * pSaveSib; + + // Switch the two operators around + + switch (pParentNode->nd.op.eOperator) + { + case XFLM_EQ_OP: + case XFLM_NE_OP: + pSaveSib = pQNode->pPrevSib; + break; + case XFLM_LT_OP: + pSaveSib = pQNode->pPrevSib; + pParentNode->nd.op.eOperator = XFLM_GE_OP; + break; + case XFLM_LE_OP: + pSaveSib = pQNode->pPrevSib; + pParentNode->nd.op.eOperator = XFLM_GT_OP; + break; + case XFLM_GT_OP: + pSaveSib = pQNode->pPrevSib; + pParentNode->nd.op.eOperator = XFLM_LE_OP; + break; + case XFLM_GE_OP: + pSaveSib = pQNode->pPrevSib; + pParentNode->nd.op.eOperator = XFLM_LT_OP; + break; + default: + pSaveSib = NULL; + break; + } + + // If we can switch the operator, we can create a + // predicate for optimization. Otherwise, we must + // leave it alone - or assert? + + if (pSaveSib) + { + pQNode->pNextSib = pSaveSib; + pQNode->pPrevSib = NULL; + pSaveSib->pPrevSib = pQNode; + pSaveSib->pNextSib = NULL; + pParentNode->pFirstChild = pQNode; + pParentNode->pLastChild = pSaveSib; + + // Create a compare predicate, but need to switch + // the operators around + + if (RC_BAD( rc = addPredicateToContext( pContext, + pXPathContext, + pXPathComp, + pParentNode->nd.op.eOperator, + pParentNode->nd.op.uiCompareRules, + pParentNode->nd.op.pOpComparer, + pContextNode, pQNode->bNotted, + &pQNode->pNextSib->currVal, + &bClippedContext, &pQNode))) + { + goto Exit; + } + + // If adding this predicate would cause a false + // result, the root node of the context will + // have been turned into a boolean value with a + // FALSE value. There is no need to process any + // more of this branch, because this branch will + // have been cut off. pQNode will have been + // altered. + + if (bClippedContext) + { + goto Exit; + } + } + else + { + + // We have a comparison operation that cannot be + // optimized. + + if (!pContext->bIntersect) + { + pContext->bForceOptToScan = TRUE; + } + } + } + else + { + + // We have a comparison operation that cannot be + // optimized. + + if (!pContext->bIntersect) + { + pContext->bForceOptToScan = TRUE; + } + } + } + } + } + else + { + + // Create an exists predicate + + if (RC_BAD( rc = addPredicateToContext( pContext, pXPathContext, + pXPathComp, XFLM_EXISTS_OP, 0, NULL, + pContextNode, pQNode->bNotted, NULL, &bClippedContext, + &pQNode))) + { + goto Exit; + } + + // If adding this predicate would cause a false + // result, the root node of the context will + // have been turned into a boolean value with a + // FALSE value. There is no need to process any + // more of this branch, because this branch will + // have been cut off. pQNode will have been + // altered. + + if (bClippedContext) + { + goto Exit; + } + } + } + else + { + + // We have something in the expression that cannot be + // optimized. + + if (!pContext->bIntersect) + { + pContext->bForceOptToScan = TRUE; + } + } + + if (bHadComponentExpressions) + { + + // Now, merge in the expressions for each component of the + // XPATH. We wait to do this until AFTER the predicates + // have been added above, because this "merge" does not + // actually merge RANGE operators, etc., but just adds + // the predicates to the list - which is all we want to + // have happen at this point. + + pXPathComp = pXPath->pFirstComponent; + while (pXPathComp) + { + if (pXPathComp->pExpr) + { + + // Merge each expression's context into pContext, which + // should be an intersection context - should have + // been set up above. + + flmAssert( pContext && pContext->bIntersect); + + fqMergeContexts( pXPathComp->pExpr, pContext); + } + pXPathComp = pXPathComp->pNext; + } + } + +Exit: + + *ppQNode = pQNode; + + return( rc); +} + +/*************************************************************************** +Desc: Evaluate operands of an AND or OR operator to see if we can + replace one. + TRUE && P1 will be replaced with P1 + FALSE && P1 will be replaced with FALSE + TRUE || P1 will be replaced with TRUE + FALSE || P1 will be replaced with P1 +***************************************************************************/ +FSTATIC FQNODE * fqEvalLogicalOperands( + FQNODE * pQNode + ) +{ + FLMBOOL bLeftIsBool = FALSE; + FLMBOOL bRightIsBool = FALSE; + XFlmBoolType eLeftBoolVal = XFLM_UNKNOWN; + XFlmBoolType eRightBoolVal = XFLM_UNKNOWN; + FQNODE * pLeftNode = pQNode->pFirstChild; + FQNODE * pRightNode = pLeftNode->pNextSib; + FQNODE * pReplacementNode = NULL; + OP_CONTEXT * pContext; + OP_CONTEXT * pParentContext; + + if (isBoolNode( pLeftNode)) + { + bLeftIsBool = TRUE; + eLeftBoolVal = pLeftNode->currVal.val.eBool; + } + if (isBoolNode( pRightNode)) + { + bRightIsBool = TRUE; + eRightBoolVal = pRightNode->currVal.val.eBool; + } + + // If neither operand is a boolean value, there is no replacement + // that can be done, but we still need to go up one level. + + if (!bLeftIsBool && !bRightIsBool) + { + goto Exit; + } + + // Handle the case where both operands are boolean values. + + if (bLeftIsBool && bRightIsBool) + { + XFlmBoolType eNewBoolVal; + + if (pQNode->nd.op.eOperator == XFLM_AND_OP) + { + if (eLeftBoolVal == XFLM_FALSE || + eRightBoolVal == XFLM_FALSE) + { + eNewBoolVal = XFLM_FALSE; + } + else if (eLeftBoolVal == XFLM_TRUE && + eRightBoolVal == XFLM_TRUE) + { + eNewBoolVal = XFLM_TRUE; + } + else + { + eNewBoolVal = XFLM_UNKNOWN; + } + } + else // XFLM_OR_OP + { + if (eLeftBoolVal == XFLM_TRUE || + eRightBoolVal == XFLM_TRUE) + { + eNewBoolVal = XFLM_TRUE; + } + else if (eLeftBoolVal == XFLM_FALSE && + eRightBoolVal == XFLM_FALSE) + { + eNewBoolVal = XFLM_FALSE; + } + else + { + eNewBoolVal = XFLM_UNKNOWN; + } + } + + // Doesn't really matter which one we use to + // replace the AND or OR node - we will use + // the left one. + + pLeftNode->currVal.val.eBool = eNewBoolVal; + pReplacementNode = pLeftNode; + } + else if (pQNode->nd.op.eOperator == XFLM_OR_OP) + { + + // Operator is an OR, and only one of the operands + // is a boolean value. + + if (bLeftIsBool) + { + pReplacementNode = (eLeftBoolVal == XFLM_TRUE) + ? pLeftNode + : pRightNode; + } + else + { + pReplacementNode = (eRightBoolVal == XFLM_TRUE) + ? pRightNode + : pLeftNode; + } + } + else + { + + // Operator is an AND, and only one of the operands is + // a boolean value. + + if (bLeftIsBool) + { + pReplacementNode = (eLeftBoolVal != XFLM_TRUE) + ? pLeftNode + : pRightNode; + } + else + { + pReplacementNode = (eRightBoolVal != XFLM_TRUE) + ? pRightNode + : pLeftNode; + } + } + + // If the node we are going to replace (pQNode) is the root + // of the context, change the context to point to the replacement + // node as the new root of the context. Also, if the replacement + // node is a boolean, or a non-logical operator, set the context + // to be an intersect context, so that if it is merged into another + // context, it will merge correctly. + + pContext = pQNode->pContext; + if (pContext->pQRootNode == pQNode) + { + pParentContext = pContext->pParent; + if (pReplacementNode->eNodeType == FLM_VALUE_NODE) + { + + // If this context node is a child to another + // context, we can excise it completely + + // At this point, if pQNode was an OR, the + // replacement node is guaranteed to be TRUE. + // If the replacement node was an AND, the + // replacement node is guaranteed to be FALSE. + // We are at the top of a context, so the entire + // context can be removed. + + // Strip off any child contexts and predicates of this context - + // Don't want to travel down this path when optimizing. + + pReplacementNode->pContext = pParentContext; + fqClipContext( pContext); + } + else if (pReplacementNode->eNodeType == FLM_FUNCTION_NODE) + { + + // Better not be anything in the context that needs to + // be imported into a parent context + + flmAssert( !pContext->pFirstPath); + flmAssert( !pContext->pFirstChild); + if (pParentContext) + { + pReplacementNode->pContext = pParentContext; + fqClipContext( pContext); + } + else + { + pReplacementNode->pContext = pContext; + pContext->bIntersect = TRUE; + pContext->pQRootNode = pReplacementNode; + } + } + else if (pReplacementNode->eNodeType != FLM_OPERATOR_NODE || + !isLogicalOp( pReplacementNode->nd.op.eOperator)) + { + if (pParentContext) + { + // Context has a parent context. + // If there is only one path and no child contexts, + // import that path into the parent context. + // Otherwise, change the root node of the context + // to point to the replacement node. + + if( pContext->pFirstPath == pContext->pLastPath && + !pContext->pFirstChild) + { + fqImportContextPaths( pParentContext, pContext); + pReplacementNode->pContext = pParentContext; + fqClipContext( pContext); + } + else + { + pContext->pQRootNode = pReplacementNode; + } + } + else + { + + // No parent context, so set the bIntersect flag to + // TRUE so that if it ever is merged to a higher + // context, it will merge properly - because we + // know that there is only one predicate in this + // context. + + pContext->bIntersect = TRUE; + pReplacementNode->pContext = pContext; + pContext->pQRootNode = pReplacementNode; + } + } + else + { + + // pReplacement node is either an AND or OR operator. + + // At this point, we know that we are moving an AND or OR up the tree + // to replace an AND or an OR. If they are the same operator, they + // would have already been in the same context. If not, they would have + // been in different contexts, and the replacement node will be the + // same operator as the parent of the node being replaced. Hence, + // the replacement node's context needs to be merged with the parent + // node's context. + + if (pReplacementNode->pContext != pContext) + { + fqClipContext( pContext); + + // Replace node's operator is different than the node it is + // replacing. AND --> OR, or OR --> AND. This means that + // the parent context should be the same as the replacement + // node's context, and hence, they should be merged. + + if (pParentContext) + { + flmAssert( (pParentContext->bIntersect && + pReplacementNode->pContext->bIntersect) || + (!pParentContext->bIntersect && + !pReplacementNode->pContext->bIntersect)); + fqImportContext( pParentContext, pReplacementNode->pContext); + fqClipContext( pReplacementNode->pContext); + pReplacementNode->pContext = pParentContext; + } + } + } + } + fqReplaceNode( pQNode, pReplacementNode); + pQNode = pReplacementNode; + +Exit: + + return( pQNode); +} + +/*************************************************************************** +Desc: Clip a NOT node out of the tree. +***************************************************************************/ +FSTATIC FQNODE * fqClipNotNode( + FQNODE * pQNode, + FQNODE ** ppExpr + ) +{ + FQNODE * pKeepNode; + + // If this NOT node has no parent, the root + // of the tree needs to be set to its child. + + pKeepNode = pQNode->pFirstChild; + + // Child better not have any siblings - NOT nodes only have + // one operand. + + flmAssert( !pKeepNode->pNextSib && !pKeepNode->pPrevSib); + + // Set child to point to the NOT node's parent. + + if ((pKeepNode->pParent = pQNode->pParent) == NULL) + { + *ppExpr = pKeepNode; + } + else + { + + // Link child in where the NOT node used to be. + + if ((pKeepNode->pPrevSib = pQNode->pPrevSib) != NULL) + { + pKeepNode->pPrevSib->pNextSib = pKeepNode; + } + else + { + pKeepNode->pParent->pFirstChild = pKeepNode; + } + if ((pKeepNode->pNextSib = pQNode->pNextSib) != NULL) + { + pKeepNode->pNextSib->pPrevSib = pKeepNode; + } + else + { + pKeepNode->pParent->pLastChild = pKeepNode; + } + } + return( pKeepNode); +} + +/*************************************************************************** +Desc: Get predicates for a query expression. Also strips out NOT nodes. +***************************************************************************/ +RCODE F_Query::getPredicates( + FQNODE ** ppExpr, + FQNODE * pStartNode, + XPATH_COMPONENT * pXPathContextComponent + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode = pStartNode ? pStartNode : *ppExpr; + FQNODE * pParentNode = NULL; + eNodeTypes eNodeType; + eQueryOperators eOperator; + FLMBOOL bNotted = FALSE; + FLMBOOL bGetPredicates; + + // Don't get predicates if this is a constant or arithmetic expression. + // But if it is an arithmetic expression, still need to reduce it to + // a single constant if possible. + + if (pQNode->eNodeType == FLM_VALUE_NODE || + pQNode->eNodeType == FLM_OPERATOR_NODE && + isArithOp( pQNode->nd.op.eOperator)) + { + bGetPredicates = FALSE; + } + else + { + bGetPredicates = TRUE; + } + + for (;;) + { + eNodeType = pQNode->eNodeType; + + // Need to save bNotted on each node so that when we traverse + // back up the tree it can be reset properly. If bNotted is + // TRUE and pQNode is an operator, we may change the operator in + // some cases. Even if we change the operator, we still want to + // set the bNotted flag because it also implies "for every" when set + // to TRUE, and we need to remember that as well. + + pQNode->bNotted = bNotted; + if (eNodeType == FLM_OPERATOR_NODE) + { + eOperator = pQNode->nd.op.eOperator; + if (eOperator == XFLM_AND_OP || eOperator == XFLM_OR_OP) + { + + // Logical sub-expressions can only be operands of + // AND, OR, or NOT operators. + + if (pParentNode) + { + if (!isLogicalOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + } + if (bNotted) + { + eOperator = (eOperator == XFLM_AND_OP + ? XFLM_OR_OP + : XFLM_AND_OP); + pQNode->nd.op.eOperator = eOperator; + } + if (pParentNode) + { + flmAssert( pParentNode->pContext); + if (pParentNode->nd.op.eOperator == eOperator) + { + pQNode->pContext = pParentNode->pContext; + } + else + { + if (RC_BAD( rc = createOpContext( pParentNode->pContext, + (FLMBOOL)(eOperator == XFLM_AND_OP + ? TRUE + : FALSE), pQNode))) + { + goto Exit; + } + } + } + else + { + if (RC_BAD( rc = createOpContext( NULL, + (FLMBOOL)(eOperator == XFLM_AND_OP + ? TRUE + : FALSE), pQNode))) + { + goto Exit; + } + } + } + else if (eOperator == XFLM_NOT_OP) + { + + // Logical sub-expressions can only be operands of + // AND, OR, or NOT operators. + + if (pParentNode) + { + if (!isLogicalOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + } + bNotted = !bNotted; + + // Clip NOT nodes out of the tree. + + pQNode = fqClipNotNode( pQNode, ppExpr); + pParentNode = pQNode->pParent; + continue; + } + else if (isCompareOp( eOperator)) + { + + // Comparison sub-expressions can only be operands of + // AND, OR, or NOT operators. + + if (pParentNode) + { + if (!isLogicalOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + + // Associate context with parent node + + pQNode->pContext = pParentNode->pContext; + } + else + { + + // We set the bIntersect flag to TRUE for this context, + // even though it is not an AND operator. This is done + // because we know there will only ever be one predicate + // in this context, and therefore, if it is ever merged + // into another "intersect" context, we want the predicate + // merged in by itself instead of the entire context. + // See fqMergeContexts. + + if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) + { + goto Exit; + } + } + if (bNotted) + { + switch (eOperator) + { + case XFLM_EQ_OP: + eOperator = XFLM_NE_OP; + break; + case XFLM_NE_OP: + eOperator = XFLM_EQ_OP; + break; + case XFLM_LT_OP: + eOperator = XFLM_GE_OP; + break; + case XFLM_LE_OP: + eOperator = XFLM_GT_OP; + break; + case XFLM_GT_OP: + eOperator = XFLM_LE_OP; + break; + case XFLM_GE_OP: + eOperator = XFLM_LT_OP; + break; + default: + + // Don't change the other operators. + // Will just use the bNotted flag when + // evaluating. + + break; + } + pQNode->nd.op.eOperator = eOperator; + } + } + else + { + + // Better be an arithmetic operator we are dealing with + // at this point. + + flmAssert( isArithOp( eOperator)); + + // Arithmetic sub-expressions can only be operands + // of arithmetic or comparison operators + + if (pParentNode) + { + if (!isCompareOp( pParentNode->nd.op.eOperator) && + !isArithOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + } + } + } + else if (eNodeType == FLM_XPATH_NODE) + { + if (bGetPredicates) + { + if (RC_BAD( rc = getPathPredicates( pParentNode, + &pQNode, pXPathContextComponent))) + { + goto Exit; + } + } + + // NOTE: Upon return pQNode may no longer be + // an XPATH node. This branch of the tree may + // have been clipped as we evaluated predicates + // that were ANDed together, in which case + // pQNode will be pointing at a value node + // that has a value of FALSE. Either way, + // there should be no children at this point. + + flmAssert( !pQNode->pFirstChild); + } + else if (eNodeType == FLM_FUNCTION_NODE) + { + + // a function node should not have any children + + flmAssert( !pQNode->pFirstChild); + + // If this function is ORed into the context, set the + // bForceOptToScan flag on the context. This will + // force the context to scan when we optimize. Note that + // we cannot just set the bMustScan flag, as that flag is + // initialized by the optimization code to FALSE in the case + // of a non-intersect context. + + if (bGetPredicates) + { + if (pParentNode) + { + if (pParentNode->nd.op.eOperator == XFLM_OR_OP) + { + pParentNode->pContext->bForceOptToScan = TRUE; + } + } + else + { + + // Need to have a context for later merging. + + if (RC_BAD( rc = createOpContext( NULL, TRUE, pQNode))) + { + goto Exit; + } + pQNode->pContext->bForceOptToScan = TRUE; + } + } + } + else + { + flmAssert( eNodeType == FLM_VALUE_NODE); + + // If bNotted is TRUE and we have a boolean value, change + // the value: FALSE ==> TRUE, TRUE ==> FALSE. + + if (bNotted && pQNode->currVal.eValType == XFLM_BOOL_VAL) + { + if (pQNode->currVal.val.eBool == XFLM_TRUE) + { + pQNode->currVal.val.eBool = XFLM_FALSE; + } + else if (pQNode->currVal.val.eBool == XFLM_FALSE) + { + pQNode->currVal.val.eBool = XFLM_TRUE; + } + } + + // Values can only be operands of arithmetic or comparison operators, + // unless they are boolean values, in which case they can only be + // operands of logical operators. + + if (pParentNode) + { + if (pQNode->currVal.eValType == XFLM_BOOL_VAL) + { + if (!isLogicalOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + } + else + { + if (!isCompareOp( pParentNode->nd.op.eOperator) && + !isArithOp( pParentNode->nd.op.eOperator)) + { + rc = RC_SET( NE_XFLM_Q_ILLEGAL_OPERAND); + goto Exit; + } + } + } + + // A value node should not have any children + + flmAssert( !pQNode->pFirstChild); + } + + // Do traversal to child node, if any + + if (pQNode->pFirstChild) + { + pParentNode = pQNode; + pQNode = pQNode->pFirstChild; + continue; + } + + // Go back up the tree until we hit something that has + // a sibling. + + while (!pQNode->pNextSib) + { + + // If there are no more parents, we are done. + + if ((pQNode = pQNode->pParent) == NULL) + { + goto Exit; + } + + flmAssert( pQNode->eNodeType == FLM_OPERATOR_NODE); + + // Evaluate arithmetic expressions if both operands are + // constants. + + if (isArithOp( pQNode->nd.op.eOperator) && + pQNode->pFirstChild->eNodeType == FLM_VALUE_NODE && + pQNode->pLastChild->eNodeType == FLM_VALUE_NODE) + { + if (RC_BAD( rc = fqArithmeticOperator( + &pQNode->pFirstChild->currVal, + &pQNode->pLastChild->currVal, + pQNode->nd.op.eOperator, + &pQNode->currVal))) + { + goto Exit; + } + pQNode->eNodeType = FLM_VALUE_NODE; + pQNode->currVal.uiFlags = VAL_IS_CONSTANT; + pQNode->pFirstChild = NULL; + pQNode->pLastChild = NULL; + } + else + { + + // For the AND and OR operators, check the operands to + // see if they are boolean values. Boolean values can + // be weeded out of the criteria as we go back up the + // tree. + + if (pQNode->nd.op.eOperator == XFLM_OR_OP || + pQNode->nd.op.eOperator == XFLM_AND_OP) + { + pQNode = fqEvalLogicalOperands( pQNode); + if (!pQNode->pParent) + { + *ppExpr = pQNode; + } + } + } + + pParentNode = pQNode->pParent; + } + + // pQNode will NEVER be NULL if we get here, because we + // will jump to Exit in those cases. + + pQNode = pQNode->pNextSib; + + // Need to reset the bNotted flag to what it would have + // been as we traverse back up the tree. + + bNotted = pParentNode->bNotted; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Determine if an XPATH component is getting the node's node id or + document id. +***************************************************************************/ +FINLINE FLMBOOL isNodeOrDocIdComponent( + XPATH_COMPONENT * pXPathComponent + ) +{ + return( pXPathComponent->eXPathAxis == META_AXIS && + (pXPathComponent->uiDictNum == XFLM_META_NODE_ID || + pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID) + ? TRUE + : FALSE); +} + +/*************************************************************************** +Desc: Get the meta data type for an xpath component, if any. +***************************************************************************/ +FINLINE FLMUINT getMetaDataType( + XPATH_COMPONENT * pXPathComponent + ) +{ + return( pXPathComponent->eXPathAxis != META_AXIS + ? 0 + : pXPathComponent->uiDictNum); +} + +/*************************************************************************** +Desc: Get the node ID constant from an FQVALUE node. +***************************************************************************/ +FSTATIC RCODE fqGetNodeIdValue( + FQVALUE * pQValue + ) +{ + RCODE rc = NE_XFLM_OK; + + switch (pQValue->eValType) + { + case XFLM_UINT_VAL: + pQValue->eValType = XFLM_UINT64_VAL; + pQValue->val.ui64Val = (FLMUINT64)pQValue->val.uiVal; + break; + case XFLM_MISSING_VAL: + case XFLM_UINT64_VAL: + break; + case XFLM_INT_VAL: + pQValue->eValType = XFLM_UINT64_VAL; + pQValue->val.ui64Val = (FLMUINT64)((FLMINT64)(pQValue->val.iVal)); + break; + case XFLM_INT64_VAL: + pQValue->eValType = XFLM_UINT64_VAL; + pQValue->val.ui64Val = (FLMUINT64)(pQValue->val.i64Val); + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_Q_INVALID_NODE_ID_VALUE); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Optimize a predicate. +***************************************************************************/ +RCODE F_Query::optimizePredicate( + XPATH_COMPONENT * pXPathComponent, + PATH_PRED * pPred + ) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd; + ICD * pTmpIcd; + XPATH_COMPONENT * pTmpXPathComponent; + FLMBOOL bCanCompareOnKey; + FLMBOOL bDoNodeMatch; + FLMBOOL bTmpCanCompareOnKey; + FLMBOOL bMustVerifyPath; + FSIndexCursor * pFSIndexCursor = NULL; + FLMUINT uiLeafBlocksBetween; + FLMUINT uiTotalRefs; + FLMBOOL bTotalsEstimated; + FLMUINT uiCost; + F_AttrElmInfo defInfo; + + // Special handling for app. defined source nodes + + if (pXPathComponent->pNodeSource) + { + FLMBOOL bMustScan; + + if (RC_OK( rc = pXPathComponent->pNodeSource->searchCost( + (IF_Db *)m_pDb, pPred->bNotted, + &pPred->OptInfo.uiCost, + &bMustScan))) + { + if (bMustScan) + { + pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + } + pPred->pNodeSource = pXPathComponent->pNodeSource; + pPred->pNodeSource->AddRef(); + } + goto Exit; + } + + // A predicate of the form "A operator Value" will always + // return FALSE if A is missing, regardless of the operator. + // So will a predicate of "exists(A)" + // This is because we do not use default data for missing + // values. The only time it will return TRUE + // when A is missing is if the predicate is notted. + // Hence, a predicate that is notted cannot be optimized. + + if (pPred->bNotted || (pPred->pContextNode && pPred->pContextNode->bNotted)) + { + pPred->OptInfo.uiCost = ~((FLMUINT)0); + pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + goto Exit; + } + + // Special handling for node id and document id attributes. + // These will never be indexed, but we use a collection + // cursor for them, if necessary. + + if (pXPathComponent->eXPathAxis == META_AXIS) + { + + // Default is to do a full collection scan - may be changed below. + + pPred->OptInfo.uiCost = ~((FLMUINT)0); + pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + + if (pXPathComponent->uiDictNum == XFLM_META_NODE_ID || + pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID) + { + FLMBOOL bDocumentIds = + pXPathComponent->uiDictNum == XFLM_META_DOCUMENT_ID + ? TRUE + : FALSE; + + // If the user has specified an index, we must set it up to + // do an index scan. + + if (m_bIndexSet) + { + pPred->OptInfo.uiCost = ~((FLMUINT)0); + pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + } + else if (pPred->eOperator == XFLM_APPROX_EQ_OP) + { + pPred->OptInfo.uiCost = 1; + + // Value should have already been converted to a 64 bit + // unsigned value. + + flmAssert( pPred->pFromValue->eValType == XFLM_UINT64_VAL); + pPred->OptInfo.ui64NodeId = pPred->pFromValue->val.ui64Val; + pPred->OptInfo.eOptType = XFLM_QOPT_SINGLE_NODE_ID; + pPred->OptInfo.bMustVerifyPath = TRUE; + } + else if (pPred->eOperator == XFLM_RANGE_OP) + { + + // Value should have already been converted to a 64 bit + // unsigned value. + + flmAssert( pPred->pFromValue->eValType == XFLM_UINT64_VAL); + pPred->OptInfo.ui64NodeId = pPred->pFromValue->val.ui64Val; + if (!pPred->bInclFrom) + { + pPred->OptInfo.ui64NodeId++; + } + + // Value should have already been converted to a 64 bit + // unsigned value. + + flmAssert( pPred->pUntilValue->eValType == XFLM_UINT64_VAL); + pPred->OptInfo.ui64EndNodeId = pPred->pUntilValue->val.ui64Val; + if (!pPred->bInclUntil) + { + pPred->OptInfo.ui64EndNodeId--; + } + if (pPred->OptInfo.ui64NodeId == pPred->OptInfo.ui64EndNodeId) + { + pPred->OptInfo.uiCost = 1; + pPred->OptInfo.eOptType = XFLM_QOPT_SINGLE_NODE_ID; + } + else + { + pPred->OptInfo.eOptType = XFLM_QOPT_NODE_ID_RANGE; + if ((pPred->pFSCollectionCursor = f_new FSCollectionCursor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = pPred->pFSCollectionCursor->setupRange( m_pDb, + m_uiCollection, bDocumentIds, + pPred->OptInfo.ui64NodeId, + pPred->OptInfo.ui64EndNodeId, + &uiLeafBlocksBetween, &uiTotalRefs, + &bTotalsEstimated))) + { + pPred->pFSCollectionCursor->Release(); + pPred->pFSCollectionCursor = NULL; + goto Exit; + } + pPred->OptInfo.uiCost = uiLeafBlocksBetween; + if (!pPred->OptInfo.uiCost) + { + pPred->OptInfo.uiCost = 1; + } + } + pPred->OptInfo.bMustVerifyPath = TRUE; + } + else + { + + // Only other operators allowed would be the NE + // operator. EXISTS would have been converted to + // a TRUE constant. + + flmAssert( pPred->eOperator == XFLM_NE_OP); + } + } + goto Exit; + } + + // Get the ICD chain + + if (pXPathComponent->eNodeType == ELEMENT_NODE) + { + if (RC_BAD( rc = m_pDb->m_pDict->getElement( m_pDb, + pXPathComponent->uiDictNum, + &defInfo))) + { + goto Exit; + } + } + else if (pXPathComponent->eNodeType == ATTRIBUTE_NODE) + { + if (RC_BAD( rc = m_pDb->m_pDict->getAttribute( m_pDb, + pXPathComponent->uiDictNum, &defInfo))) + { + goto Exit; + } + } + + pIcd = defInfo.m_pFirstIcd; + + // Get the indexes in the ICD chain that are suitable for this + // predicate. + + for (; pIcd; pIcd = pIcd->pNextInChain) + { + + // Stop at the first non-required ICD. + + if (!(pIcd->uiFlags & + (ICD_REQUIRED_PIECE | ICD_REQUIRED_IN_SET))) + { + break; + } + + // If this ICD is not a key component, we cannot use it. + // Also, it must be the FIRST key component, and none of the + // other components can be required. + + if (pIcd->uiKeyComponent != 1) + { + continue; + } + pTmpIcd = pIcd->pNextKeyComponent; + while (pTmpIcd && + !(pTmpIcd->uiFlags & ICD_REQUIRED_PIECE)) + { + pTmpIcd = pTmpIcd->pNextKeyComponent; + } + if (pTmpIcd) + { + continue; + } + + // Check the following conditions of suitability: + // 1) Index must be on the collection we are searching. + // 2) Index must be on-line + + if (pIcd->pIxd->uiCollectionNum != m_uiCollection || + (pIcd->pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED))) + { + continue; + } + + // If the user has specified an index, and this is not + // that index, we will ignore it. + + if (m_bIndexSet && pIcd->uiIndexNum != m_uiIndex) + { + continue; + } + + // Make sure the ICD's path is of less or equal specificity + // to the path for this predicate. + + pTmpIcd = pIcd->pParent; + pTmpXPathComponent = pXPathComponent; + + bMustVerifyPath = FALSE; + + while (pTmpIcd) + { + + // If this is the self axis, we stay on the ICD we are currently on. + // The XPATH component previous to/above this one is the same as + // this one. + + if (pTmpXPathComponent->eXPathAxis == SELF_AXIS) + { + FLMUINT uiTmpDictNum = pTmpXPathComponent->uiDictNum; + eDomNodeType eTmpNodeType = pTmpXPathComponent->eNodeType; + + if (pTmpXPathComponent->pPrev) + { + pTmpXPathComponent = pTmpXPathComponent->pPrev; + if (pTmpXPathComponent->pExpr) + { + bMustVerifyPath = TRUE; + } + } + else if ((pTmpXPathComponent = + pTmpXPathComponent->pXPathContext) == NULL) + { + break; + } + if (eTmpNodeType == ANY_NODE_TYPE || + (eTmpNodeType == pTmpXPathComponent->eNodeType && + (uiTmpDictNum == pTmpXPathComponent->uiDictNum || + !uiTmpDictNum))) + { + continue; + } + else + { + break; + } + } + + // If the ICD is the root tag, then the path needs to be + // the root axis in order for it to be a match. + + if (pTmpIcd->uiDictNum == ELM_ROOT_TAG) + { + + // Root tag should not have a parent. + + flmAssert( pTmpIcd->pParent == NULL); + + // If the path is not off of the root, then the + // index's path is too specific to have indexed + + if (pTmpXPathComponent->eXPathAxis == ROOT_AXIS) + { + + // Should not be anything previous to a root axis. + + flmAssert( pTmpXPathComponent->pPrev == NULL); + pTmpXPathComponent = NULL; + pTmpIcd = NULL; + } + break; + } + + // Cannot match on anything except parent/child relationships + + if (pTmpXPathComponent->eXPathAxis != CHILD_AXIS && + pTmpXPathComponent->eXPathAxis != ATTRIBUTE_AXIS) + { + bMustVerifyPath = TRUE; + break; + } + if (pTmpXPathComponent->pPrev) + { + pTmpXPathComponent = pTmpXPathComponent->pPrev; + if (pTmpXPathComponent->pExpr) + { + bMustVerifyPath = TRUE; + } + } + else if ((pTmpXPathComponent = + pTmpXPathComponent->pXPathContext) == NULL) + { + break; + } + + // See if the element is the same as the ICD. + // NOTE: At this point, the ICD MUST be an element, because attributes + // are not allowed as parent ICDs of another ICD. + + if (pTmpXPathComponent->eNodeType != ELEMENT_NODE || + pTmpXPathComponent->uiDictNum != pTmpIcd->uiDictNum) + { + break; + } + + // Go to ICD's parent + + pTmpIcd = pTmpIcd->pParent; + } + + // If we get here and we did not get all the way up to the + // parent ICD, the index path is more specific than the XPATH. + + if (pTmpIcd) + { + continue; + } + + // If there are more components in the XPATH, it is more specific + // than the ICD path, so we must verify the path. + + if (!bMustVerifyPath && pTmpXPathComponent && + (pTmpXPathComponent->eXPathAxis == ROOT_AXIS || + pTmpXPathComponent->pPrev || + pTmpXPathComponent->pXPathContext)) + { + bMustVerifyPath = TRUE; + } + + bCanCompareOnKey = TRUE; + + // If the ICD is on element or attributes and the operator is not + // the exists operator, we must also fetch the node to evaluate + // the predicate. + + if ((pIcd->uiFlags & ICD_PRESENCE) && pPred->eOperator != XFLM_EXISTS_OP) + { + bCanCompareOnKey = FALSE; + } + + // If the comparison rules aren't the same as those specified + // on the ICD, we will need to read the node to do the comparison. + + if (bCanCompareOnKey && defInfo.m_uiDataType == XFLM_TEXT_TYPE) + { + if (!(pPred->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE)) + { + if (pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) + { + bCanCompareOnKey = FALSE; + } + } + + // Check comparison flags that must match exactly + + if ((pPred->uiCompareRules & + (XFLM_COMP_COMPRESS_WHITESPACE | + XFLM_COMP_NO_WHITESPACE | + XFLM_COMP_NO_UNDERSCORES | + XFLM_COMP_NO_DASHES | + XFLM_COMP_WHITESPACE_AS_SPACE | + XFLM_COMP_IGNORE_LEADING_SPACE | + XFLM_COMP_IGNORE_TRAILING_SPACE)) != + (pIcd->uiCompareRules & + (XFLM_COMP_COMPRESS_WHITESPACE | + XFLM_COMP_NO_WHITESPACE | + XFLM_COMP_NO_UNDERSCORES | + XFLM_COMP_NO_DASHES | + XFLM_COMP_WHITESPACE_AS_SPACE | + XFLM_COMP_IGNORE_LEADING_SPACE | + XFLM_COMP_IGNORE_TRAILING_SPACE))) + { + bCanCompareOnKey = FALSE; + } + } + + // Need to select best metaphone for approximate equals + // on text value. + + if (pPred->eOperator == XFLM_APPROX_EQ_OP && + pIcd->uiFlags & ICD_METAPHONE && + pPred->pFromValue->eValType == XFLM_UTF8_VAL) + { + F_BufferIStream bufferIStream; + FLMUINT uiMeta; + FLMUINT uiAltMeta; + FQVALUE metaValue; + FQVALUE * pSaveValue = pPred->pFromValue; + FLMUINT uiMetaCost; + + uiCost = 0; + if (RC_BAD( rc = bufferIStream.open( + pPred->pFromValue->val.pucBuf, + pPred->pFromValue->uiDataLen))) + { + goto Exit; + } + + // This is a little bit trickiness here. We need to set up the + // metaphone value as a XFLM_UTF8_VAL, but then set + // metaValue.val.uiVal. That is because the code that + // generates the key is expecting this - see kybldkey.cpp, + // flmAddNonTextKeyPiece. + + metaValue.eValType = XFLM_UTF8_VAL; + for (;;) + { + if( RC_BAD( rc = flmGetNextMetaphone( + &bufferIStream, &uiMeta, &uiAltMeta))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + break; + } + + if (!uiMeta) + { + if (!uiAltMeta) + { + continue; + } + + uiMeta = uiAltMeta; + } + + // Generate the from and until keys for this index. If the estimated + // cost is low enough, we will look no further for a better index. + + if (pFSIndexCursor) + { + pFSIndexCursor->resetCursor(); + } + else + { + if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + // Temporarily change the from value in the predicate + // to point to the metaphone value. That is what the + // key will be generated on. + + metaValue.val.uiVal = uiMeta; + pPred->pFromValue = &metaValue; + rc = pFSIndexCursor->setupKeys( m_pDb, pIcd->pIxd, pPred, + &bDoNodeMatch, &bTmpCanCompareOnKey, + &uiLeafBlocksBetween, &uiTotalRefs, + &bTotalsEstimated); + + // Restore the from value in the predicate before + // going any further. + + pPred->pFromValue = pSaveValue; + if (RC_BAD( rc)) + { + goto Exit; + } + + // Will always have to fetch the node, so cost should + // always include uiTotalRefs in this case. + + uiMetaCost = uiLeafBlocksBetween + uiTotalRefs; + if (!uiMetaCost) + { + uiMetaCost = 1; + } + if (!uiCost || uiMetaCost < uiCost) + { + uiCost = uiMetaCost; + if (uiCost < MIN_OPT_COST) + { + break; + } + } + } + bTmpCanCompareOnKey = FALSE; + bDoNodeMatch = TRUE; + } + else + { + + // Generate the from and until keys for this index. If the estimated + // cost is low enough, we will look no further for a better index. + + if (pFSIndexCursor) + { + pFSIndexCursor->resetCursor(); + } + else + { + if ((pFSIndexCursor = f_new FSIndexCursor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + if (RC_BAD( rc = pFSIndexCursor->setupKeys( m_pDb, pIcd->pIxd, pPred, + &bDoNodeMatch, &bTmpCanCompareOnKey, + &uiLeafBlocksBetween, &uiTotalRefs, + &bTotalsEstimated))) + { + goto Exit; + } + if (!bTmpCanCompareOnKey) + { + bDoNodeMatch = TRUE; + } + uiCost = uiLeafBlocksBetween + uiTotalRefs; + } + + // Could be that there are zero leaf blocks between and + // bDoRecMatch is FALSE. But we do not want a cost of + // zero. + + if (!uiCost) + { + uiCost = 1; + } + + if (!pPred->OptInfo.uiCost || uiCost < pPred->OptInfo.uiCost) + { + FSIndexCursor * pTmpFSIndexCursor; + F_NameTable * pNameTable = NULL; + + // Exchange the temporary file system cursor and the + // file system cursor inside the predicate. Want to + // keep the temporary one and reuse the one that was + // inside the sub-query. + + pTmpFSIndexCursor = pPred->pFSIndexCursor; + pPred->pFSIndexCursor = pFSIndexCursor; + pFSIndexCursor = pTmpFSIndexCursor; + pPred->OptInfo.eOptType = XFLM_QOPT_USING_INDEX; + pPred->OptInfo.uiIxNum = pIcd->pIxd->uiIndexNum; + pPred->OptInfo.szIxName [0] = 0; + if (RC_OK( m_pDb->getNameTable( &pNameTable))) + { + FLMUINT uiIxNameLen = sizeof( pPred->OptInfo.szIxName); + if (RC_BAD( pNameTable->getFromTagTypeAndNum( + m_pDb, ELM_INDEX_TAG, + pPred->OptInfo.uiIxNum, NULL, + (char *)pPred->OptInfo.szIxName, + &uiIxNameLen, NULL, NULL, + NULL, NULL, TRUE))) + { + pPred->OptInfo.szIxName [0] = 0; + } + } + if (pNameTable) + { + pNameTable->Release(); + } + pPred->OptInfo.uiCost = uiCost; + pPred->OptInfo.bDoNodeMatch = bDoNodeMatch; + pPred->OptInfo.bMustVerifyPath = bMustVerifyPath; + pPred->OptInfo.bCanCompareOnKey = + (bTmpCanCompareOnKey && bCanCompareOnKey) ? TRUE : FALSE; + + // If the cost is now low enough, we will quit looking + + if (uiCost < MIN_OPT_COST) + { + break; + } + } + } + + // If no index was found, the predicate will force a full + // collection scan. + + if (!pPred->OptInfo.uiCost) + { + pPred->OptInfo.uiCost = ~((FLMUINT)0); + pPred->OptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + } + +Exit: + + if (pFSIndexCursor) + { + pFSIndexCursor->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Optimize a context path. +***************************************************************************/ +RCODE F_Query::optimizePath( + CONTEXT_PATH * pContextPath, + PATH_PRED * pSingleNodeIdPred, + FLMBOOL bIntersect + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pPred; + XPATH_COMPONENT * pXPathComponent = pContextPath->pXPathComponent; + + pPred = pContextPath->pFirstPred; + if (bIntersect) + { + pContextPath->uiCost = ~((FLMUINT)0); + pContextPath->pSelectedPred = NULL; + pContextPath->bMustScan = TRUE; + + // If we already know we have a single node id predicate, there + // is no need to optimize any of the other predicates, because + // this one is guaranteed to have the lowest cost: 1. + + if (pSingleNodeIdPred) + { + if (RC_BAD( rc = optimizePredicate( pXPathComponent, + pSingleNodeIdPred))) + { + goto Exit; + } + + // Cost better have returned as 1! and the optimization + // better have been XFLM_QOPT_SINGLE_NODE_ID. + + flmAssert( pSingleNodeIdPred->OptInfo.uiCost == 1); + flmAssert( pSingleNodeIdPred->OptInfo.eOptType == + XFLM_QOPT_SINGLE_NODE_ID); + pContextPath->uiCost = pSingleNodeIdPred->OptInfo.uiCost; + pContextPath->pSelectedPred = pSingleNodeIdPred; + pContextPath->bMustScan = FALSE; + } + else + { + while (pPred) + { + if (RC_BAD( rc = optimizePredicate( pXPathComponent, pPred))) + { + goto Exit; + } + if (pPred->OptInfo.eOptType != XFLM_QOPT_FULL_COLLECTION_SCAN && + (pPred->OptInfo.uiCost < pContextPath->uiCost || + pContextPath->bMustScan)) + { + pContextPath->uiCost = pPred->OptInfo.uiCost; + if (pContextPath->pSelectedPred) + { + if (pContextPath->pSelectedPred->pFSIndexCursor) + { + pContextPath->pSelectedPred->pFSIndexCursor->Release(); + pContextPath->pSelectedPred->pFSIndexCursor = NULL; + } + else if (pContextPath->pSelectedPred->pFSCollectionCursor) + { + pContextPath->pSelectedPred->pFSCollectionCursor->Release(); + pContextPath->pSelectedPred->pFSCollectionCursor = NULL; + } + else if (pContextPath->pSelectedPred->pNodeSource) + { + pContextPath->pSelectedPred->pNodeSource->Release(); + pContextPath->pSelectedPred->pNodeSource = NULL; + } + } + pContextPath->pSelectedPred = pPred; + pContextPath->bMustScan = FALSE; + + // No need to evaluate more predicates if the cost is + // MIN_OPT_COST or below. + + if (pPred->OptInfo.uiCost < MIN_OPT_COST) + { + break; + } + } + else + { + if (pPred->pFSIndexCursor) + { + pPred->pFSIndexCursor->Release(); + pPred->pFSIndexCursor = NULL; + } + else if (pPred->pFSCollectionCursor) + { + pPred->pFSCollectionCursor->Release(); + pPred->pFSCollectionCursor = NULL; + } + else if (pPred->pNodeSource) + { + pContextPath->pSelectedPred->pNodeSource->Release(); + pContextPath->pSelectedPred->pNodeSource = NULL; + } + } + pPred = pPred->pNext; + } + } + } + else + { + pContextPath->uiCost = 0; + while (pPred) + { + if (RC_BAD( rc = optimizePredicate( pXPathComponent, pPred))) + { + goto Exit; + } + if (~((FLMUINT)0) - pContextPath->uiCost > pPred->OptInfo.uiCost && + pPred->OptInfo.eOptType != XFLM_QOPT_FULL_COLLECTION_SCAN) + { + pContextPath->uiCost += pPred->OptInfo.uiCost; + } + else + { + pContextPath->uiCost = ~((FLMUINT)0); + pContextPath->bMustScan = TRUE; + break; + } + pPred = pPred->pNext; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Optimize a context. It's child contexts have already been + optimized. +***************************************************************************/ +RCODE F_Query::optimizeContext( + OP_CONTEXT * pContext, + CONTEXT_PATH * pSingleNodeIdPath, + PATH_PRED * pSingleNodeIdPred + ) +{ + RCODE rc = NE_XFLM_OK; + OP_CONTEXT * pChildContext; + CONTEXT_PATH * pContextPath; + + // If this context has no CONTEXT_PATHs and no child contexts, + // it had to have had only non-optimizable predicates, or + // no context would have been created. In that case, this context + // must be forced to scan. + + if (pContext->bForceOptToScan || + (!pContext->pFirstPath && !pContext->pFirstChild)) + { + pContext->uiCost = ~((FLMUINT)0); + pContext->bMustScan = TRUE; + goto Exit; + } + + // Optimize each of the context paths, if any. + // If this is an intersect context, select the context path + // one a cost that is lower than the context's current cost. + // The context's current cost may have already been set when + // its child contexts were optimized. + + if (pContext->bIntersect) + { + pContext->bMustScan = TRUE; + pContext->uiCost = ~((FLMUINT)0); + + // Determine what the lowest cost child context is. + // If we have a single node id path, we already know + // it is guaranteed to have the lowest cost: 1. + + if (pSingleNodeIdPath) + { + if (RC_BAD( rc = optimizePath( pSingleNodeIdPath, + pSingleNodeIdPred, TRUE))) + { + goto Exit; + } + + // Cost better have returned as one! + // Its bMustScan flag had also better be set to FALSE! + + flmAssert( pSingleNodeIdPath->uiCost == 1); + flmAssert( !pSingleNodeIdPath->bMustScan); + pContext->uiCost = pSingleNodeIdPath->uiCost; + pContext->pSelectedPath = pSingleNodeIdPath; + pContext->pSelectedChild = NULL; + pContext->bMustScan = FALSE; + } + else + { + pChildContext = pContext->pFirstChild; + while (pChildContext) + { + if (!pChildContext->bMustScan && + (pChildContext->uiCost < pContext->uiCost || + pContext->bMustScan)) + { + pContext->uiCost = pChildContext->uiCost; + pContext->pSelectedChild = pChildContext; + pContext->pSelectedPath = NULL; + pContext->bMustScan = FALSE; + } + pChildContext = pChildContext->pNextSib; + } + + // Find the most optimal context path + + pContextPath = pContext->pFirstPath; + while (pContextPath && pContext->uiCost >= MIN_OPT_COST) + { + if (RC_BAD( rc = optimizePath( pContextPath, NULL, TRUE))) + { + goto Exit; + } + if (!pContextPath->bMustScan && + (pContextPath->uiCost < pContext->uiCost || + pContext->bMustScan)) + { + pContext->uiCost = pContextPath->uiCost; + pContext->pSelectedPath = pContextPath; + pContext->pSelectedChild = NULL; + pContext->bMustScan = FALSE; + + // If the cost is below MIN_OPT_COST, no need to do any more + // cost analysis. + + if (pContextPath->uiCost < MIN_OPT_COST) + { + break; + } + } + pContextPath = pContextPath->pNext; + } + } + } + + // Have a UNION (OR) context + + else + { + + // In the case of union, there is no point in optimizing any + // of the context paths paths once we discover that a context + // must scan the database. + + pContext->bMustScan = FALSE; + pContext->uiCost = 0; + + // Add in the cost of each child context. + + pChildContext = pContext->pFirstChild; + while (pChildContext) + { + if (pChildContext->bMustScan) + { + pContext->bMustScan = TRUE; + pContext->uiCost = ~((FLMUINT)0); + + // Once we set the bMustScan flag, there is no point + // in going any further. + + break; + } + else if (~((FLMUINT)0) - pContext->uiCost > pChildContext->uiCost) + { + pContext->uiCost += pChildContext->uiCost; + } + else + { + pContext->uiCost = ~((FLMUINT)0); + pContext->bMustScan = TRUE; + + // Once we set the bMustScan flag, there is no point + // in going any further. + + break; + } + pChildContext = pChildContext->pNextSib; + } + + // In the case of a non-intersecting context, sum the costs of + // each context path into the cost for the context until we + // hit ~0, at which time we will mark the context as + // "must scan" and quit attempting to optimize it. + + pContextPath = pContext->pFirstPath; + while (pContextPath && !pContext->bMustScan) + { + if (RC_BAD( rc = optimizePath( pContextPath, NULL, FALSE))) + { + goto Exit; + } + if (pContextPath->bMustScan) + { + pContext->uiCost = ~((FLMUINT)0); + pContext->bMustScan = TRUE; + + // No need to do any more optimization once we have + // determined to do a collection scan. + + break; + } + else if (~((FLMUINT)0) - pContext->uiCost > pContextPath->uiCost) + { + pContext->uiCost += pContextPath->uiCost; + } + else + { + pContext->uiCost = ~((FLMUINT)0); + pContext->bMustScan = TRUE; + + // No need to do any more optimization once we have + // determined to do a collection scan. + + break; + } + pContextPath = pContextPath->pNext; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Setup to scan an index - the index was specified by the user. +***************************************************************************/ +RCODE F_Query::setupIndexScan( void) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + FLMBOOL bDoNodeMatch; + FLMBOOL bCanCompareOnKey; + + flmAssert( m_uiIndex); + + // If the index is not on-line or it is not associated + // with the collection, we have a problem. + + if (RC_BAD( rc = m_pDb->m_pDict->getIndex( m_uiIndex, NULL, + &pIxd, FALSE))) + { + goto Exit; + } + if (pIxd->uiCollectionNum != m_uiCollection) + { + rc = RC_SET( NE_XFLM_BAD_IX); + goto Exit; + } + + if ((m_pFSIndexCursor = f_new FSIndexCursor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + } + + // Setup to scan from beginning of key to end of key. + + if (RC_BAD( rc = m_pFSIndexCursor->setupKeys( m_pDb, pIxd, NULL, + &bDoNodeMatch, &bCanCompareOnKey, + NULL, NULL, NULL))) + { + goto Exit; + } + m_bScanIndex = TRUE; + m_bScan = FALSE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: This routine determines if an ICD has an descendents that are key + components. +***************************************************************************/ +FSTATIC FLMBOOL haveChildKeyComponents( + ICD * pParentIcd) +{ + ICD * pTmpIcd; + + if ((pTmpIcd = pParentIcd->pFirstChild) == NULL) + { + return( FALSE); + } + for (;;) + { + if (pTmpIcd->uiKeyComponent) + { + return( TRUE); + } + if (pTmpIcd->pFirstChild) + { + pTmpIcd = pTmpIcd->pFirstChild; + } + else + { + while (!pTmpIcd->pNextSibling) + { + if ((pTmpIcd = pTmpIcd->pParent) == pParentIcd) + { + return( FALSE); + } + } + if (!pTmpIcd) + { + break; + } + pTmpIcd = pTmpIcd->pNextSibling; + } + } + return( FALSE); +} + +/*************************************************************************** +Desc: Determines if the optimization index already has the order we need + for sorting. +***************************************************************************/ +RCODE F_Query::checkSortIndex( + FLMUINT uiOptIndex) +{ + RCODE rc = NE_XFLM_OK; + IXD * pOptIxd; + ICD * pSortIcd; + ICD * pOptIcd; + + // Should not be called unless we have a sort key specified. + + flmAssert( m_pSortIxd); + + // Get the index definition. + + if (RC_BAD( rc = m_pDb->m_pDict->getIndex( uiOptIndex, NULL, &pOptIxd, TRUE))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + // Verify that the language is the same. + + if (m_pSortIxd->uiLanguage != pOptIxd->uiLanguage) + { + goto Exit; + } + + // Make sure the sort IXD is completely represented in the optimization IXD + // If we find all of the keys in the same context, the optimization + // index will work as the sort index. Even if the optimization index + // has more key components it will work, because it will provide the + // keys in the same order. + + pSortIcd = m_pSortIxd->pIcdTree; + pOptIcd = pOptIxd->pIcdTree; + + for (;;) + { + + // See if there is a matching opt ICD in the list of siblings to + // opt ICD. + + pOptIcd = (pOptIcd->pParent) + ? pOptIcd->pParent->pFirstChild + : pOptIxd->pIcdTree; + while (pOptIcd) + { + if (pOptIcd->uiDictNum == pSortIcd->uiDictNum && + (pOptIcd->uiFlags & ICD_IS_ATTRIBUTE) == + (pSortIcd->uiFlags & ICD_IS_ATTRIBUTE)) + { + + // If the match ICD is a key component, we want a + // search ICD that is the same key component and compare rules. + + if (pSortIcd->uiKeyComponent) + { + if (pOptIcd->uiKeyComponent == pSortIcd->uiKeyComponent && + pOptIcd->uiFlags == pSortIcd->uiFlags) + { + break; + } + } + else + { + break; + } + } + pOptIcd = pOptIcd->pNextSibling; + } + + // Did we find a matching opt ICD? + + if (!pOptIcd) + { + + // If the sort ICD is a key component, or there are key ICDs + // subordinate to the sort ICD, then the indexes cannot match, + // because there is no matching context for the child key ICDs. + + if (pSortIcd->uiKeyComponent || haveChildKeyComponents( pSortIcd)) + { + goto Exit; + } + +Check_Siblings: + + while (!pSortIcd->pNextSibling) + { + if ((pSortIcd = pSortIcd->pParent) == NULL) + { + break; + } + pOptIcd = pOptIcd->pParent; + + // If pSortIcd != NULL, pOptIcd better be also! + + flmAssert( pOptIcd); + } + if (!pSortIcd) + { + + // Done - index key components match. + + break; + } + + // pNextSibling better be non-NULL at this point. + // NOTE: Do not set pOptIcd to its sibling - it may not have one. + // That doesn't matter, because we will search for a matching + // sibling up above. + + pSortIcd = pSortIcd->pNextSibling; + + // No need to check this sort ICD if it is not a key + // component and it has no child ICDs. + + if (!pSortIcd->uiKeyComponent && !pSortIcd->pFirstChild) + { + goto Check_Siblings; + } + } + else + { + + // Go to the child nodes, if any + + if (pSortIcd->pFirstChild) + { + if (!pOptIcd->pFirstChild) + { + + // Sort ICD has a child, opt ICD doesn't. See if there are + // any index components subordinate to sort ICD. If so, the indexes + // are different. Otherwise, it really doesn't matter - we can + // simply proceed with sort ICD's siblings. + + if (haveChildKeyComponents( pSortIcd)) + { + goto Exit; + } + goto Check_Siblings; + } + else + { + pSortIcd = pSortIcd->pFirstChild; + pOptIcd = pOptIcd->pFirstChild; + } + } + else + { + goto Check_Siblings; + } + } + } + + m_bEntriesAlreadyInOrder = TRUE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Optimize a query expression. +***************************************************************************/ +RCODE F_Query::optimize( void) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pQNode = m_pQuery; + OP_CONTEXT * pContext; + + if (m_bOptimized) + { + rc = RC_SET( NE_XFLM_Q_ALREADY_OPTIMIZED); + goto Exit; + } + + // We save the F_Database object so that we can always check and make + // sure we are associated with this database on any query operations + // that occur after optimization. -- Link it into the list of queries + // off of the F_Database object. NOTE: We may not always use the + // same F_Db object, but it must always be the same F_Database object. + + m_pDatabase = m_pDb->m_pDatabase; + m_pNext = NULL; + m_pDatabase->lockMutex(); + if ((m_pPrev = m_pDatabase->m_pLastQuery) != NULL) + { + m_pPrev->m_pNext = this; + } + else + { + m_pDatabase->m_pFirstQuery = this; + } + m_pDatabase->m_pLastQuery = this; + m_pDatabase->unlockMutex(); + + // Verify sort keys, if any + + if (m_pSortIxd) + { + if (RC_BAD( rc = verifySortKeys())) + { + goto Exit; + } + } + + // Make sure we have a completed expression + + if( m_pCurExprState) + { + if ( m_pCurExprState->pPrev || + m_pCurExprState->uiNestLevel || + (m_pCurExprState->pLastNode && + m_pCurExprState->pLastNode->eNodeType == FLM_OPERATOR_NODE)) + { + rc = RC_SET( NE_XFLM_Q_INCOMPLETE_QUERY_EXPR); + goto Exit; + } + if (RC_BAD( rc = getPredicates( &m_pCurExprState->pExpr, NULL, NULL))) + { + goto Exit; + } + + m_pQuery = m_pCurExprState->pExpr; + } + + m_uiLanguage = m_pDb->getDefaultLanguage(); + + // An empty expression should scan the database and return everything. + + if ((pQNode = m_pQuery) == NULL) + { + if (m_bIndexSet && m_uiIndex) + { + rc = setupIndexScan(); + } + else + { + m_bScan = TRUE; + } + goto Exit; + } + + // Handle the case of a value node or arithmetic expression at the root + // These types of expressions do not return results from the database. + + if (pQNode->eNodeType == FLM_VALUE_NODE) + { + if (pQNode->currVal.eValType == XFLM_BOOL_VAL && + pQNode->currVal.val.eBool == XFLM_TRUE) + { + m_bScan = TRUE; + } + else + { + m_bEmpty = TRUE; + } + } + else if (pQNode->eNodeType == FLM_OPERATOR_NODE && + isArithOp( pQNode->nd.op.eOperator)) + { + m_bEmpty = TRUE; + goto Exit; + } + + // If the user explicitly said to NOT use an index, we will not + + if (m_bIndexSet && !m_uiIndex) + { + m_bScan = TRUE; + goto Exit; + } + + // Start with the context in the root node. + + if ((pContext = pQNode->pContext) == NULL) + { + m_bScan = TRUE; + goto Exit; + } + + for (;;) + { + CONTEXT_PATH * pSingleNodeIdPath = NULL; + PATH_PRED * pSingleNodeIdPred = NULL; + + // See if any of the context paths in this context have a + // single node ID predicate. If so, we can optimize it right + // away and ignore all of the other predicates. + + if (pContext->bIntersect) + { + pSingleNodeIdPath = pContext->pFirstPath; + while (pSingleNodeIdPath) + { + if (isNodeOrDocIdComponent( pSingleNodeIdPath->pXPathComponent)) + { + + // See if any of the predicates are single-node id. + + pSingleNodeIdPred = pSingleNodeIdPath->pFirstPred; + while (pSingleNodeIdPred) + { + if (pSingleNodeIdPred->pFromValue && + pSingleNodeIdPred->pUntilValue) + { + + // From and until values should have already been + // converted to appropriate node ids - 64 bit values. + + flmAssert( pSingleNodeIdPred->pFromValue->eValType == + XFLM_UINT64_VAL); + flmAssert( pSingleNodeIdPred->pUntilValue->eValType == + XFLM_UINT64_VAL); + if (!pSingleNodeIdPred->bInclFrom) + { + pSingleNodeIdPred->pFromValue->val.ui64Val++; + pSingleNodeIdPred->bInclFrom = TRUE; + } + if (!pSingleNodeIdPred->bInclUntil) + { + pSingleNodeIdPred->pUntilValue->val.ui64Val--; + pSingleNodeIdPred->bInclUntil = TRUE; + } + if (pSingleNodeIdPred->pFromValue->val.ui64Val == + pSingleNodeIdPred->pUntilValue->val.ui64Val) + { + break; + } + } + pSingleNodeIdPred = pSingleNodeIdPred->pNext; + } + } + if (pSingleNodeIdPred) + { + break; + } + + pSingleNodeIdPath = pSingleNodeIdPath->pNext; + } + } + if (pContext->pFirstChild && !pSingleNodeIdPath) + { + pContext = pContext->pFirstChild; + continue; + } + + // Optimize the context paths of the context we are on + + if (RC_BAD( rc = optimizeContext( pContext, + pSingleNodeIdPath, pSingleNodeIdPred))) + { + goto Exit; + } + + // Go to sibling context, if any + + while (!pContext->pNextSib) + { + if ((pContext = pContext->pParent) == NULL) + { + break; + } + + // Child contexts have all been optimized and the cost + // reflected up to this context. But there may be + // CONTEXT_PATHs on this context that will change that + // cost. + + if (RC_BAD( rc = optimizeContext( pContext, NULL, NULL))) + { + goto Exit; + } + } + + // If pContext is NULL at this point, there are no + // other contexts to optimize. + + if (!pContext) + { + break; + } + pContext = pContext->pNextSib; + + // There has to have been a sibling context at this point. + + flmAssert( pContext); + } + + if (m_pQuery->pContext->bMustScan) + { + + // If we were told to use an index, we will use that index + // and scan the entire index. + + if (m_bIndexSet) + { + if (RC_BAD( rc = setupIndexScan())) + { + goto Exit; + } + } + else + { + m_bScan = TRUE; + } + } + +Exit: + + if (RC_OK( rc) && !m_bEmpty) + { + FLMUINT uiOptIndex = 0; + + if (m_bScan || m_bScanIndex) + { + if (m_bScan) + { + m_scanOptInfo.eOptType = XFLM_QOPT_FULL_COLLECTION_SCAN; + } + else + { + F_NameTable * pNameTable = NULL; + + m_scanOptInfo.eOptType = XFLM_QOPT_USING_INDEX; + m_scanOptInfo.uiCost = ~((FLMUINT)0); + m_scanOptInfo.uiIxNum = m_uiIndex; + uiOptIndex = m_uiIndex; + m_scanOptInfo.bMustVerifyPath = TRUE; + m_scanOptInfo.bDoNodeMatch = TRUE; + m_scanOptInfo.bCanCompareOnKey = FALSE; + m_scanOptInfo.szIxName [0] = 0; + if (RC_OK( m_pDb->getNameTable( &pNameTable))) + { + FLMUINT uiIxNameLen = sizeof( m_scanOptInfo.szIxName); + + if (RC_BAD( pNameTable->getFromTagTypeAndNum( + m_pDb, ELM_INDEX_TAG, + m_uiIndex, NULL, + (char *)m_scanOptInfo.szIxName, + &uiIxNameLen, NULL, NULL, + NULL, NULL, TRUE))) + { + m_scanOptInfo.szIxName [0] = 0; + } + } + if (pNameTable) + { + pNameTable->Release(); + } + } + m_scanOptInfo.ui64KeysRead = 0; + m_scanOptInfo.ui64KeyHadDupDoc = 0; + m_scanOptInfo.ui64KeysPassed = 0; + m_scanOptInfo.ui64NodesRead = 0; + m_scanOptInfo.ui64NodesTested = 0; + m_scanOptInfo.ui64NodesPassed = 0; + m_scanOptInfo.ui64DocsRead = 0; + m_scanOptInfo.ui64DupDocsEliminated = 0; + m_scanOptInfo.ui64NodesFailedValidation = 0; + m_scanOptInfo.ui64DocsFailedValidation = 0; + m_scanOptInfo.ui64DocsPassed = 0; + m_pCurrOpt = &m_scanOptInfo; + rc = newSource(); + } + else + { + FLMBOOL bHaveMultipleIndexes = FALSE; + + // Need to initialize all of the optimization information. + + m_pCurrContext = m_pQuery->pContext; + useLeafContext( TRUE); + do + { + m_pCurrOpt->ui64KeysRead = 0; + m_pCurrOpt->ui64KeyHadDupDoc = 0; + m_pCurrOpt->ui64KeysPassed = 0; + m_pCurrOpt->ui64NodesRead = 0; + m_pCurrOpt->ui64NodesTested = 0; + m_pCurrOpt->ui64NodesPassed = 0; + m_pCurrOpt->ui64DocsRead = 0; + m_pCurrOpt->ui64DupDocsEliminated = 0; + m_pCurrOpt->ui64NodesFailedValidation = 0; + m_pCurrOpt->ui64DocsFailedValidation = 0; + m_pCurrOpt->ui64DocsPassed = 0; + + // See if we are using an index. Later, if it turns out + // we are only using one index, we will see if it matches + // the sort index. + + if (m_pSortIxd && !bHaveMultipleIndexes) + { + if (m_pCurrPred->pNodeSource) + { + FLMUINT uiIndex; + + if (RC_BAD( rc = m_pCurrPred->pNodeSource->getIndex( + (IF_Db *)m_pDb, &uiIndex, + &bHaveMultipleIndexes))) + { + break; + } + if (uiIndex) + { + if (uiOptIndex == 0) + { + uiOptIndex = uiIndex; + } + else if (uiIndex != uiOptIndex) + { + bHaveMultipleIndexes = TRUE; + } + } + } + else if (m_pCurrOpt->uiIxNum) + { + if (uiOptIndex == 0) + { + uiOptIndex = m_pCurrOpt->uiIxNum; + } + else if (m_pCurrOpt->uiIxNum != uiOptIndex) + { + bHaveMultipleIndexes = TRUE; + } + } + } + } + while (useNextPredicate()); + if (bHaveMultipleIndexes || RC_BAD( rc)) + { + uiOptIndex = 0; + } + } + + // If we are sorting the results, see if the optimization + // index already has the keys in the order we need. + + if (RC_OK( rc) && m_pSortIxd && uiOptIndex) + { + rc = checkSortIndex( uiOptIndex); + } + } + + if (m_pSortIxd || m_bPositioningEnabled) + { + // Need to eliminate dups in case there are multiple sort key + // values in a document. + // NOTE: When a sort key is specified, if a document has multiple + // keys, we want them to appear sorted according to whatever is the + // "lowest" sort key in the document. If we create a result set, + // this will always work. However, if the entries are already "in order" + // we have a small problem, because the order in which the document + // is retrieved will then depend on the direction of traversal used + // by the application. By eliminating duplicate documents, the application + // can get the expected behavior of having the document sort according + // to its "lowest" key if it reads forward. However, if the application + // reads "backward" the document will be retrieved as if it were sorted + // by its "higest" key. + + setDupHandling( TRUE); + + // If we have sort keys and the optimization index is not in + // the same order as the sort index, we must create a result set. + // The same is true if the application asked to enable positioning. + + if (m_bPositioningEnabled || !m_bEntriesAlreadyInOrder) + { + if (RC_OK( rc)) + { + rc = createResultSet(); + } + } + } + + if ( RC_OK( rc)) + { + m_bOptimized = TRUE; + } + + return( rc); +} + +/*************************************************************************** +Desc: Set node's current value to missing. Also releases the stream + associated with the value, if any. +***************************************************************************/ +FINLINE void fqReleaseNodeValue( + FQNODE * pQNode + ) +{ + if ((pQNode->currVal.eValType == XFLM_BINARY_VAL || + pQNode->currVal.eValType == XFLM_UTF8_VAL) && + (pQNode->currVal.uiFlags & VAL_IS_STREAM) && + pQNode->currVal.val.pIStream) + { + pQNode->currVal.uiFlags &= (~(VAL_IS_STREAM)); + pQNode->currVal.val.pIStream->Release(); + pQNode->currVal.val.pIStream = NULL; + } + if (pQNode->eNodeType != FLM_VALUE_NODE) + { + pQNode->currVal.eValType = XFLM_MISSING_VAL; + } +} + +/*************************************************************************** +Desc: Evaluate a simple operator. +***************************************************************************/ +FSTATIC RCODE fqEvalOperator( + FLMUINT uiLanguage, + FQNODE * pQNode + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pLeftOperand; + FQNODE * pRightOperand; + XFlmBoolType eLeftBool; + XFlmBoolType eRightBool; + + // Right now we are only able to do operator nodes. + + flmAssert( pQNode->eNodeType == FLM_OPERATOR_NODE); + + pLeftOperand = pQNode->pFirstChild; + pRightOperand = pQNode->pLastChild; + + // See if either of this operator's operands have already + // passed. If so, we can skip the evaluation. + + if (pLeftOperand->currVal.eValType == XFLM_PASSING_VAL || + pRightOperand->currVal.eValType == XFLM_PASSING_VAL) + { + pQNode->currVal.eValType = XFLM_BOOL_VAL; + pQNode->currVal.val.eBool = XFLM_TRUE; + goto Exit; + } + + if (pQNode->nd.op.eOperator != XFLM_AND_OP && + pQNode->nd.op.eOperator != XFLM_OR_OP) + { + // If the left operand is an XPATH node that is the node id or + // document id, be sure to convert the right operand value to + // a node id value. + + if (pLeftOperand->eNodeType == FLM_XPATH_NODE && + pLeftOperand->nd.pXPath->pLastComponent->eXPathAxis == META_AXIS) + { + if (RC_BAD( rc = fqGetNodeIdValue( &pRightOperand->currVal))) + { + goto Exit; + } + } + + // If the right operand is an XPATH node that is the node id or + // document id, be sure to convert the left operand value to + // a node id value. + + if (pRightOperand->eNodeType == FLM_XPATH_NODE && + pRightOperand->nd.pXPath->pLastComponent->eXPathAxis == META_AXIS) + { + if (RC_BAD( rc = fqGetNodeIdValue( &pLeftOperand->currVal))) + { + goto Exit; + } + } + } + + pQNode->currVal.eValType = XFLM_MISSING_VAL; + + switch (pQNode->nd.op.eOperator) + { + case XFLM_AND_OP: + case XFLM_OR_OP: + eLeftBool = XFLM_UNKNOWN; + eRightBool = XFLM_UNKNOWN; + + // Get the left operand + + if (pLeftOperand->eNodeType == FLM_OPERATOR_NODE) + { + + // This operator may not have been evaluated because of missing + // XPATH values in one or both operands, in which case + // its state will be XFLM_MISSING_VALUE. If it was evaluated, + // its state should show a boolean value. + + if (pLeftOperand->currVal.eValType == XFLM_MISSING_VAL) + { + eLeftBool = (pLeftOperand->bNotted ? XFLM_TRUE : XFLM_FALSE); + } + else + { + flmAssert( pLeftOperand->currVal.eValType == XFLM_BOOL_VAL); + eLeftBool = pLeftOperand->currVal.val.eBool; + } + } + else if (pLeftOperand->eNodeType == FLM_XPATH_NODE) + { + if (!pLeftOperand->bNotted) + { + eLeftBool = (pLeftOperand->currVal.eValType != XFLM_MISSING_VAL) + ? XFLM_TRUE + : XFLM_FALSE; + } + else + { + eLeftBool = (pLeftOperand->currVal.eValType != XFLM_MISSING_VAL) + ? XFLM_FALSE + : XFLM_TRUE; + } + } + else if (pLeftOperand->eNodeType == FLM_VALUE_NODE) + { + flmAssert( pLeftOperand->currVal.eValType == XFLM_BOOL_VAL); + eLeftBool = pLeftOperand->currVal.val.eBool; + } + else + { + flmAssert( pLeftOperand->eNodeType == FLM_FUNCTION_NODE); + if (!pLeftOperand->bNotted) + { + eLeftBool = fqTestValue( pLeftOperand) ? XFLM_TRUE : XFLM_FALSE; + } + else + { + eLeftBool = fqTestValue( pLeftOperand) ? XFLM_FALSE : XFLM_TRUE; + } + } + + // Get the right operand + + if ( pRightOperand->eNodeType == FLM_OPERATOR_NODE) + { + + // This operator may not have been evaluated because of missing + // XPATH values in one or both operands, in which case + // its state will be XFLM_MISSING_VALUE. If it was evaluated, + // its state should show a boolean value. + + if (pRightOperand->currVal.eValType == XFLM_MISSING_VAL) + { + eRightBool = (pRightOperand->bNotted ? XFLM_TRUE : XFLM_FALSE); + } + else + { + flmAssert( pRightOperand->currVal.eValType == XFLM_BOOL_VAL); + eRightBool = pRightOperand->currVal.val.eBool; + } + } + else if (pRightOperand->eNodeType == FLM_XPATH_NODE) + { + if (!pRightOperand->bNotted) + { + eRightBool = (pRightOperand->currVal.eValType != XFLM_MISSING_VAL) + ? XFLM_TRUE + : XFLM_FALSE; + } + else + { + eRightBool = (pRightOperand->currVal.eValType != XFLM_MISSING_VAL) + ? XFLM_FALSE + : XFLM_TRUE; + } + } + else if (pRightOperand->eNodeType == FLM_VALUE_NODE) + { + flmAssert( pRightOperand->currVal.eValType == XFLM_BOOL_VAL); + eRightBool = pRightOperand->currVal.val.eBool; + } + else + { + flmAssert( pRightOperand->eNodeType == FLM_FUNCTION_NODE); + if (!pRightOperand->bNotted) + { + eRightBool = fqTestValue( pRightOperand) ? XFLM_TRUE : XFLM_FALSE; + } + else + { + eRightBool = fqTestValue( pRightOperand) ? XFLM_FALSE : XFLM_TRUE; + } + } + + // Calculate the answer + + pQNode->currVal.eValType = XFLM_BOOL_VAL; + if (pQNode->nd.op.eOperator == XFLM_AND_OP) + { + if (eLeftBool == XFLM_FALSE || eRightBool == XFLM_FALSE) + { + pQNode->currVal.val.eBool = XFLM_FALSE; + } + else if (eLeftBool == XFLM_UNKNOWN || eRightBool == XFLM_UNKNOWN) + { + pQNode->currVal.val.eBool = XFLM_UNKNOWN; + } + else + { + + // Both have to be XFLM_TRUE at this point + + pQNode->currVal.val.eBool = XFLM_TRUE; + } + } + else // pQNode->nd.op.eOperator == XFLM_OR_OP + { + if (eLeftBool == XFLM_TRUE || eRightBool == XFLM_TRUE) + { + pQNode->currVal.val.eBool = XFLM_TRUE; + } + else if (eLeftBool == XFLM_UNKNOWN || eRightBool == XFLM_UNKNOWN) + { + pQNode->currVal.val.eBool = XFLM_UNKNOWN; + } + else + { + + // Both have to be XFLM_FALSE at this point + + pQNode->currVal.val.eBool = XFLM_FALSE; + } + } + break; + + case XFLM_EQ_OP: + case XFLM_APPROX_EQ_OP: + case XFLM_NE_OP: + case XFLM_LT_OP: + case XFLM_LE_OP: + case XFLM_GT_OP: + case XFLM_GE_OP: + pQNode->currVal.eValType = XFLM_BOOL_VAL; + if (RC_BAD( rc = fqCompareOperands( uiLanguage, + &pLeftOperand->currVal, + &pRightOperand->currVal, + pQNode->nd.op.eOperator, + pQNode->nd.op.uiCompareRules, + pQNode->nd.op.pOpComparer, + pQNode->bNotted, + &pQNode->currVal.val.eBool))) + { + goto Exit; + } + break; + + case XFLM_BITAND_OP: + case XFLM_BITOR_OP: + case XFLM_BITXOR_OP: + case XFLM_MULT_OP: + case XFLM_DIV_OP: + case XFLM_MOD_OP: + case XFLM_PLUS_OP: + case XFLM_MINUS_OP: + case XFLM_NEG_OP: + if (RC_BAD( rc = fqArithmeticOperator( &pLeftOperand->currVal, + &pRightOperand->currVal, + pQNode->nd.op.eOperator, + &pQNode->currVal))) + { + goto Exit; + } + break; + + default: + break; + } + +Exit: + + // Need to release node values in case we are holding on + // to an IStream. + + if (pLeftOperand) + { + fqReleaseNodeValue( pLeftOperand); + } + if (pRightOperand) + { + fqReleaseNodeValue( pRightOperand); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the next value from an XPATH node. +***************************************************************************/ +FSTATIC void fqResetIterator( + FQNODE * pQNode, + FLMBOOL bFullRelease, + FLMBOOL bUseKeyNodes + ) +{ + FXPATH * pXPath; + XPATH_COMPONENT * pXPathComponent; + + // Node better be an XPATH node. + + flmAssert( pQNode->eNodeType == FLM_XPATH_NODE); + pXPath = pQNode->nd.pXPath; + if (bFullRelease) + { + pXPath->bIsSource = FALSE; + pXPath->pSourceComponent = NULL; + pXPath->bHavePassingNode = FALSE; + } + pXPathComponent = pXPath->pLastComponent; + while (pXPathComponent) + { + if (bFullRelease) + { + pXPathComponent->bIsSource = FALSE; + pXPathComponent->pExprXPathSource = NULL; + pXPathComponent->pOptPred = NULL; + if (pXPathComponent->pKeyNode) + { + pXPathComponent->pKeyNode->Release(); + pXPathComponent->pKeyNode = NULL; + } + } + else if (pXPathComponent->bIsSource && bUseKeyNodes) + { + break; + } + if (pXPathComponent->pCurrNode) + { + pXPathComponent->pCurrNode->Release(); + pXPathComponent->pCurrNode = NULL; + } + + if (bFullRelease && pXPathComponent->pExpr) + { + fqReleaseQueryExpr( pXPathComponent->pExpr); + } + + pXPathComponent = pXPathComponent->pPrev; + } + pXPath->bGettingNodes = FALSE; +} + +/*************************************************************************** +Desc: Get the first, last, next or previous node from a node source object. +***************************************************************************/ +RCODE F_Query::getNodeSourceNode( + FLMBOOL bForward, + IF_QueryNodeSource * pNodeSource, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTimeLimit = m_uiTimeLimit; + FLMUINT uiCurrTime; + FLMUINT uiElapsedTime; + + // Determine the timeout limit that is left. + + if (uiTimeLimit) + { + uiCurrTime = FLM_GET_TIMER(); + uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime); + if (uiElapsedTime >= m_uiTimeLimit) + { + rc = RC_SET( NE_XFLM_TIMEOUT); + goto Exit; + } + else + { + FLM_TIMER_UNITS_TO_MILLI( (m_uiTimeLimit - uiElapsedTime), uiTimeLimit); + + // Always give at least one milli-second. + + if (!uiTimeLimit) + { + uiTimeLimit = 1; + } + } + } + + if (*ppCurrNode) + { + + // Get next or previous node. + + rc = (RCODE)(bForward + ? pNodeSource->getNext( (IF_Db *)m_pDb, pContextNode, ppCurrNode, + uiTimeLimit, m_pQueryStatus) + : pNodeSource->getPrev( (IF_Db *)m_pDb, pContextNode, ppCurrNode, + uiTimeLimit, m_pQueryStatus)); + } + else + { + + // Get first or last node. + + rc = (RCODE)(bForward + ? pNodeSource->getFirst( (IF_Db *)m_pDb, pContextNode, ppCurrNode, + uiTimeLimit, m_pQueryStatus) + : pNodeSource->getLast( (IF_Db *)m_pDb, pContextNode, ppCurrNode, + uiTimeLimit, m_pQueryStatus)); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + if (*ppCurrNode) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + } + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a ROOT_AXIS. +***************************************************************************/ +RCODE F_Query::getRootAxisNode( + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + + // Better be an element we are searching for + + if (m_pCurrDoc->getNodeType() == DOCUMENT_NODE) + { + if (RC_BAD( rc = m_pCurrDoc->getFirstChild( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // Search for an element node - it will be the root + // of the document + + while ((*ppCurrNode)->getNodeType() != ELEMENT_NODE) + { + if (RC_BAD( rc = (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + rc = NE_XFLM_OK; + } + goto Exit; + } + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + else + { + *ppCurrNode = m_pCurrDoc; + (*ppCurrNode)->AddRef(); + if ((*ppCurrNode)->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next/previous node in a document +***************************************************************************/ +RCODE F_Query::walkDocument( + FLMBOOL bForward, + FLMBOOL bWalkAttributes, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + + if (!(*ppCurrNode)) + { + *ppCurrNode = m_pCurrDoc; + (*ppCurrNode)->AddRef(); + goto Exit; + } + + if ((*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE) + { + if (uiAttrNameId) + { + + // This attribute node should be the node that we already processed. + // We simply want to go back to its parent node. No need to + // walk through all of the sibling attributes. + + rc = RC_SET( NE_XFLM_DOM_NODE_NOT_FOUND); + } + else + { + rc = bForward + ? (*ppCurrNode)->getNextSibling( m_pDb, + ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, + ppCurrNode); + } + if (RC_OK( rc)) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Need to get the node's encompassing element node and + // process it now. + + else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + + // See if the node has a child + + else if (RC_OK( rc = bForward + ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode))) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + +Walk_To_Attr_Nodes: + + if (bWalkAttributes && (*ppCurrNode)->getNodeType() == ELEMENT_NODE) + { + if( uiAttrNameId) + { + rc = (*ppCurrNode)->getAttribute( m_pDb, uiAttrNameId, ppCurrNode); + } + else + { + rc = bForward + ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode); + } + if (RC_OK( rc)) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + + // Go up tree until we find a sibling. + + for (;;) + { + if (RC_OK( rc = bForward + ? (*ppCurrNode)->getNextSibling( m_pDb, + ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, + ppCurrNode))) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + goto Walk_To_Attr_Nodes; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, + ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + break; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next or previous child node. +***************************************************************************/ +RCODE F_Query::getChildAxisNode( + FLMBOOL bForward, + IF_DOMNode * pContextNode, + FLMUINT uiChildNameId, + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + + if (!pContextNode) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + } + else + { + if (*ppCurrNode) + { + // If name id is non-zero, the caller should have prevented us + // from coming back in here. + + flmAssert( !uiChildNameId); + + // Get next or previous sibling - should still be a child + // of our context node, whatever it was. + + rc = (RCODE)(bForward + ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); + } + else if (uiChildNameId) + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + rc = (*ppCurrNode)->getChildElement( m_pDb, uiChildNameId, ppCurrNode); + } + else + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + rc = (RCODE)(bForward + ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a PARENT_AXIS. +***************************************************************************/ +RCODE F_Query::getParentAxisNode( + FLMBOOL bForward, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + if (!(*ppCurrNode)) + { + break; + } + + // This node must be a parent of another node, + // which means it must have at least one child node. + + if (RC_BAD( rc = (*ppCurrNode)->getFirstChildId( m_pDb, + &ui64NodeId))) + { + goto Exit; + } + if (ui64NodeId) + { + break; + } + } + } + else + { + + // PARENT_AXIS always starts from the context node - it + // really doesn't matter what the last node was. + + if (RC_BAD( rc = pContextNode->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + if (*ppCurrNode) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + rc = NE_XFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next ancestor node. +***************************************************************************/ +RCODE F_Query::getAncestorAxisNode( + FLMBOOL bForward, + FLMBOOL bIncludeSelf, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64ContextNodeId; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64ParentId; + FLMUINT64 ui64Tmp; + FLMUINT uiContextAttrNameId; + FLMUINT uiNameId; + FLMUINT uiTmp; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + + if (!(*ppCurrNode)) + { + break; + } + + // If we are including self, whatever node we get to matches + // the axis. + + if (bIncludeSelf) + { + break; + } + + // This node must be an ancestor of another node, + // which means it must have at least one child node. + + if (RC_BAD( rc = (*ppCurrNode)->getFirstChildId( m_pDb, + &ui64NodeId))) + { + goto Exit; + } + if (ui64NodeId) + { + break; + } + } + } + else + { + if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, + &ui64ContextNodeId, &uiContextAttrNameId))) + { + goto Exit; + } + + if (*ppCurrNode) + { + if (bForward) + { + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + else + { + if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, + &ui64NodeId, &uiNameId))) + { + goto Exit; + } + + // If the current node is the context node, we + // have no further to go. + + if( ui64NodeId == ui64ContextNodeId && + uiContextAttrNameId == uiNameId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + goto Exit; + } + + // Start from the context node, and go to just before + // the node we are on. If we don't hit it, this node + // is not an ancestor of our context node. + + (*ppCurrNode)->Release(); + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + + for (;;) + { + if (RC_BAD( rc = (*ppCurrNode)->getParentId( + m_pDb, &ui64ParentId))) + { + goto Exit; + } + if (ui64ParentId == ui64NodeId) + { + if (*ppCurrNode == pContextNode && !bIncludeSelf) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + break; + } + + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( + m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + } + else + { + if (bForward) + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + + // If including context node, we have what we want. + // Otherwise, we have to go to the parent. + + if (bIncludeSelf) + { + goto Exit; + } + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + else + { + // Start at document root. Return it if it is not the + // context node, or if we are including self. + + if( RC_BAD( rc = ((F_DOMNode *)m_pCurrDoc)->getNodeId( m_pDb, + &ui64Tmp, &uiTmp))) + { + goto Exit; + } + + if( ui64Tmp != ui64ContextNodeId || uiTmp != uiContextAttrNameId || + bIncludeSelf) + { + *ppCurrNode = m_pCurrDoc; + (*ppCurrNode)->AddRef(); + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a DESCENDANT_AXIS or a DESCENDANT_OR_SELF_AXIS. +***************************************************************************/ +RCODE F_Query::getDescendantAxisNode( + FLMBOOL bForward, + FLMBOOL bIncludeSelf, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64ContextNodeId; + FLMUINT64 ui64Tmp; + FLMUINT uiContextAttrNameId; + FLMUINT uiTmp; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + if (!(*ppCurrNode)) + { + break; + } + + // If we are including self, whatever node we get to matches + // the axis. + + if (bIncludeSelf) + { + break; + } + + // This node must be a descendant of some + // other node, so it must have a parent. + + if (RC_BAD( rc = (*ppCurrNode)->getParentId( m_pDb, &ui64NodeId))) + { + goto Exit; + } + if (ui64NodeId) + { + break; + } + } + } + else + { + if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, + &ui64ContextNodeId, &uiContextAttrNameId))) + { + goto Exit; + } + + if (bForward) + { + if (!(*ppCurrNode)) + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + if (bIncludeSelf) + { + goto Exit; + } + } + + // See if the node has a child + + if (RC_OK( rc = (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode))) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + + // Go up tree until we find a sibling. - Don't + // go up past the context node. + + for (;;) + { + + // If we are on the context node, we are done. + + if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, + &ui64Tmp, &uiTmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + goto Exit; + } + + if (RC_OK( rc = (*ppCurrNode)->getNextSibling( m_pDb, + ppCurrNode))) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + break; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, + ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + } + else + { + if (*ppCurrNode) + { + + // If we are going backwards and we are on the context node + // there are no more nodes to get. + + if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, + &ui64Tmp, &uiTmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + goto Exit; + } + + // See if the node has a previous sibling + + if (RC_BAD( rc = (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // Go down to the last child of the previous sibling + // that was found. + +Get_Last_Child: + + for (;;) + { + if (RC_BAD( rc = (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // We are positioned on the rightmost child now. + + rc = NE_XFLM_OK; + break; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + } + else + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + + // Go down to the last child of the context node. + + goto Get_Last_Child; + } + + // If we arrive at the context node, we are done, unless + // we are including self. + + if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, + &ui64Tmp, &uiTmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64ContextNodeId && uiTmp == uiContextAttrNameId && + !bIncludeSelf) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a PRECEDING_SIBLING_AXIS or + FOLLOWING_SIBLING_AXIS. +***************************************************************************/ +RCODE F_Query::getSibAxisNode( + FLMBOOL bForward, + FLMBOOL bPrevSibAxis, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64CurrNodeId; + FLMUINT64 ui64ContextNodeId; + FLMUINT uiCurrNameId; + FLMUINT uiContextAttrNameId; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + + if (!(*ppCurrNode)) + { + break; + } + + // This node must be the next or previous sibling to some + // node - which means it must have a previous or next sibling. + + rc = (RCODE)(bPrevSibAxis + ? (*ppCurrNode)->getNextSibId( m_pDb, + &ui64NodeId) + : (*ppCurrNode)->getPrevSibId( m_pDb, + &ui64NodeId)); + if (RC_BAD( rc)) + { + goto Exit; + } + if (ui64NodeId) + { + break; + } + } + } + else + { + if (bForward) + { + if (!(*ppCurrNode)) + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + } + rc = (RCODE)(bPrevSibAxis + ? (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else + { + if (*ppCurrNode) + { + rc = (RCODE)(bPrevSibAxis + ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else + { + FLMBOOL bAttr; + + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + + bAttr = (*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE + ? TRUE + : FALSE; + + // Go to the parent and get either the first or + // last child, depending on the axis. That is + // where we need to start from. + + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // A node without a parent should not have any siblings! + + rc = NE_XFLM_OK; + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + goto Exit; + } + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // Get the node's first or last child. + + if (!bAttr) + { + rc = (RCODE)(bPrevSibAxis + ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); + } + else + { + rc = (RCODE)(bPrevSibAxis + ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode)); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + + // If we landed on the context node, we are done - there will be + // no more siblings to get. + + if( RC_BAD( rc = ((F_DOMNode *)(*ppCurrNode))->getNodeId( m_pDb, + &ui64CurrNodeId, &uiCurrNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = ((F_DOMNode *)pContextNode)->getNodeId( m_pDb, + &ui64ContextNodeId, &uiContextAttrNameId))) + { + goto Exit; + } + + if( ui64CurrNodeId == ui64ContextNodeId && + uiCurrNameId == uiContextAttrNameId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a PRECEDING_AXIS or + FOLLOWING_AXIS. +***************************************************************************/ +RCODE F_Query::getPrevOrAfterAxisNode( + FLMBOOL bForward, + FLMBOOL bPrevAxis, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiChildCnt; + IF_DOMNode * pParentNode = NULL; + IF_DOMNode * pSibNode = NULL; + IF_DOMNode * pHighestAncestorWithSib = NULL; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64ContextId; + FLMUINT64 ui64Tmp; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, FALSE, 0, ppCurrNode))) + { + goto Exit; + } + if (!(*ppCurrNode)) + { + break; + } + + if (pParentNode) + { + pParentNode->Release(); + } + pParentNode = *ppCurrNode; + pParentNode->AddRef(); + + // This node must be previous to some other node if the + // axis is PRECEDING_AXIS (bPrevAxis == TRUE) or after some + // other node if the axis is FOLLOWING_AXIS + // (bPrevAxis == FALSE). This does not count descendants + // or ancestors or attribute nodes. + + for (;;) + { + rc = (RCODE)(bPrevAxis + ? pParentNode->getNextSibId( m_pDb, &ui64NodeId) + : pParentNode->getPrevSibId( m_pDb, &ui64NodeId)); + if (RC_BAD( rc)) + { + goto Exit; + } + if (ui64NodeId) + { + goto Exit; + } + if (RC_BAD( rc = pParentNode->getParentNode( m_pDb, + &pParentNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + } + else + { + if( RC_BAD( rc = pContextNode->getNodeId( m_pDb, &ui64ContextId))) + { + goto Exit; + } + + // Context node better not be an attribute node. That is illegal. + // To be nice here, we will change the context node to be the + // attribute's encompassing element node. + + if (pContextNode->getNodeType() == ATTRIBUTE_NODE) + { + if (RC_BAD( rc = pContextNode->getParentNode( m_pDb, &pContextNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + } + + if (bForward) + { + if (*ppCurrNode) + { + + // Go to child of current node + + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode)); + if (RC_OK( rc)) + { + rc = incrNodesRead(); + goto Exit; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + else + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + } + + for (;;) + { + + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode)); + if (RC_OK( rc)) + { + rc = incrNodesRead(); + goto Exit; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Go to the parent node + + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + rc = NE_XFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + else + { + + // Searching backwards + + if (!(*ppCurrNode)) + { + *ppCurrNode = pContextNode; + (*ppCurrNode)->AddRef(); + pHighestAncestorWithSib = NULL; + + // Find highest parent that has a sibling + + for (;;) + { + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getPreviousSibling( m_pDb, &pSibNode) + : (*ppCurrNode)->getNextSibling( m_pDb, &pSibNode)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + if (pHighestAncestorWithSib) + { + pHighestAncestorWithSib->Release(); + } + pHighestAncestorWithSib = *ppCurrNode; + pHighestAncestorWithSib->AddRef(); + } + + // Go to node's parent + + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + + // If none of the ancestry had a prev/next sibling + // we are done because there will be no nodes to + // process. + + if (!pHighestAncestorWithSib) + { + goto Exit; + } + + // Find the leftmost/rightmost sibling of the ancestor node, + // and go down to its leftmost/rightmost child. + // That is equivalent to going up to the parent node (which + // there must be one!) and then going down to the + // leftmost/rightmost child of that parent. + + *ppCurrNode = pHighestAncestorWithSib; + (*ppCurrNode)->AddRef(); + if (RC_OK( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + // Should not be possible, because we already know that + // pHighestAncestorWithSib has a sibling! - which + // necessitates it having a parent! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Now go down to the leftmost/rightmost child + + uiChildCnt = 0; + for (;;) + { + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + // If we didn't go down to any child, this is + // a problem, because we got to the parent node + // from a child node! + + if (!uiChildCnt) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else + { + rc = NE_XFLM_OK; + } + } + goto Exit; + } + else + { + uiChildCnt++; + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + } + + // The following code is only executed if *ppCurrNode is non-NULL + // when we enter this method. + + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); + if (RC_OK( rc)) + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // If we have hit the context node, we are done + + if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64ContextId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + goto Exit; + } + + // Go to rightmost/leftmost child - If we hit the + // context node anywhere in here, we are done. + + for (;;) + { + rc = (RCODE)(bPrevAxis + ? (*ppCurrNode)->getFirstChild( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastChild( m_pDb, ppCurrNode)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + + // See if we hit the context node. + + if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64ContextId) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + goto Exit; + } + } + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + if (RC_BAD( rc = (*ppCurrNode)->getParentNode( m_pDb, ppCurrNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + rc = NE_XFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // Should never hit the context node here + +#ifdef FLM_DEBUG + if( RC_BAD( rc = (*ppCurrNode)->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + flmAssert( ui64Tmp != ui64ContextId); +#endif + } + } + } + } + +Exit: + + if (pParentNode) + { + pParentNode->Release(); + } + if (pSibNode) + { + pSibNode->Release(); + } + if (pHighestAncestorWithSib) + { + pHighestAncestorWithSib->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from a ATTRIBUTE_AXIS or NAMESPACE_AXIS. +***************************************************************************/ +RCODE F_Query::getAttrAxisNode( + FLMBOOL bForward, + FLMBOOL bAttrAxis, + FLMUINT uiAttrNameId, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppCurrNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bIsNamespaceDecl; + + if (!pContextNode) + { + for (;;) + { + if (RC_BAD( rc = walkDocument( bForward, TRUE, uiAttrNameId, ppCurrNode))) + { + goto Exit; + } + if (!(*ppCurrNode)) + { + break; + } + + // Must be an attribute node. + + if ((*ppCurrNode)->getNodeType() != ATTRIBUTE_NODE) + { + continue; + } + if (bAttrAxis) + { + break; + } + + // Must be a namespace attribute. + + if (RC_BAD( rc = (*ppCurrNode)->isNamespaceDecl( m_pDb, + &bIsNamespaceDecl))) + { + goto Exit; + } + if (bIsNamespaceDecl) + { + break; + } + } + } + else + { + for (;;) + { + if( *ppCurrNode) + { + flmAssert( (*ppCurrNode)->getNodeType() == ATTRIBUTE_NODE); + + // Better not be testing for a specific name. This should + // not have been called if we already processed that + // attribute. + + flmAssert( !uiAttrNameId); + + // Get the next/previous sibling, if any + + rc = (RCODE)(bForward + ? (*ppCurrNode)->getNextSibling( m_pDb, ppCurrNode) + : (*ppCurrNode)->getPreviousSibling( m_pDb, ppCurrNode)); + + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + rc = NE_XFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + else + { + (*ppCurrNode) = pContextNode; + (*ppCurrNode)->AddRef(); + + // Context node better be an element node + + flmAssert( (*ppCurrNode)->getNodeType() == ELEMENT_NODE); + + // Get the first/last attribute of the node. If a specific + // attribute ID is specified, get it - no need to cycle through + // all of the attributes. + + if (uiAttrNameId) + { + rc = (*ppCurrNode)->getAttribute( m_pDb, uiAttrNameId, ppCurrNode); + } + else + { + rc = (RCODE)(bForward + ? (*ppCurrNode)->getFirstAttribute( m_pDb, ppCurrNode) + : (*ppCurrNode)->getLastAttribute( m_pDb, ppCurrNode)); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + rc = NE_XFLM_OK; + } + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + } + + if (bAttrAxis) + { + break; + } + + // Must be a namespace attribute. + + if (RC_BAD( rc = (*ppCurrNode)->isNamespaceDecl( m_pDb, + &bIsNamespaceDecl))) + { + goto Exit; + } + if (bIsNamespaceDecl) + { + break; + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Determine the inverted axis for a given axis type. +***************************************************************************/ +FINLINE eXPathAxisTypes invertedAxis( + eXPathAxisTypes eAxis) +{ + eXPathAxisTypes eInvertedAxis = ROOT_AXIS; + + switch (eAxis) + { + case ROOT_AXIS: + + // A root axis cannot be inverted. + + flmAssert( 0); + eInvertedAxis = ROOT_AXIS; + break; + case CHILD_AXIS: + case ATTRIBUTE_AXIS: + case NAMESPACE_AXIS: + eInvertedAxis = PARENT_AXIS; + break; + case PARENT_AXIS: + eInvertedAxis = CHILD_AXIS; + break; + case ANCESTOR_AXIS: + eInvertedAxis = DESCENDANT_AXIS; + break; + case DESCENDANT_AXIS: + eInvertedAxis = ANCESTOR_AXIS; + break; + case FOLLOWING_SIBLING_AXIS: + eInvertedAxis = PRECEDING_SIBLING_AXIS; + break; + case PRECEDING_SIBLING_AXIS: + eInvertedAxis = FOLLOWING_SIBLING_AXIS; + break; + case FOLLOWING_AXIS: + eInvertedAxis = PRECEDING_AXIS; + break; + case PRECEDING_AXIS: + eInvertedAxis = FOLLOWING_AXIS; + break; + case SELF_AXIS: + eInvertedAxis = SELF_AXIS; + break; + case DESCENDANT_OR_SELF_AXIS: + eInvertedAxis = ANCESTOR_OR_SELF_AXIS; + break; + case ANCESTOR_OR_SELF_AXIS: + eInvertedAxis = DESCENDANT_OR_SELF_AXIS; + break; + case META_AXIS: + eInvertedAxis = SELF_AXIS; + break; + } + return( eInvertedAxis); +} + +/*************************************************************************** +Desc: See if the passed in node ID satisfies the occurrence criteria + for the xpath component. +***************************************************************************/ +RCODE F_Query::verifyOccurrence( + FLMBOOL bUseKeyNodes, + XPATH_COMPONENT * pXPathComponent, + IF_DOMNode * pCurrNode, + FLMBOOL * pbPassed) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pContextNode = NULL; + IF_DOMNode * pTmpCurrNode = NULL; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64Tmp; + FLMBOOL bGetContextNodes = FALSE; + XPATH_COMPONENT * pXPathContextComponent = pXPathComponent->pPrev + ? pXPathComponent->pPrev + : pXPathComponent->pXPathContext; + + if( RC_BAD( rc = pCurrNode->getNodeId( m_pDb, &ui64NodeId))) + { + goto Exit; + } + + if (!pXPathContextComponent) + { + pContextNode = NULL; + } + else if (bUseKeyNodes && pXPathContextComponent->pKeyNode) + { + pContextNode = pXPathContextComponent->pKeyNode; + pContextNode->AddRef(); + } + else if (pXPathContextComponent->pCurrNode) + { + pContextNode = pXPathContextComponent->pCurrNode; + pContextNode->AddRef(); + } + else + { + + // Need to get context nodes. May have to try multiple ones. + + bGetContextNodes = TRUE; + if (RC_BAD( rc = getXPathComponentFromAxis( pCurrNode, TRUE, FALSE, + pXPathContextComponent, &pContextNode, + invertedAxis( pXPathComponent->eXPathAxis), + TRUE, FALSE))) + { + goto Exit; + } + if (!pContextNode) + { + *pbPassed = FALSE; + goto Exit; + } + } + + // *pbPassed better have been passed in as TRUE + // It will get set to FALSE if we don't find the node + // we are looking for. + + flmAssert( *pbPassed); + + for (;;) + { + if (RC_BAD( rc = getXPathComponentFromAxis( pContextNode, TRUE, FALSE, + pXPathComponent, &pTmpCurrNode, + pXPathComponent->eXPathAxis, + FALSE, FALSE))) + { + goto Exit; + } + if (!pTmpCurrNode) + { + +No_More_Nodes: + + if (!bGetContextNodes) + { + *pbPassed = FALSE; + goto Exit; + } + + // If we didn't have a context node to begin with, + // attempt another one. + + if (RC_BAD( rc = getXPathComponentFromAxis( pCurrNode, TRUE, FALSE, + pXPathContextComponent, + &pContextNode, + invertedAxis( pXPathComponent->eXPathAxis), + TRUE, FALSE))) + { + goto Exit; + } + + // No more context nodes to try. We are done. + + if (!pContextNode) + { + *pbPassed = FALSE; + goto Exit; + } + continue; + } + + if( RC_BAD( rc = pTmpCurrNode->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + if( ui64Tmp == ui64NodeId) + { + break; + } + + // If we are dealing with a constant position, and we didn't + // find the node we were looking for, we are done. The + // above call would have found it. If we are dealing + // with an expression, there is no guarantee that the above + // code would have found just one occurrence of a matching + // node, and it may not have been the one we were looking + // for. + + if (pXPathComponent->uiContextPosNeeded) + { + goto No_More_Nodes; + } + } + +Exit: + + if (pTmpCurrNode) + { + pTmpCurrNode->Release(); + } + if (pContextNode) + { + pContextNode->Release(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the node for an xpath component that is related to the + given xpath context component by the specified axis. +***************************************************************************/ +RCODE F_Query::getXPathComponentFromAxis( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + XPATH_COMPONENT * pXPathComponent, + IF_DOMNode ** ppCurrNode, + eXPathAxisTypes eAxis, + FLMBOOL bAxisInverted, + FLMBOOL bCountNodes) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pCurrNode = NULL; + FLMUINT uiOccurrence = 0; + FLMBOOL bCanCountOccurrence; + FLMBOOL bMatches; + FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); + FLMUINT uiNameId; + + // Remember the current node, and then set to NULL. + + if (*ppCurrNode) + { + pCurrNode = *ppCurrNode; + pCurrNode->AddRef(); + (*ppCurrNode)->Release(); + *ppCurrNode = NULL; + } + + // Determine if we can count occurrences. + + if (bForward && !bAxisInverted) + { + uiOccurrence = 0; + bCanCountOccurrence = TRUE; + } + else + { + + // Cannot keep a running count if we are going backwards or if + // we are operating on an inverted axis. + + bCanCountOccurrence = FALSE; + } + + // Go until we get a node that passes, or until we have no more + // nodes to check. + + for (;;) + { + if (pXPathComponent->pNodeSource) + { + + // Axis is ignored for xpath components that have a node source. + + if (RC_BAD( rc = getNodeSourceNode( bForward, + pXPathComponent->pNodeSource, + pContextNode, &pCurrNode))) + { + goto Exit; + } + goto Test_Node; + } + + // Type of search we do will depend on axis + + switch (eAxis) + { + case ROOT_AXIS: + + // There is only one root node to get, so if we already + // got it, we are done. + + if (pCurrNode) + { + goto Exit; + } + flmAssert( pXPathComponent->eNodeType == ELEMENT_NODE); + if (RC_BAD( rc = getRootAxisNode( &pCurrNode))) + { + goto Exit; + } + + // Will only ever be one occurrence of a root node. + + if (!bAxisInverted) + { + uiOccurrence = 0; + bCanCountOccurrence = TRUE; + } + break; + + case CHILD_AXIS: + + // For a context that requires all child elements to be unique, + // there will only be one child with that name ID. + // If we already got it, we are done. + + if (pContextNode && + (((F_DOMNode *)pContextNode)->getModeFlags() & + FDOM_HAVE_CELM_LIST) && + pXPathComponent->uiDictNum) + { + if( pCurrNode) + { + if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) + { + goto Exit; + } + + if( uiNameId == pXPathComponent->uiDictNum) + { + goto Exit; + } + } + + if (RC_BAD( rc = getChildAxisNode( bForward, pContextNode, + pXPathComponent->uiDictNum, + &pCurrNode))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = getChildAxisNode( bForward, pContextNode, 0, + &pCurrNode))) + { + goto Exit; + } + } + break; + + case PARENT_AXIS: + + // There is only one parent node to get if we are in a + // specific context, so if we already got it, we are + // done. + + if (pCurrNode && pContextNode) + { + goto Exit; + } + if (RC_BAD( rc = getParentAxisNode( bForward, pContextNode, + &pCurrNode))) + { + goto Exit; + } + + // Will only ever be one occurrence of a parent node. + + if (!bAxisInverted) + { + uiOccurrence = 0; + bCanCountOccurrence = TRUE; + } + break; + + case ANCESTOR_AXIS: + if (RC_BAD( rc = getAncestorAxisNode( bForward, FALSE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case DESCENDANT_AXIS: + if (RC_BAD( rc = getDescendantAxisNode( bForward, FALSE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case FOLLOWING_SIBLING_AXIS: + if (RC_BAD( rc = getSibAxisNode( bForward, FALSE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case PRECEDING_SIBLING_AXIS: + if (RC_BAD( rc = getSibAxisNode( bForward, TRUE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case FOLLOWING_AXIS: + if (RC_BAD( rc = getPrevOrAfterAxisNode( bForward, FALSE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case PRECEDING_AXIS: + if (RC_BAD( rc = getPrevOrAfterAxisNode( bForward, TRUE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case ATTRIBUTE_AXIS: + + // For a specific context, there is only one attribute with + // a given name ID. If we already got it, we are done. + + if( pCurrNode && pContextNode && pXPathComponent->uiDictNum) + { + if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) + { + goto Exit; + } + + if( uiNameId == pXPathComponent->uiDictNum) + { + goto Exit; + } + } + + if (RC_BAD( rc = getAttrAxisNode( bForward, TRUE, + pXPathComponent->uiDictNum, + pContextNode, + &pCurrNode))) + { + goto Exit; + } + break; + + case NAMESPACE_AXIS: + if (RC_BAD( rc = getAttrAxisNode( bForward, FALSE, 0, pContextNode, + &pCurrNode))) + { + goto Exit; + } + break; + + case SELF_AXIS: + case META_AXIS: + + // Within a given context, there is only one occurrence of the + // META or SELF axis. If we already got it, we are done. + + if (pCurrNode && pContextNode) + { + goto Exit; + } + if (!pContextNode) + { + + // If the node is the SELF_AXIS, there better be + // a context node! + + flmAssert( eAxis != SELF_AXIS); + if (RC_BAD( rc = walkDocument( bForward, TRUE, 0, &pCurrNode))) + { + goto Exit; + } + } + else + { + pCurrNode = pContextNode; + pCurrNode->AddRef(); + } + + // Will only ever be one occurrence of a meta-axis node or + // a self-axis node. + + if (!bAxisInverted) + { + uiOccurrence = 0; + bCanCountOccurrence = TRUE; + } + break; + + case DESCENDANT_OR_SELF_AXIS: + if (RC_BAD( rc = getDescendantAxisNode( bForward, TRUE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + + case ANCESTOR_OR_SELF_AXIS: + if (RC_BAD( rc = getAncestorAxisNode( bForward, TRUE, + pContextNode, &pCurrNode))) + { + goto Exit; + } + break; + } + +Test_Node: + + if (!pCurrNode) + { + break; + } + + if (bCountNodes) + { + m_pCurrOpt->ui64NodesTested++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + + bMatches = TRUE; + if (pXPathComponent->eNodeType != ANY_NODE_TYPE && eAxis != META_AXIS) + { + if (pCurrNode->getNodeType() != pXPathComponent->eNodeType) + { + bMatches = FALSE; + } + + // Verify that the dictionary number matches. + + else if (pXPathComponent->eNodeType == ELEMENT_NODE || + pXPathComponent->eNodeType == ATTRIBUTE_NODE || + pXPathComponent->eNodeType == DATA_NODE) + { + if (pXPathComponent->uiDictNum) + { + if( RC_BAD( rc = pCurrNode->getNameId( m_pDb, &uiNameId))) + { + goto Exit; + } + + if( uiNameId != pXPathComponent->uiDictNum) + { + bMatches = FALSE; + } + } + } + } + + // If there is an expression evaluate it. + // The expression must pass in order to + // keep this particular node. + + if (bMatches && pXPathComponent->pExpr) + { + if (RC_BAD( rc = evalExpr( pCurrNode, TRUE, bUseKeyNodes, + pXPathComponent->pExpr, &bMatches, NULL))) + { + goto Exit; + } + } + + // See if there is an occurrence test. + + if (bMatches && bHasContextPosTest) + { + if (bCanCountOccurrence) + { + uiOccurrence++; + if (pXPathComponent->uiContextPosNeeded) + { + if (uiOccurrence != pXPathComponent->uiContextPosNeeded) + { + bMatches = FALSE; + } + } + else + { + FLMUINT uiPosNeeded; + + if (RC_BAD( rc = evalExpr( pCurrNode, TRUE, bUseKeyNodes, + pXPathComponent->pContextPosExpr, + NULL, NULL))) + { + goto Exit; + } + if (RC_BAD( fqGetPosition( &pXPathComponent->pContextPosExpr->currVal, + &uiPosNeeded)) || + uiOccurrence != uiPosNeeded) + { + bMatches = FALSE; + } + } + } + else + { + + // Need to verify the context position because we couldn't + // calculate it as we went. + + if (RC_BAD( rc = verifyOccurrence( bUseKeyNodes, pXPathComponent, + pCurrNode, &bMatches))) + { + goto Exit; + } + } + } + + if (bMatches) + { + *ppCurrNode = pCurrNode; + + // No need to AddRef on *ppCurrNode, just steal it from + // pCurrNode. Setting pCurrNode to NULL keeps it from + // being Release()'d below. + + pCurrNode = NULL; + break; + } + } + +Exit: + + if (pCurrNode) + { + pCurrNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get an FQVALUE from a DOM node. +***************************************************************************/ +FSTATIC RCODE fqGetValueFromNode( + F_Db * pDb, + IF_DOMNode * pNode, + FQVALUE * pQValue, + FLMUINT uiMetaDataType + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataType; + FLMUINT uiNumChars; + + pQValue->uiFlags = 0; + if (uiMetaDataType) + { + switch (uiMetaDataType) + { + case XFLM_META_NODE_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getNodeId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_DOCUMENT_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getDocumentId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_PARENT_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getParentId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_FIRST_CHILD_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getFirstChildId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_LAST_CHILD_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getLastChildId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_NEXT_SIBLING_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getNextSibId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_PREV_SIBLING_ID: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getPrevSibId( pDb, &pQValue->val.ui64Val); + goto Exit; + case XFLM_META_VALUE: + pQValue->eValType = XFLM_UINT64_VAL; + rc = pNode->getMetaValue( pDb, &pQValue->val.ui64Val); + goto Exit; + default: + break; + } + } + + if (RC_BAD( rc = pNode->getDataType( pDb, &uiDataType))) + { + goto Exit; + } + switch (uiDataType) + { + case XFLM_NODATA_TYPE: + + // Should have been set to missing on the outside + + flmAssert( pQValue->eValType == XFLM_MISSING_VAL); + pQValue->eValType = XFLM_BOOL_VAL; + pQValue->val.eBool = XFLM_TRUE; + break; + case XFLM_TEXT_TYPE: + if (RC_BAD( rc = pNode->getTextIStream( pDb, + &pQValue->val.pIStream, + &uiNumChars))) + { + goto Exit; + } + pQValue->eValType = XFLM_UTF8_VAL; + pQValue->uiFlags |= VAL_IS_STREAM; + break; + case XFLM_NUMBER_TYPE: + + // First, see if we can get it as a UINT - the most common + // type. + + if (RC_OK( rc = pNode->getUINT( pDb, &pQValue->val.uiVal))) + { + pQValue->eValType = XFLM_UINT_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_OVERFLOW) + { + if (RC_BAD( rc = pNode->getUINT64( pDb, + &pQValue->val.ui64Val))) + { + goto Exit; + } + pQValue->eValType = XFLM_UINT64_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) + { + if (RC_OK( rc = pNode->getINT( pDb, &pQValue->val.iVal))) + { + pQValue->eValType = XFLM_INT_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) + { + if (RC_BAD( rc = pNode->getINT64( pDb, + &pQValue->val.i64Val))) + { + goto Exit; + } + pQValue->eValType = XFLM_INT64_VAL; + } + } + else + { + goto Exit; + } + break; + case XFLM_BINARY_TYPE: + if (RC_BAD( rc = pNode->getIStream( pDb, &pQValue->val.pIStream, + &uiDataType))) + { + goto Exit; + } + flmAssert( uiDataType == XFLM_BINARY_TYPE); + pQValue->eValType = XFLM_BINARY_VAL; + pQValue->uiFlags |= VAL_IS_STREAM; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next value from an XPATH node. +***************************************************************************/ +RCODE F_Query::getNextXPathValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + FLMBOOL bXPathIsEntireExpr, + FQNODE * pQNode + ) +{ + RCODE rc = NE_XFLM_OK; + FXPATH * pXPath; + XPATH_COMPONENT * pXPathComponent; + + // Node better be an XPATH node. + + flmAssert( pQNode->eNodeType == FLM_XPATH_NODE); + pXPath = pQNode->nd.pXPath; + + // If the old value type was a stream, get rid of it. + + fqReleaseNodeValue( pQNode); + if (pXPath->bGettingNodes) + { + pXPathComponent = pXPath->pLastComponent; + } + else if (pXPath->bIsSource && bUseKeyNodes) + { + pXPathComponent = pXPath->pSourceComponent->pNext; + + // This routine should never be called if the source component + // is the rightmost component. That is taken care of on + // the outside. + + flmAssert( pXPathComponent); + } + else + { + pXPathComponent = pXPath->pFirstComponent; + } + + // This loop will go until we get to the last xpath component and get + // a node. If it cannot get a node at one context, it will retreat + // to the prior context, until it gets back to the first xpath + // component, and there are no more to get + + for (;;) + { + IF_DOMNode * pTmpContextNode; + + if (!pXPathComponent->pPrev) + { + pTmpContextNode = pContextNode; + } + else if (bUseKeyNodes && pXPathComponent->pPrev->pKeyNode) + { + pTmpContextNode = pXPathComponent->pPrev->pKeyNode; + } + else + { + pTmpContextNode = pXPathComponent->pPrev->pCurrNode; + } + if (RC_BAD( rc = getXPathComponentFromAxis( pTmpContextNode, + bForward, bUseKeyNodes, pXPathComponent, + &pXPathComponent->pCurrNode, + pXPathComponent->eXPathAxis, FALSE, + bXPathIsEntireExpr && !pXPathComponent->pNext + ? TRUE + : FALSE))) + { + goto Exit; + } + + // If we didn't get any nodes that passed here, we have nothing + // to give back. + + if (pXPathComponent->pCurrNode) + { + if ((pXPathComponent = pXPathComponent->pNext) == NULL) + { + break; + } + } + else if (pXPathComponent->pPrev && + (!pXPathComponent->pPrev->bIsSource || !bUseKeyNodes)) + { + + // Was no node, go to prior context to get its next node. + + pXPathComponent = pXPathComponent->pPrev; + } + else + { + + // There was no prior context, so we do not have a value + // to return for this xpath. Note that + // pQNode->currVal.eValType should already have been set to + // XFLM_MISSING_VAL up above, so no need to set it here. + + flmAssert( pQNode->currVal.eValType == XFLM_MISSING_VAL); + fqResetIterator( pQNode, FALSE, bUseKeyNodes); + goto Exit; + } + } + + // Extract the value from the DOM node pointed to by the + // last component in the xpath. + + pXPath->bGettingNodes = TRUE; + + if (pQNode->pParent && isLogicalOp( pQNode->pParent->nd.op.eOperator)) + { + pQNode->currVal.eValType = XFLM_BOOL_VAL; + pQNode->currVal.val.eBool = (pQNode->bNotted ? XFLM_FALSE : XFLM_TRUE); + } + else + { + if (RC_BAD( rc = fqGetValueFromNode( m_pDb, + pXPath->pLastComponent->pCurrNode, + &pQNode->currVal, + getMetaDataType( pXPath->pLastComponent)))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Test a value and return a TRUE/FALSE boolean. +***************************************************************************/ +FSTATIC FLMBOOL fqTestValue( + FQNODE * pQueryExpr + ) +{ + FLMBOOL bPassed; + + switch (pQueryExpr->currVal.eValType) + { + case XFLM_BOOL_VAL: + bPassed = (pQueryExpr->currVal.val.eBool == XFLM_TRUE) ? TRUE : FALSE; + break; + case XFLM_UINT_VAL: + bPassed = (pQueryExpr->currVal.val.uiVal) ? TRUE : FALSE; + break; + case XFLM_UINT64_VAL: + bPassed = (pQueryExpr->currVal.val.ui64Val) ? TRUE : FALSE; + break; + case XFLM_INT_VAL: + bPassed = (pQueryExpr->currVal.val.iVal) ? TRUE : FALSE; + break; + case XFLM_INT64_VAL: + bPassed = (pQueryExpr->currVal.val.i64Val) ? TRUE : FALSE; + break; + case XFLM_BINARY_VAL: + case XFLM_UTF8_VAL: + bPassed = (pQueryExpr->currVal.uiDataLen) ? TRUE : FALSE; + break; + default: + bPassed = FALSE; + break; + } + return( bPassed); +} + +/*************************************************************************** +Desc: Get the next value for a function. +***************************************************************************/ +RCODE F_Query::getNextFunctionValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE * pCurrNode, + F_DynaBuf * pDynaBuf + ) +{ + RCODE rc = NE_XFLM_OK; + ValIterator eIterator; + FLMBYTE ucValBuf [sizeof( FLMUINT64)]; + FLMBYTE * pucVal; + IF_DOMNode * pNode = NULL; + FLMBOOL bPassed; + + // Evaluate the parameter expression, if any. + + if (pCurrNode->nd.pQFunction->pFirstArg) + { + if (RC_BAD( rc = evalExpr( pContextNode, TRUE, FALSE, + pCurrNode->nd.pQFunction->pFirstArg->pExpr, + &bPassed, &pNode))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) + { + flmAssert( pNode == NULL); + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + if (bForward) + { + eIterator = (!pCurrNode->bUsedValue) ? GET_FIRST_VAL : GET_NEXT_VAL; + } + else + { + eIterator = (!pCurrNode->bUsedValue) ? GET_LAST_VAL : GET_PREV_VAL; + } + + fqReleaseNodeValue( pCurrNode); + pCurrNode->currVal.uiDataLen = 0; + pCurrNode->currVal.uiFlags = 0; + pDynaBuf->truncateData( 0); + if (RC_BAD( rc = pCurrNode->nd.pQFunction->pFuncObj->getValue( (IF_Db *)m_pDb, + pNode, eIterator, &pCurrNode->currVal.eValType, + &pCurrNode->bLastValue, &ucValBuf, (IF_DynaBuf *)pDynaBuf))) + { + goto Exit; + } + + pucVal = &ucValBuf [0]; + switch (pCurrNode->currVal.eValType) + { + case XFLM_BOOL_VAL: + pCurrNode->currVal.val.eBool = *((XFlmBoolType *)pucVal); + break; + case XFLM_UINT_VAL: + pCurrNode->currVal.val.uiVal = *((FLMUINT *)pucVal); + break; + case XFLM_UINT64_VAL: + pCurrNode->currVal.val.ui64Val = *((FLMUINT64 *)pucVal); + break; + case XFLM_INT_VAL: + pCurrNode->currVal.val.iVal = *((FLMINT *)pucVal); + break; + case XFLM_INT64_VAL: + pCurrNode->currVal.val.i64Val = *((FLMINT64 *)pucVal); + break; + case XFLM_BINARY_VAL: + case XFLM_UTF8_VAL: + pCurrNode->currVal.uiDataLen = pDynaBuf->getDataLength(); + pCurrNode->currVal.val.pucBuf = (FLMBYTE *)pDynaBuf->getBufferPtr(); + break; + case XFLM_MISSING_VAL: + default: + break; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Reset the entire query tree. It could have been left in a partial + evaluation state from the last document evaluated. +***************************************************************************/ +FSTATIC void fqResetQueryTree( + FQNODE * pQueryTree, + FLMBOOL bUseKeyNodes, + FLMBOOL bResetAllXPaths) +{ + FQNODE * pCurrNode = pQueryTree; + + for (;;) + { + fqReleaseNodeValue( pCurrNode); + pCurrNode->bUsedValue = FALSE; + pCurrNode->bLastValue = FALSE; + if (pCurrNode->pFirstChild) + { + pCurrNode = pCurrNode->pFirstChild; + } + else + { + if (pCurrNode->eNodeType == FLM_XPATH_NODE) + { + + // Don't reset the iterator if this xpath node is + // the root node of the outermost query. + + if (bResetAllXPaths || + pCurrNode->pParent || + pCurrNode->nd.pXPath->pFirstComponent->pXPathContext) + { + fqResetIterator( pCurrNode, FALSE, bUseKeyNodes); + } + } + while (!pCurrNode->pNextSib) + { + if ((pCurrNode = pCurrNode->pParent) == NULL) + { + break; + } + } + if (!pCurrNode) + { + break; + } + pCurrNode = pCurrNode->pNextSib; + } + } +} + +/*************************************************************************** +Desc: Evaluate an operand in a query expression. If it is an operand of a + boolean operator (AND/OR), we may be able to short-circuit the + evaluation of the other operand. This will be propagated up the + query tree as far as possible. + If it is an operand of some other operator, we check to see if we + have both operands. If not, we move *ppCurrNode to get the next + sibling so we can evalute the other operand. + VISIT: We could short-circuit arithmetic operations if an operand + is missing. +***************************************************************************/ +FSTATIC RCODE fqTryEvalOperator( + FLMUINT uiLanguage, + FQNODE ** ppCurrNode) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pCurrNode = *ppCurrNode; + XFlmBoolType eBoolVal; + XFlmBoolType eBoolPartialEval; + + for (;;) + { + if (!pCurrNode->pParent) + { + pCurrNode = NULL; + break; + } + + // If the current node's parent is an AND or OR + // operator, see if we even need to go to the next + // sibling. + + flmAssert( pCurrNode->pParent->eNodeType == FLM_OPERATOR_NODE); + if (pCurrNode->pParent->nd.op.eOperator == XFLM_AND_OP || + pCurrNode->pParent->nd.op.eOperator == XFLM_OR_OP) + { + eBoolVal = XFLM_UNKNOWN; + eBoolPartialEval = pCurrNode->pParent->nd.op.eOperator == XFLM_AND_OP + ? XFLM_FALSE + : XFLM_TRUE; + if (pCurrNode->eNodeType == FLM_OPERATOR_NODE) + { + + // It may not have been evaluated because of missing + // XPATH values in one or both operands, in which case + // its state will be XFLM_MISSING_VALUE. If it was + // evaluated, its state should show a boolean value. + + if (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) + { + eBoolVal = (pCurrNode->bNotted ? XFLM_TRUE : XFLM_FALSE); + } + else + { + flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL); + eBoolVal = pCurrNode->currVal.val.eBool; + } + } + else if (pCurrNode->eNodeType == FLM_XPATH_NODE) + { + if (!pCurrNode->bNotted) + { + eBoolVal = (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) + ? XFLM_FALSE + : XFLM_TRUE; + } + else + { + eBoolVal = (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) + ? XFLM_TRUE + : XFLM_FALSE; + } + } + else if (pCurrNode->eNodeType == FLM_VALUE_NODE) + { + + // Only allowed value node underneath a logical operator is + // a boolean value that has a value of XFLM_UNKNOWN. + // XFLM_FALSE and XFLM_TRUE will already have been weeded out. + + flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL && + pCurrNode->currVal.val.eBool == XFLM_UNKNOWN); + + // No need to set eBoolVal to XFLM_UNKNOWN, because it will never + // match eBoolPartialEval in the test below. eBoolPartialEval + // is always either XFLM_FALSE or XFLM_TRUE. + + // eBoolVal = XFLM_UNKNOWN; + + } + else + { + flmAssert( pCurrNode->eNodeType == FLM_FUNCTION_NODE); + if (!pCurrNode->bNotted) + { + eBoolVal = fqTestValue( pCurrNode) ? XFLM_TRUE : XFLM_FALSE; + } + else + { + eBoolVal = fqTestValue( pCurrNode) ? XFLM_FALSE : XFLM_TRUE; + } + } + if (eBoolVal == eBoolPartialEval) + { + pCurrNode = pCurrNode->pParent; + pCurrNode->currVal.eValType = XFLM_BOOL_VAL; + pCurrNode->currVal.val.eBool = eBoolVal; + } + else if (pCurrNode->pNextSib) + { + pCurrNode = pCurrNode->pNextSib; + break; + } + else + { + pCurrNode = pCurrNode->pParent; + if (RC_BAD( rc = fqEvalOperator( uiLanguage, pCurrNode))) + { + goto Exit; + } + } + } + else + { + + // Visit: can shortcircuit comparison operators if the + // value is missing. + + if (pCurrNode->pNextSib) + { + pCurrNode = pCurrNode->pNextSib; + break; + } + pCurrNode = pCurrNode->pParent; + + // All operands are now present - do evaluation + + if (RC_BAD( rc = fqEvalOperator( uiLanguage, pCurrNode))) + { + goto Exit; + } + + // If this is a comparison operator, see if we need to + // do existential or universal comparison. + + if (isCompareOp( pCurrNode->nd.op.eOperator)) + { + + // Evaluation should have forced current value to be a + // boolean TRUE or FALSE value. + + flmAssert( pCurrNode->currVal.eValType == XFLM_BOOL_VAL); + if ((isUniversal( pCurrNode) && + pCurrNode->currVal.val.eBool == XFLM_TRUE) || + (isExistential( pCurrNode) && + pCurrNode->currVal.val.eBool == XFLM_FALSE)) + { + + // Go back down right hand side to get next rightmost + // operand + + while (pCurrNode->pLastChild) + { + pCurrNode = pCurrNode->pLastChild; + } + break; + } + } + } + } + +Exit: + + *ppCurrNode = pCurrNode; + return( rc); +} + +/*************************************************************************** +Desc: Backup the query tree +***************************************************************************/ +FSTATIC FQNODE * fqBackupTree( + FQNODE * pCurrNode, + FLMBOOL * pbGetNodeValue + ) +{ + flmAssert( !(*pbGetNodeValue)); + + // This was the last value, backup + + pCurrNode->bUsedValue = FALSE; + pCurrNode->bLastValue = FALSE; + + // If parent node is a logical operator, there is + // nothing more we can do on this branch, so we fall + // through and process the thing. + + if (pCurrNode->pParent && + !isLogicalOp( pCurrNode->pParent->nd.op.eOperator)) + { + while (!pCurrNode->pPrevSib) + { + if ((pCurrNode = pCurrNode->pParent) == NULL) + { + goto Exit; + } + + // Don't backup any higher than comparison operators + // We are only backing up so we can do existential + // and universal on them with multiple left and right + // operands. + + flmAssert( pCurrNode->eNodeType == FLM_OPERATOR_NODE); + if (isCompareOp( pCurrNode->nd.op.eOperator)) + { + goto Exit; + } + } + + // If we came up to a parent that is a comparison operator + // we are done - there is noplace else to backup to, so + // we will fall through. + + if (!isCompareOp( pCurrNode->nd.op.eOperator)) + { + + // has to be a prev sib at this point. + + pCurrNode = pCurrNode->pPrevSib; + + // Go down to rightmost child + + while (pCurrNode->pLastChild) + { + pCurrNode = pCurrNode->pLastChild; + } + *pbGetNodeValue = TRUE; + } + } + +Exit: + + return( pCurrNode); +} + +/*************************************************************************** +Desc: Get the value of a function node, or move to another node. +***************************************************************************/ +RCODE F_Query::getFuncValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE ** ppCurrNode, + FLMBOOL * pbGetNodeValue, + F_DynaBuf * pDynaBuf + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pCurrNode = *ppCurrNode; + + // We currently only support user-defined functions. + + flmAssert( pCurrNode->nd.pQFunction->pFuncObj); + flmAssert( !(*pbGetNodeValue)); + + if (pCurrNode->bLastValue) + { + pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); + } + else + { + if (RC_BAD( rc = getNextFunctionValue( pContextNode, + bForward, pCurrNode, + pDynaBuf))) + { + goto Exit; + } + + // Handle case where the function call is the only thing in the + // query. + + if (!pCurrNode->pParent) + { + FLMBOOL bTmpPassed; + FLMBOOL bFinal; + + if (pCurrNode->currVal.eValType == XFLM_MISSING_VAL) + { + bFinal = TRUE; + + // No more values. If it is universal, and we got to this + // point, it means that all prior values passed, so we set + // bTmpPassed to TRUE in that case. If it is existential and + // we got to this point, it means that all prior values + // failed, so we set bTmpPassed to FALSE in that case. + + bTmpPassed = isUniversal( pCurrNode); + } + else + { + bTmpPassed = fqTestValue( pCurrNode); + bFinal = (pCurrNode->bLastValue || + (isUniversal( pCurrNode) && !bTmpPassed) || + (isExistential( pCurrNode) && bTmpPassed)) + ? TRUE + : FALSE; + } + fqReleaseNodeValue( pCurrNode); + if (!bFinal) + { + *pbGetNodeValue = TRUE; + goto Exit; + } + pCurrNode->currVal.eValType = XFLM_BOOL_VAL; + pCurrNode->currVal.val.eBool = bTmpPassed ? XFLM_TRUE : XFLM_FALSE; + + // Must set to NULL so that evalExpr will break out of loop. + + pCurrNode = NULL; + goto Exit; + } + + // Handle case where it is not the top node of the query. + + if (pCurrNode->currVal.eValType != XFLM_MISSING_VAL) + { + pCurrNode->bUsedValue = TRUE; + } + else + { + if (!pCurrNode->bUsedValue) + { + + // Need to handle missing value if we have not done so yet. + + pCurrNode->bUsedValue = TRUE; + pCurrNode->bLastValue = TRUE; + } + else + { + // This was the last value, backup + + pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); + } + } + } + +Exit: + + *ppCurrNode = pCurrNode; + return( rc); +} + +/*************************************************************************** +Desc: Get the value of an XPATH node or move to another node. +***************************************************************************/ +RCODE F_Query::getXPathValue( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FQNODE ** ppCurrNode, + FLMBOOL * pbGetNodeValue, + FLMBOOL bUseKeyNodes, + FLMBOOL bXPathIsEntireExpr + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pCurrNode = *ppCurrNode; + + flmAssert( !(*pbGetNodeValue)); + flmAssert( !(*pbGetNodeValue)); + + // See if value is already set from an index. + + if (pCurrNode->nd.pXPath->bHavePassingNode && bUseKeyNodes) + { + if (pCurrNode->bUsedValue) + { + pCurrNode->currVal.eValType = XFLM_MISSING_VAL; + } + else + { + pCurrNode->currVal.eValType = XFLM_PASSING_VAL; + if (bXPathIsEntireExpr) + { + m_pCurrOpt->ui64NodesTested++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + } + } + else if (bUseKeyNodes && pCurrNode->nd.pXPath->bIsSource && + pCurrNode->nd.pXPath->pLastComponent->bIsSource) + { + fqReleaseNodeValue( pCurrNode); + if (!pCurrNode->bUsedValue) + { + XPATH_COMPONENT * pLastComponent = + pCurrNode->nd.pXPath->pLastComponent; + + if (RC_BAD( rc = fqGetValueFromNode( m_pDb, + pLastComponent->pKeyNode, + &pCurrNode->currVal, + getMetaDataType( pLastComponent)))) + { + goto Exit; + } + pCurrNode->bUsedValue = TRUE; + if (bXPathIsEntireExpr) + { + m_pCurrOpt->ui64NodesTested++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + } + } + else + { + if (RC_BAD( rc = getNextXPathValue( pContextNode, + bForward, bUseKeyNodes, + bXPathIsEntireExpr, pCurrNode))) + { + goto Exit; + } + } + + // If this expression is an XPATH, set pCurrNode to NULL so that + // it will cause evalExpr to exit from its loop and return + // the node we have found. No need to attempt to evaluate + // an operators, there are none. + + if (!pCurrNode->pParent) + { + pCurrNode = NULL; + goto Exit; + } + + if (pCurrNode->currVal.eValType != XFLM_MISSING_VAL) + { + pCurrNode->bUsedValue = TRUE; + } + else + { + fqResetIterator( pCurrNode, FALSE, bUseKeyNodes); + + // See if we used the "missing" value in evaluating + // the operator. Need to if we have not yet. + // Otherwise, we backup in the tree because we will + // have used at least one value. + + if (!pCurrNode->bUsedValue) + { + pCurrNode->bUsedValue = TRUE; + } + else + { + pCurrNode = fqBackupTree( pCurrNode, pbGetNodeValue); + } + } + +Exit: + + *ppCurrNode = pCurrNode; + return( rc); +} + +/*************************************************************************** +Desc: Set the return value for an expression. +***************************************************************************/ +RCODE F_Query::setExprReturnValue( + FLMBOOL bUseKeyNodes, + FQNODE * pQueryExpr, + FLMBOOL * pbPassed, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + + if (pQueryExpr->eNodeType == FLM_XPATH_NODE) + { + if (pQueryExpr->currVal.eValType != XFLM_MISSING_VAL) + { + + // Need to clear the current node's value. + + fqReleaseNodeValue( pQueryExpr); + if (ppNode) + { + + // *ppNode should have been initialized to NULL at the + // beginning of the evalExpr routine. + + flmAssert( *ppNode == NULL); + if (bUseKeyNodes && + pQueryExpr->nd.pXPath->pLastComponent->pKeyNode) + { + *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pKeyNode; + } + else + { + *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pCurrNode; + } + (*ppNode)->AddRef(); + m_pCurrOpt->ui64NodesPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + if (pbPassed) + { + *pbPassed = TRUE; + } + } + else + { + + // *pbPassed should have been initialized to FALSE in evalExpr, + // if pbPassed is non-NULL. + + flmAssert( !pbPassed || !(*pbPassed)); + + // *ppNode will already have been set to NULL - at beginning + // of evalExpr - so no need to do it here. Just assert + // that either ppNode is NULL or *ppNode is NULL. + + flmAssert( !ppNode || *ppNode == NULL); + + // If pCurrNode or pKeyNode is non-NULL it means we found one, but + // it doesn't have any data. That's ok, because this is + // only an exists test, and it should pass. + + if (bUseKeyNodes && + pQueryExpr->nd.pXPath->pLastComponent->pKeyNode) + { + if (pbPassed) + { + *pbPassed = TRUE; + } + if (ppNode) + { + *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pKeyNode; + (*ppNode)->AddRef(); + } + m_pCurrOpt->ui64NodesPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + else if (pQueryExpr->nd.pXPath->pLastComponent->pCurrNode) + { + if (pbPassed) + { + *pbPassed = TRUE; + } + if (ppNode) + { + *ppNode = pQueryExpr->nd.pXPath->pLastComponent->pCurrNode; + (*ppNode)->AddRef(); + } + m_pCurrOpt->ui64NodesPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + } + + // This routine is only called for expressions that are just an + // XPATH. If it is the outermost XPATH, we don't want to + // reset the iterator, as that is what we are using to + // iterate with. + + if (pQueryExpr->nd.pXPath->pFirstComponent->pXPathContext) + { + fqResetIterator( pQueryExpr, FALSE, bUseKeyNodes); + } + } + else if (pbPassed) + { + *pbPassed = fqTestValue( pQueryExpr); + if (ppNode) + { + if (*pbPassed) + { + *ppNode = m_pCurrDoc; + (*ppNode)->AddRef(); + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Evaluate a query expression. +***************************************************************************/ +RCODE F_Query::evalExpr( + IF_DOMNode * pContextNode, + FLMBOOL bForward, + FLMBOOL bUseKeyNodes, + FQNODE * pQueryExpr, + FLMBOOL * pbPassed, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pCurrNode = pQueryExpr; + FLMBOOL bXPathIsEntireExpr = FALSE; + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); + FLMBOOL bGetNodeValue; + + // IMPORTANT NOTE: A non-NULL ppNode should only be passed in for the + // very outermost evalExpr, not for nested ones. We use this to determine + // whether to count documents read and documents passed. + + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + if (pbPassed) + { + *pbPassed = FALSE; + } + + // If the query is empty, it passes + + if (!pQueryExpr) + { + if (pbPassed) + { + *pbPassed = TRUE; + } + if (ppNode) + { + *ppNode = m_pCurrDoc; + (*ppNode)->AddRef(); + } + goto Exit; + } + + if (pQueryExpr->eNodeType == FLM_XPATH_NODE && + !pQueryExpr->nd.pXPath->pFirstComponent->pXPathContext) + { + bXPathIsEntireExpr = TRUE; + } + fqResetQueryTree( pQueryExpr, bUseKeyNodes, m_bResetAllXPaths); + m_bResetAllXPaths = FALSE; + + // Perform the evaluation + + pCurrNode = pQueryExpr; + for (;;) + { + while (pCurrNode->pFirstChild) + { + pCurrNode = pCurrNode->pFirstChild; + } + + // We should be positioned on a leaf node that is either a + // value, a function, or an xpath. + + bGetNodeValue = FALSE; + if (pCurrNode->eNodeType == FLM_VALUE_NODE) + { + if (!pCurrNode->pParent) + { + pCurrNode = NULL; + break; + } + if (pCurrNode->bUsedValue) + { + pCurrNode = fqBackupTree( pCurrNode, &bGetNodeValue); + } + else + { + pCurrNode->bUsedValue = TRUE; + } + } + else if (pCurrNode->eNodeType == FLM_FUNCTION_NODE) + { + if (RC_BAD( rc = getFuncValue( pContextNode, bForward, + &pCurrNode, &bGetNodeValue, + &dynaBuf))) + { + goto Exit; + } + } + else + { + + // Better be an xpath at this point + + flmAssert( pCurrNode->eNodeType == FLM_XPATH_NODE); + if (RC_BAD( rc = getXPathValue( pContextNode, bForward, &pCurrNode, + &bGetNodeValue, bUseKeyNodes, + bXPathIsEntireExpr))) + { + goto Exit; + } + } + if (!pCurrNode) + { + break; + } + if (bGetNodeValue) + { + continue; + } + + // When we get to this point, we have at least one leaf + // level operand in hand - pCurrNode. + // See if we can evaluate the operator of pCurrNode. + // This will take care of any short-circuiting evaluation + // that can be done. + + if (RC_BAD( rc = fqTryEvalOperator( m_uiLanguage, &pCurrNode))) + { + goto Exit; + } + if (!pCurrNode) + { + break; + } + } + + if (RC_BAD( rc = setExprReturnValue( bUseKeyNodes, pQueryExpr, + pbPassed, ppNode))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Release the resources of a query expression +***************************************************************************/ +FSTATIC void fqReleaseQueryExpr( + FQNODE * pQNode + ) +{ + // Reset the entire query tree. It could have been left in a partial + // evaluation state from the last document evaluated. + + for (;;) + { + fqReleaseNodeValue( pQNode); + pQNode->bUsedValue = FALSE; + pQNode->bLastValue = FALSE; + if (pQNode->pFirstChild) + { + pQNode = pQNode->pFirstChild; + } + else + { + if (pQNode->eNodeType == FLM_XPATH_NODE) + { + fqResetIterator( pQNode, TRUE, FALSE); + } + while (!pQNode->pNextSib) + { + if ((pQNode = pQNode->pParent) == NULL) + { + break; + } + } + if (!pQNode) + { + break; + } + pQNode = pQNode->pNextSib; + } + } +} + +/*************************************************************************** +Desc: Release the resources of a query +***************************************************************************/ +void XFLMAPI F_Query::resetQuery( void) +{ + if (m_pQuery) + { + fqReleaseQueryExpr( m_pQuery); + } + + m_eState = XFLM_QUERY_NOT_POSITIONED; + + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } +} + +/*************************************************************************** +Desc: Get leaf predicate for the current context, changing current + context, context path, and predicate as needed. +***************************************************************************/ +void F_Query::useLeafContext( + FLMBOOL bGetFirst) +{ + for (;;) + { + if (m_pCurrContext->bIntersect) + { + if (m_pCurrContext->pSelectedChild) + { + flmAssert( !m_pCurrContext->pSelectedPath); + m_pCurrContext = m_pCurrContext->pSelectedChild; + } + else + { + flmAssert( m_pCurrContext->pSelectedPath); + m_pCurrContextPath = m_pCurrContext->pSelectedPath; + m_pCurrPred = m_pCurrContextPath->pSelectedPred; + flmAssert( m_pCurrContextPath && m_pCurrPred); + m_pCurrOpt = &m_pCurrPred->OptInfo; + break; + } + } + else + { + + // In a non-intersect context, pSelectedChild should NOT have + // been set. Nor should pSelectedPath. + + flmAssert( !m_pCurrContext->pSelectedChild); + flmAssert( !m_pCurrContext->pSelectedPath); + + if (bGetFirst) + { + if (m_pCurrContext->pFirstChild) + { + m_pCurrContext = m_pCurrContext->pFirstChild; + } + else + { + m_pCurrContextPath = m_pCurrContext->pFirstPath; + flmAssert( m_pCurrContextPath); + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pFirstPred; + flmAssert( m_pCurrPred); + m_pCurrOpt = &m_pCurrPred->OptInfo; + break; + } + } + else + { + if (m_pCurrContext->pLastChild) + { + m_pCurrContext = m_pCurrContext->pLastChild; + } + else + { + m_pCurrContextPath = m_pCurrContext->pLastPath; + flmAssert( m_pCurrContextPath); + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pLastPred; + flmAssert( m_pCurrPred); + m_pCurrOpt = &m_pCurrPred->OptInfo; + break; + } + } + } + } +} + +/*************************************************************************** +Desc: Get next predicate to evaluate for a query. +***************************************************************************/ +FLMBOOL F_Query::useNextPredicate( void) +{ + FLMBOOL bGotNext = FALSE; + + // If we are in a non-intersecting context, exhaust its + // context paths and predicates before going up a level. + + if (!m_pCurrContext->bIntersect) + { + if (m_pCurrPred) + { + if (m_pCurrPred->pNext) + { + m_pCurrPred = m_pCurrPred->pNext; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotNext = TRUE; + goto Exit; + } + if (m_pCurrContextPath->pNext) + { + m_pCurrContextPath = m_pCurrContextPath->pNext; + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pFirstPred; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotNext = TRUE; + goto Exit; + } + } + + // Go up one context level, if there is one. + + if (!m_pCurrContext->pParent) + { + goto Exit; + } + + // Parent better be an intersecting context + + m_pCurrContext = m_pCurrContext->pParent; + flmAssert( m_pCurrContext->bIntersect); + } + + // Go back up the context tree to get next context. + + for (;;) + { + OP_CONTEXT * pParent = m_pCurrContext->pParent; + + // If there is no parent context, we are done. + + if (!pParent) + { + break; + } + if (m_pCurrContext->bIntersect) + { + + // Parent context should be non-intersecting, so we should + // go to the sibling context, if there is one. + + flmAssert( !pParent->bIntersect); + if (m_pCurrContext->pNextSib) + { + m_pCurrContext = m_pCurrContext->pNextSib; + + // Travel down this part of the context tree to get the + // "leaf-most" context, context path, and predicate. + + useLeafContext( TRUE); + bGotNext = TRUE; + break; + } + } + else + { + if (m_pCurrContext->pFirstPath) + { + m_pCurrContextPath = m_pCurrContext->pFirstPath; + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pFirstPred; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotNext = TRUE; + goto Exit; + } + + // Parent context better be pointing to this context as its + // "selected" context. + + flmAssert( pParent->pSelectedChild == m_pCurrContext); + + // Parent also better be an intersecting context. + + flmAssert( pParent->bIntersect); + } + m_pCurrContext = pParent; + } + +Exit: + + return( bGotNext); +} + +/*************************************************************************** +Desc: Get previous predicate to evaluate for a query. +***************************************************************************/ +FLMBOOL F_Query::usePrevPredicate( void) +{ + FLMBOOL bGotPrev = FALSE; + + // If we are in a non-intersecting context, exhaust its + // context paths and predicates before going up a level. + + if (!m_pCurrContext->bIntersect) + { + if (m_pCurrPred) + { + if (m_pCurrPred->pPrev) + { + m_pCurrPred = m_pCurrPred->pPrev; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotPrev = TRUE; + goto Exit; + } + if (m_pCurrContextPath->pPrev) + { + m_pCurrContextPath = m_pCurrContextPath->pPrev; + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pLastPred; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotPrev = TRUE; + goto Exit; + } + } + + // Go up one context level, if there is one. + + if (!m_pCurrContext->pParent) + { + goto Exit; + } + + // Parent better be an intersecting context + + m_pCurrContext = m_pCurrContext->pParent; + flmAssert( m_pCurrContext->bIntersect); + } + + // Go back up the context tree to get previous context. + + for (;;) + { + OP_CONTEXT * pParent = m_pCurrContext->pParent; + + // If there is no parent context, we are done. + + if (!pParent) + { + break; + } + if (m_pCurrContext->bIntersect) + { + + // Parent context should be non-intersecting, so we should + // go to the sibling context, if there is one. + + flmAssert( !pParent->bIntersect); + if (m_pCurrContext->pPrevSib) + { + m_pCurrContext = m_pCurrContext->pPrevSib; + + // Travel down this part of the context tree to get the + // "leaf-most" context, context path, and predicate. + + useLeafContext( FALSE); + bGotPrev = TRUE; + break; + } + } + else + { + if (m_pCurrContext->pLastPath) + { + m_pCurrContextPath = m_pCurrContext->pLastPath; + + // In a non-intersect context, pSelectedPred should NOT have + // been set. + + flmAssert( !m_pCurrContextPath->pSelectedPred); + m_pCurrPred = m_pCurrContextPath->pLastPred; + m_pCurrOpt = &m_pCurrPred->OptInfo; + bGotPrev = TRUE; + goto Exit; + } + + // Parent context better be pointing to this context as its + // "selected" context. + + flmAssert( pParent->pSelectedChild == m_pCurrContext); + + // Parent also better be an intersecting context. + + flmAssert( pParent->bIntersect); + } + m_pCurrContext = pParent; + } + +Exit: + + return( bGotPrev); +} + +/*************************************************************************** +Desc: Get the next/previous node for an application predicate. +***************************************************************************/ +RCODE F_Query::getAppNode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent + ) +{ + RCODE rc = NE_XFLM_OK; + IF_QueryNodeSource * pNodeSource = m_pCurrPred->pNodeSource; + FLMUINT uiCurrTime; + FLMUINT uiElapsedTime; + FLMUINT uiTimeLimit = m_uiTimeLimit; + FLMUINT64 ui64DocId; + + for (;;) + { + + // Reset the timeout value everytime through the loop, if it + // is non-zero. + + if (uiTimeLimit) + { + uiCurrTime = FLM_GET_TIMER(); + uiElapsedTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiStartTime); + if (uiElapsedTime >= m_uiTimeLimit) + { + rc = RC_SET( NE_XFLM_TIMEOUT); + goto Exit; + } + else + { + FLM_TIMER_UNITS_TO_MILLI( (m_uiTimeLimit - uiElapsedTime), uiTimeLimit); + + // Always give at least one milli-second. + + if (!uiTimeLimit) + { + uiTimeLimit = 1; + } + } + } + + if (pXPathComponent->pKeyNode) + { + pXPathComponent->pKeyNode->Release(); + pXPathComponent->pKeyNode = NULL; + } + + // Get the next or previous key from the index. + + if (bForward) + { + rc = (RCODE)(*pbFirstLast + ? pNodeSource->getFirst( (IF_Db *)m_pDb, NULL, + &pXPathComponent->pKeyNode, + uiTimeLimit, m_pQueryStatus) + : pNodeSource->getNext( (IF_Db *)m_pDb, NULL, + &pXPathComponent->pKeyNode, + uiTimeLimit, m_pQueryStatus)); + + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + else + { + rc = (RCODE)(*pbFirstLast + ? pNodeSource->getLast( (IF_Db *)m_pDb, NULL, + &pXPathComponent->pKeyNode, + uiTimeLimit, m_pQueryStatus) + : pNodeSource->getPrev( (IF_Db *)m_pDb, NULL, + &pXPathComponent->pKeyNode, + uiTimeLimit, m_pQueryStatus)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + *pbFirstLast = FALSE; + + // If we are eliminating duplicates, see if the document + // has already been processed. If so, skip the key. + + if (RC_BAD( rc = pXPathComponent->pKeyNode->getDocumentId( (IF_Db *)m_pDb, + &ui64DocId))) + { + goto Exit; + } + if (m_pDocIdSet) + { + if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) + { + if (rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + + // Document has already been passed, go to next/prev key. + + m_pCurrOpt->ui64KeyHadDupDoc++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + continue; + } + } + + // At this point, the key has passed at least the predicate function. + // Get the document node. + + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, ui64DocId, &m_pCurrDoc))) + { + goto Exit; + } + + // Found one! + + break; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get an FQVALUE from a key. +***************************************************************************/ +FSTATIC RCODE fqGetValueFromKey( + FLMUINT uiDataType, + F_DataVector * pKey, + FQVALUE * pQValue, + FLMBYTE ** ppucValue, + FLMUINT uiValueBufSize + ) +{ + RCODE rc = NE_XFLM_OK; + + pQValue->uiFlags = 0; + switch (uiDataType) + { + case XFLM_NODATA_TYPE: + + // Should have been set to missing on the outside + + flmAssert( pQValue->eValType == XFLM_MISSING_VAL); + pQValue->eValType = XFLM_BOOL_VAL; + pQValue->val.eBool = XFLM_TRUE; + break; + case XFLM_TEXT_TYPE: + pQValue->uiDataLen = pKey->getDataLength( 0) + 1; + if (pQValue->uiDataLen > uiValueBufSize) + { + if (RC_BAD( rc = f_alloc( pQValue->uiDataLen, ppucValue))) + { + goto Exit; + } + } + pQValue->val.pucBuf = *ppucValue; + if (RC_BAD( rc = pKey->getUTF8( 0, pQValue->val.pucBuf, + &pQValue->uiDataLen))) + { + goto Exit; + } + pQValue->eValType = XFLM_UTF8_VAL; + break; + case XFLM_NUMBER_TYPE: + + // First, see if we can get it as a UINT - the most common + // type. + + if (RC_OK( rc = pKey->getUINT( 0, &pQValue->val.uiVal))) + { + pQValue->eValType = XFLM_UINT_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_OVERFLOW) + { + if (RC_BAD( rc = pKey->getUINT64( 0, + &pQValue->val.ui64Val))) + { + goto Exit; + } + pQValue->eValType = XFLM_UINT64_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) + { + if (RC_OK( rc = pKey->getINT( 0, &pQValue->val.iVal))) + { + pQValue->eValType = XFLM_INT_VAL; + } + else if (rc == NE_XFLM_CONV_NUM_UNDERFLOW) + { + if (RC_BAD( rc = pKey->getINT64( 0, + &pQValue->val.i64Val))) + { + goto Exit; + } + pQValue->eValType = XFLM_INT64_VAL; + } + } + else + { + goto Exit; + } + break; + case XFLM_BINARY_TYPE: + pQValue->uiDataLen = pKey->getDataLength( 0) + 1; + if (pQValue->uiDataLen > uiValueBufSize) + { + if (RC_BAD( rc = f_alloc( pQValue->uiDataLen, ppucValue))) + { + goto Exit; + } + } + pQValue->val.pucBuf = *ppucValue; + if (RC_BAD( rc = pKey->getBinary( 0, pQValue->val.pucBuf, + &pQValue->uiDataLen))) + { + goto Exit; + } + pQValue->eValType = XFLM_BINARY_VAL; + break; + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Test a value against the specified predicate. +***************************************************************************/ +FSTATIC RCODE fqPredCompare( + FLMUINT uiLanguage, + PATH_PRED * pPred, + FQVALUE * pQValue, + FLMBOOL * pbPasses + ) +{ + RCODE rc = NE_XFLM_OK; + XFlmBoolType eBool; + + switch (pPred->eOperator) + { + case XFLM_EXISTS_OP: + + // We already know this one passes. + + *pbPasses = TRUE; + goto Exit; + case XFLM_NE_OP: + case XFLM_APPROX_EQ_OP: + if (RC_BAD( rc = fqCompareOperands( uiLanguage, + pQValue, + pPred->pFromValue, + pPred->eOperator, + pPred->uiCompareRules, + pPred->pOpComparer, + pPred->bNotted, + &eBool))) + { + goto Exit; + } + break; + case XFLM_MATCH_OP: + + // From value of predicate better be a constant + // with wildcards set. + + flmAssert( pPred->pFromValue->uiFlags & VAL_IS_CONSTANT); + flmAssert( pPred->pFromValue->uiFlags & VAL_HAS_WILDCARDS); + if (RC_BAD( rc = fqCompareOperands( uiLanguage, + pQValue, + pPred->pFromValue, + XFLM_EQ_OP, + pPred->uiCompareRules, + pPred->pOpComparer, + pPred->bNotted, + &eBool))) + { + goto Exit; + } + break; + case XFLM_RANGE_OP: + eBool = XFLM_TRUE; + + // Take care of ==, > and >= + + if (pPred->pFromValue) + { + eQueryOperators eOperator; + + if (pPred->pUntilValue == pPred->pFromValue) + { + eOperator = XFLM_EQ_OP; + } + else + { + eOperator = pPred->bInclFrom ? XFLM_GE_OP : XFLM_GT_OP; + } + if (RC_BAD( rc = fqCompareOperands( uiLanguage, + pQValue, + pPred->pFromValue, + eOperator, + pPred->uiCompareRules, + pPred->pOpComparer, + pPred->bNotted, + &eBool))) + { + goto Exit; + } + } + + // Take care of < and <= if we are still TRUE + + if (eBool == XFLM_TRUE && pPred->pUntilValue && + pPred->pUntilValue != pPred->pFromValue) + { + if (RC_BAD( rc = fqCompareOperands( uiLanguage, + pQValue, + pPred->pUntilValue, + pPred->bInclUntil ? XFLM_LE_OP : XFLM_LT_OP, + pPred->uiCompareRules, + pPred->pOpComparer, + pPred->bNotted, + &eBool))) + { + goto Exit; + } + } + break; + default: + *pbPasses = FALSE; + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // If the value didn't pass the predicate, we should return + // a FALSE in *pbPasses. + + *pbPasses = (eBool == XFLM_FALSE) ? FALSE : TRUE; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Test the key against the specified predicate. +***************************************************************************/ +RCODE F_Query::testKey( + F_DataVector * pKey, + PATH_PRED * pPred, + FLMBOOL * pbPasses, + IF_DOMNode ** ppPassedNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64NodeId; + FQVALUE currVal; + FLMUINT uiDataType; + FLMBYTE ucKeyValue [128]; + FLMBYTE * pucKeyValue = &ucKeyValue [0]; + + currVal.eValType = XFLM_MISSING_VAL; + + // At this point, all use of notted predicates should have been + // weeded out. + + flmAssert( !pPred->bNotted); + *pbPasses = TRUE; + + // First see if the key passes the criteria of the predicate. + // If we can use the key, use it. Otherwise, fetch the node + // and do the comparison. + + if (pPred->OptInfo.bCanCompareOnKey) + { + // No need to retrieve the data if it is an exists operator + + if (pPred->eOperator != XFLM_EXISTS_OP) + { + if ((uiDataType = pKey->getDataType( 0)) == XFLM_UNKNOWN_TYPE) + { + + // No data - component was missing. + + *pbPasses = FALSE; + goto Exit; + } + + if (RC_BAD( rc = fqGetValueFromKey( uiDataType, pKey, &currVal, + &pucKeyValue, sizeof( ucKeyValue)))) + { + goto Exit; + } + + // Compare on the key + + if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, + &currVal, pbPasses))) + { + goto Exit; + } + if (!(*pbPasses)) + { + goto Exit; + } + } + } + + // Have to get the node whether we compare on it or not + + if ((ui64NodeId = pKey->getID( 0)) == 0) + { + + // No data - component was missing. + + *pbPasses = FALSE; + goto Exit; + } + + if( pKey->isAttr( 0)) + { + rc = m_pDb->getAttribute( m_uiCollection, + ui64NodeId, pKey->getNameId( 0), ppPassedNode); + } + else + { + rc = m_pDb->getNode( m_uiCollection, ui64NodeId, ppPassedNode); + } + + if( RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + + // See if we need to do the comparison on the node. + + if (pPred->OptInfo.bDoNodeMatch && pPred->eOperator != XFLM_EXISTS_OP) + { + if (RC_BAD( rc = fqGetValueFromNode( m_pDb, *ppPassedNode, &currVal, 0))) + { + goto Exit; + } + + // Do the comparison + + if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, + &currVal, pbPasses))) + { + goto Exit; + } + + if (!(*pbPasses)) + { + goto Exit; + } + } + + // At this point, the key has passed at least the predicate comparison. + // Get the document node + + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, + pKey->getDocumentID(), &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // If we cannot retrieve the node, we have a corruption + // in the database. + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + +Exit: + + if (pucKeyValue != &ucKeyValue [0]) + { + f_free( &pucKeyValue); + } + + if ((currVal.eValType == XFLM_BINARY_VAL || + currVal.eValType == XFLM_UTF8_VAL) && + (currVal.uiFlags & VAL_IS_STREAM) && + currVal.val.pIStream) + { + currVal.val.pIStream->Release(); + } + return( rc); +} + +/*************************************************************************** +Desc: Mark all of the XPATH nodes that are the same as the one in + pXPathComponent as having a passing value. +***************************************************************************/ +FSTATIC void fqMarkXPathNodeListPassed( + PATH_PRED * pPred + ) +{ + PATH_PRED_NODE * pXPathNodeList; + + pXPathNodeList = pPred->pXPathNodeList; + while (pXPathNodeList) + { + pXPathNodeList->pXPathNode->nd.pXPath->bHavePassingNode = TRUE; + pXPathNodeList = pXPathNodeList->pNext; + } +} + +/*************************************************************************** +Desc: Get the next/previous key for an xpath component. +***************************************************************************/ +RCODE F_Query::getKey( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pPred = pXPathComponent->pOptPred; + FSIndexCursor * pFSIndexCursor = pPred->pFSIndexCursor; + F_DataVector key; + FLMBOOL bPassed; + FLMBOOL bSkipCurrKey = FALSE; + FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); + + // Better be rightmost xpath component. + + flmAssert( !pXPathComponent->pNext); + + // Component could have an expression, but it should not be one + // we are optimizing on this time around. + + flmAssert( !pXPathComponent->pExprXPathSource); + + for (;;) + { + + // Release the current key node, if any + + if (pXPathComponent->pKeyNode) + { + pXPathComponent->pKeyNode->Release(); + pXPathComponent->pKeyNode = NULL; + } + + // Get the next or previous key from the index. + + if (bForward) + { + rc = (RCODE)(*pbFirstLast + ? pFSIndexCursor->firstKey( m_pDb, &key) + : pFSIndexCursor->nextKey( m_pDb, &key, bSkipCurrKey)); + + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + else + { + rc = (RCODE)(*pbFirstLast + ? pFSIndexCursor->lastKey( m_pDb, &key) + : pFSIndexCursor->prevKey( m_pDb, &key, bSkipCurrKey)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + *pbFirstLast = FALSE; + m_pCurrOpt->ui64KeysRead++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + + // If we are eliminating duplicates, see if the document + // has already been processed. If so, skip the key. + + if (m_pDocIdSet) + { + FLMUINT64 ui64DocId = key.getDocumentID(); + + if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) + { + if (rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + + // Document has already been passed, go to next/prev key. + + m_pCurrOpt->ui64KeyHadDupDoc++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + continue; + } + } + + // Evaluate the key. + + if (RC_BAD( rc = testKey( &key, pPred, &bPassed, + &pXPathComponent->pKeyNode))) + { + goto Exit; + } + if (!bPassed) + { + // If this is a case-sensitive index or a case-insensitive compare, + // we can skip the key. Otherwise, we cannot skip any keys. + + if (!(pFSIndexCursor->m_pIxd->pFirstKey->uiCompareRules & + XFLM_COMP_CASE_INSENSITIVE) || + (pPred->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE)) + { + bSkipCurrKey = TRUE; + } + continue; + } + bSkipCurrKey = FALSE; + m_pCurrOpt->ui64KeysPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + + // If the xpath component has an expression, evaluate it + + if (pXPathComponent->pExpr) + { + if (RC_BAD( rc = evalExpr( pXPathComponent->pKeyNode, + bForward, TRUE, pXPathComponent->pExpr, + &bPassed, NULL))) + { + goto Exit; + } + if (!bPassed) + { + continue; + } + } + + if (bHasContextPosTest) + { + + // Need to verify the context position of the found node. + + if (RC_BAD( rc = verifyOccurrence( FALSE, pXPathComponent, + pXPathComponent->pKeyNode, &bPassed))) + { + goto Exit; + } + if (!bPassed) + { + continue; + } + } + + // There may be more than one XPATH that this predicate + // covers. Set them up so they don't have to be evaluated + // if at all possible. + + fqMarkXPathNodeListPassed( pPred); + break; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Test a node's metadata against the specified predicate. +***************************************************************************/ +RCODE F_Query::testMetaData( + IF_DOMNode * pNode, + FLMUINT uiMetaDataType, + PATH_PRED * pPred, + FLMBOOL * pbPasses + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64DocId; + FQVALUE currVal; + + currVal.eValType = XFLM_MISSING_VAL; + + // At this point, all use of notted predicates should have been + // weeded out. + + flmAssert( !pPred->bNotted); + *pbPasses = TRUE; + + // See if we need to do the comparison on the node. + + if (pPred->eOperator != XFLM_EXISTS_OP) + { + if (RC_BAD( rc = fqGetValueFromNode( m_pDb, pNode, &currVal, + uiMetaDataType))) + { + goto Exit; + } + + // Do the comparison + + if (RC_BAD( rc = fqPredCompare( m_uiLanguage, pPred, + &currVal, pbPasses))) + { + goto Exit; + } + + if (!(*pbPasses)) + { + goto Exit; + } + } + + // At this point, the node has passed at least the predicate comparison. + // Get the document node, if we don't already have it. + + if( !(((F_DOMNode *)pNode)->isRootNode())) + { + if (RC_BAD( rc = pNode->getDocumentId( m_pDb, &ui64DocId))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, + ui64DocId, &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // If we cannot retrieve the node, we have a corruption + // in the database. + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + } + else + { + m_pCurrDoc = pNode; + m_pCurrDoc->AddRef(); + } + + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + +Exit: + + if ((currVal.eValType == XFLM_BINARY_VAL || + currVal.eValType == XFLM_UTF8_VAL) && + (currVal.uiFlags & VAL_IS_STREAM) && + currVal.val.pIStream) + { + currVal.val.pIStream->Release(); + } + return( rc); +} + +/*************************************************************************** +Desc: Get the next/previous node for an xpath component. +***************************************************************************/ +RCODE F_Query::getANode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent + ) +{ + RCODE rc = NE_XFLM_OK; + PATH_PRED * pPred = pXPathComponent->pOptPred; + FSCollectionCursor * pFSCollectionCursor = pPred->pFSCollectionCursor; + FLMBOOL bPassed; + IF_DOMNode * pNode = NULL; + FLMUINT64 ui64NodeId; + FLMBOOL bHasContextPosTest = hasContextPosTest( pXPathComponent); + + // Better be rightmost xpath component. + + flmAssert( !pXPathComponent->pNext); + + // Component could have an expression, but it should not be one + // we are optimizing on this time around. + + flmAssert( !pXPathComponent->pExprXPathSource); + + for (;;) + { + if (pNode) + { + pNode->Release(); + pNode = NULL; + } + + // See if we need to continue from the current node in the current + // document, or release it and get another node. + + if ((pNode = pXPathComponent->pKeyNode) != NULL) + { + pXPathComponent->pKeyNode = NULL; + + // No need to do pNode->AddRef(), because we will just + // steal the AddRef that was on pCurrNode. + + if (getMetaDataType( pXPathComponent) != XFLM_META_DOCUMENT_ID) + { + pNode->Release(); + pNode = NULL; + } + else + { + + // If we are doing document nodes, see if we can continue in the + // document - because all of the nodes in the document should + // be processed. + + if (RC_BAD( rc = walkDocument( bForward, TRUE, 0, &pNode))) + { + goto Exit; + } + } + + // If we didn't get a node back, and + // If pFSCollectionCursor is NULL, it means we + // are doing a single node, and we have already + // done that single node (or document), so we can + // quit. + + if (!pNode && !pFSCollectionCursor) + { + goto Exit; + } + } + + // Get the next or previous node from the collection, if we + // didn't get a node from the loop above. + + if (!pNode) + { + if (pFSCollectionCursor) + { + if (bForward) + { + rc = (RCODE)(*pbFirstLast + ? pFSCollectionCursor->firstNode( m_pDb, &pNode, + &ui64NodeId) + : pFSCollectionCursor->nextNode( m_pDb, &pNode, + &ui64NodeId)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + else + { + rc = (RCODE)(*pbFirstLast + ? pFSCollectionCursor->lastNode( m_pDb, &pNode, + &ui64NodeId) + : pFSCollectionCursor->prevNode( m_pDb, &pNode, + &ui64NodeId)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_BOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + } + else if (*pbFirstLast) + { + + // Getting a single node. + + if (getMetaDataType( pXPathComponent) == XFLM_META_DOCUMENT_ID) + { + if (RC_BAD( rc = m_pDb->getDocument( m_uiCollection, + XFLM_EXACT, pPred->OptInfo.ui64NodeId, + &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, + pPred->OptInfo.ui64NodeId, + &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + } + else + { + goto Exit; + } + *pbFirstLast = FALSE; + m_pCurrOpt->ui64NodesRead++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + + // If we are eliminating duplicates, see if the document + // has already been processed. If so, skip the key. + + if (m_pDocIdSet) + { + FLMUINT64 ui64DocId; + + if (RC_BAD( rc = pNode->getDocumentId( m_pDb, &ui64DocId))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) + { + if (rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + + // Document has already been passed, go to next/prev node. + + m_pCurrOpt->ui64KeyHadDupDoc++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + continue; + } + } + + // Evaluate the node. + + if (RC_BAD( rc = testMetaData( pNode, getMetaDataType( pXPathComponent), + pPred, &bPassed))) + { + goto Exit; + } + if (bPassed) + { + pXPathComponent->pKeyNode = pNode; + pXPathComponent->pKeyNode->AddRef(); + } + else + { + continue; + } + + // If the xpath component has an expression, evaluate it + + if (pXPathComponent->pExpr) + { + if (RC_BAD( rc = evalExpr( pXPathComponent->pKeyNode, + bForward, TRUE, pXPathComponent->pExpr, + &bPassed, NULL))) + { + goto Exit; + } + if (!bPassed) + { + continue; + } + } + + if (bHasContextPosTest) + { + + // Need to verify the context position of the found node. + + if (RC_BAD( rc = verifyOccurrence( FALSE, pXPathComponent, + pXPathComponent->pKeyNode, &bPassed))) + { + goto Exit; + } + if (!bPassed) + { + continue; + } + } + + // There may be more than one XPATH that this predicate + // covers. Set them up so they don't have to be evaluated + // if at all possible. + + fqMarkXPathNodeListPassed( pPred); + break; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get a context node for the passed in xpath component. +***************************************************************************/ +RCODE F_Query::getContextNode( + FLMBOOL bForward, + XPATH_COMPONENT * pXPathComponent + ) +{ + RCODE rc = NE_XFLM_OK; + + // Component better be the left-most xpath component. + + flmAssert( !pXPathComponent->pPrev); + + // See if we can now get a node for the XPATH context + // component. + + if (RC_BAD( rc = getXPathComponentFromAxis( pXPathComponent->pKeyNode, + bForward, TRUE, pXPathComponent->pXPathContext, + &pXPathComponent->pXPathContext->pKeyNode, + invertedAxis( pXPathComponent->eXPathAxis), + TRUE, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next value for an XPATH component that has an expression + that is the source for the query. +***************************************************************************/ +RCODE F_Query::getNextIndexNode( + FLMBOOL * pbFirstLast, + FLMBOOL bForward, + FQNODE * pExprXPathSource, + FLMBOOL bSkipCurrKey) +{ + RCODE rc = NE_XFLM_OK; + FXPATH * pSourceXPath; + XPATH_COMPONENT * pXPathComp; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64Tmp; + + // This routine should only be called for components that have an + // expression that is serving as the source for the query. + // Both pExpr and pXPathSource must be non-NULL. + + flmAssert( pExprXPathSource->eNodeType == FLM_XPATH_NODE); + + pSourceXPath = pExprXPathSource->nd.pXPath; + flmAssert( pSourceXPath->bIsSource); + + // Release all pCurrNodes that come AFTER the source component. + // Start from last component and go backwards. + + pXPathComp = pSourceXPath->pLastComponent; + while (pXPathComp) + { + if (pXPathComp->bIsSource) + { + break; + } + if (pXPathComp->pCurrNode) + { + pXPathComp->pCurrNode->Release(); + pXPathComp->pCurrNode = NULL; + } + pXPathComp = pXPathComp->pPrev; + } + + // If this is the first time in, we need to go right to the + // component in the XPath that is the key source. + + if (bSkipCurrKey) + { + + // Release all of the DOM nodes up to and including the one + // that is in the source component. pXPathComp should be + // pointing to the source component when we are done. + + pXPathComp = pSourceXPath->pFirstComponent; + for (;;) + { + if (pXPathComp == pSourceXPath->pSourceComponent) + { + + // Only release pKeyNode if we are not on the + // optimization predicate. Otherwise, we will + // allow the call to getKey or getANode to do it. + // This is mainly for the benefit of getANode, so it + // can properly handle the document ID case, where it + // needs to know the last node it was on inside a + // document so it can continue walking from there. + + if (!pXPathComp->pOptPred && pXPathComp->pKeyNode) + { + pXPathComp->pKeyNode->Release(); + pXPathComp->pKeyNode = NULL; + } + break; + } + if (pXPathComp->pKeyNode) + { + pXPathComp->pKeyNode->Release(); + pXPathComp->pKeyNode = NULL; + } + pXPathComp = pXPathComp->pNext; + } + flmAssert( pXPathComp->bIsSource); + } + else if (!pSourceXPath->pFirstComponent->pKeyNode) + { + pXPathComp = pSourceXPath->pSourceComponent; + flmAssert( pXPathComp->bIsSource); + } + else + { + pXPathComp = pSourceXPath->pFirstComponent; + } + for (;;) + { + if (pXPathComp->bIsSource) + { + if (pXPathComp->pOptPred) + { + if (pXPathComp->pOptPred->pFSIndexCursor) + { + if (RC_BAD( rc = getKey( pbFirstLast, bForward, pXPathComp))) + { + goto Exit; + } + } + else if (pXPathComp->pOptPred->pNodeSource) + { + if (RC_BAD( rc = getAppNode( pbFirstLast, bForward, pXPathComp))) + { + goto Exit; + } + } + else + { + + // NOTE: pXPathComp->pOptPred->pFSCollectionCursor may be NULL + // if getting a single node (eOptType == XFLM_QOPT_SINGLE_NODE_ID) + + if (RC_BAD( rc = getANode( pbFirstLast, bForward, pXPathComp))) + { + goto Exit; + } + } + + if (!pXPathComp->pKeyNode) + { + // Didn't get a node. + // Cannot go any further than this - means we are + // out of keys for this predicate. + + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + + goto Exit; + } + } + else + { + flmAssert( pXPathComp->pExprXPathSource && pXPathComp->pExpr); + + // First see if we can get another context node without going + // down to get another key. + + if (pXPathComp->pKeyNode) + { + + // Got a node from expression, get context node for that node. + + if (RC_BAD( rc = getContextNode( bForward, + pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent))) + { + goto Exit; + } + } + while (!pXPathComp->pKeyNode) + { + if (RC_BAD( rc = getNextIndexNode( pbFirstLast, bForward, + pXPathComp->pExprXPathSource, + bSkipCurrKey))) + { + goto Exit; + } + + // See if we got a node from below. If not, there is nothing + // more we can do at this level. + + if (!pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent->pKeyNode) + { + if (pXPathComp->pKeyNode) + { + pXPathComp->pKeyNode->Release(); + pXPathComp->pKeyNode = NULL; + } + goto Exit; + } + + // Got a node from expression, get context node for that node. + + if (RC_BAD( rc = getContextNode( bForward, + pXPathComp->pExprXPathSource->nd.pXPath->pFirstComponent))) + { + goto Exit; + } + } + } + } + else + { + + // There has to be a next to this node - because the source + // component has to be somewhere after this component. + + flmAssert( pXPathComp->pNext); + if (RC_BAD( rc = getXPathComponentFromAxis( + pXPathComp->pNext->pKeyNode, bForward, TRUE, + pXPathComp, &pXPathComp->pKeyNode, + invertedAxis( pXPathComp->pNext->eXPathAxis), + TRUE, FALSE))) + { + goto Exit; + } + + // If we didn't get a node, go to next component and try to + // get its next node. + + if (!pXPathComp->pKeyNode) + { + pXPathComp = pXPathComp->pNext; + continue; + } + } + + // At this point we better have gotten a node. + + flmAssert( pXPathComp->pKeyNode); + + // If this is the left-most xpath component and we got a node + // and the component's axis is the root axis, verify that the + // node is, in fact the root node. + + if (!pXPathComp->pPrev && pXPathComp->eXPathAxis == ROOT_AXIS) + { + FLMUINT64 ui64DocId; + + if( RC_BAD( rc = m_pCurrDoc->getNodeId( m_pDb, &ui64DocId))) + { + goto Exit; + } + + if( RC_BAD( rc = pXPathComp->pKeyNode->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + if( ui64DocId != ui64Tmp ) + { + + if (RC_BAD( rc = pXPathComp->pKeyNode->getParentId( + m_pDb, &ui64NodeId))) + { + goto Exit; + } + + if ( ui64NodeId != ui64DocId || m_pCurrDoc->getNodeType() != DOCUMENT_NODE) + { + continue; + } + } + } + + // See if we can go to a previous component. If not, we are done. + + if ((pXPathComp = pXPathComp->pPrev) == NULL) + { + break; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Set up to use the current predicate as the source for the query. +***************************************************************************/ +RCODE F_Query::setupCurrPredicate( + FLMBOOL bForward + ) +{ + RCODE rc = NE_XFLM_OK; + FXPATH * pXPath; + XPATH_COMPONENT * pXPathContextComponent; + FLMBOOL bFirstLast; + + // Clear all state information. + + resetQuery(); + + if (RC_BAD( rc = newSource())) + { + goto Exit; + } + + // m_pCurrPred has already been set. We just need to set up each + // XPATH appropriately. + + // Could be multiple XPATHs associated with the predicate, but it + // is only necessary to set up one of them to point to the predicate. + // We could pick any of them, but we take the first one in the list. + + pXPath = m_pCurrPred->pXPathNodeList->pXPathNode->nd.pXPath; + pXPath->bIsSource = TRUE; + + // The source component for this XPATH will always be the + // last component, because predicates are always optimized on + // the last component of an XPATH. + + pXPath->pSourceComponent = pXPath->pLastComponent; + pXPath->pSourceComponent->bIsSource = TRUE; + pXPath->pSourceComponent->pOptPred = m_pCurrPred; + + // See if this XPATH is nested inside of another XPATH + // component. If so, that XPATH component must be marked + // as the source, and its XPATH must also be marked as the + // source. That XPATH component must also point to this + // particular XPATH node as the expression XPATH source for + // the context component. + + m_pExprXPathSource = m_pCurrPred->pXPathNodeList->pXPathNode; + pXPathContextComponent = pXPath->pSourceComponent->pXPathContext; + while (pXPathContextComponent) + { + + // Get the context component's XPATH + + pXPath = pXPathContextComponent->pXPathNode->nd.pXPath; + pXPath->bIsSource = TRUE; + pXPath->pSourceComponent = pXPathContextComponent; + pXPathContextComponent->bIsSource = TRUE; + pXPathContextComponent->pExprXPathSource = m_pExprXPathSource; + + // Setup for the next higher context, if any + + m_pExprXPathSource = pXPathContextComponent->pXPathNode; + pXPathContextComponent = pXPathContextComponent->pXPathContext; + } + + // Now get the first or last index node + + bFirstLast = TRUE; + if (RC_BAD( rc = getNextIndexNode( &bFirstLast, bForward, + m_pExprXPathSource, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: See if a node/document passes all of the conditions for a query. + Also increment the necessary counters. + If it passes and we are building a result set, add it to the + result set. +***************************************************************************/ +RCODE F_Query::testPassed( + IF_DOMNode ** ppNode, + FLMBOOL * pbPassed, + FLMBOOL * pbEliminatedDup) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bReportRSStatus = FALSE; + FLMBOOL bReportQueryStatus = FALSE; + + *pbEliminatedDup = FALSE; + if (!m_pQuery || m_pQuery->eNodeType != FLM_XPATH_NODE || m_bRemoveDups) + { + + // NOTE: If the document passed, but was eliminated by + // duplicate checking, we will NOT increment documents read, + // because we know that we have read the document before. + // If the document failed, we also need to increment documents read, + // even though we don't really know if we read it before. In this + // case, we may end up getting a larger document read count than + // we should because we could count the same document as being + // read more than once. + + if (*pbPassed) + { + if (RC_BAD( rc = checkIfDup( ppNode, pbPassed))) + { + goto Exit; + } + if (*pbPassed) + { + m_pCurrOpt->ui64DocsRead++; + bReportQueryStatus = TRUE; + if (m_pSortResultSet) + { + m_ui64RSDocsRead++; + bReportRSStatus = TRUE; + } + } + else + { + *pbEliminatedDup = TRUE; + } + } + else + { + m_pCurrOpt->ui64DocsRead++; + bReportQueryStatus = TRUE; + if (m_pSortResultSet) + { + m_ui64RSDocsRead++; + bReportRSStatus = TRUE; + } + } + } + + if (RC_BAD( rc = validateNode( *ppNode, pbPassed))) + { + goto Exit; + } + if (*pbPassed) + { + m_pCurrOpt->ui64DocsPassed++; + bReportQueryStatus = TRUE; + + // If we have a sort key and the sort order is different than + // the optimization index, or the application requested that + // we build a result set so it could do positioning, add the + // document to the result set. + + if (m_pSortResultSet) + { + if (RC_BAD( rc = addToResultSet())) + { + goto Exit; + } + bReportRSStatus = TRUE; + } + } + + if (bReportQueryStatus) + { + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + if (bReportRSStatus && m_pQueryStatus) + { + if (RC_BAD( rc = m_pQueryStatus->resultSetStatus( + m_ui64RSDocsRead, m_ui64RSDocsPassed, + m_bEntriesAlreadyInOrder))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from the index. +***************************************************************************/ +RCODE F_Query::nextFromIndex( + FLMBOOL bEvalCurrDoc, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bPassed; + FLMBOOL bFirst; + + if (!bEvalCurrDoc) + { + bFirst = FALSE; + if (RC_BAD( rc = getNextIndexNode( &bFirst, TRUE, + m_pExprXPathSource, TRUE))) + { + goto Exit; + } + } + + for (;;) + { + + // If m_pCurrDoc is non-NULL, it means we are set up + // to call evalExpr. + + while (m_pCurrDoc) + { + FLMBOOL bPassedEval; + FLMBOOL bEliminatedDup; + + if (RC_BAD( rc = evalExpr( NULL, TRUE, TRUE, + m_pQuery, &bPassed, ppNode))) + { + if( rc == NE_XFLM_DOM_NODE_DELETED) + { + m_bResetAllXPaths = TRUE; + rc = NE_XFLM_OK; + goto Next_Index_Node; + } + goto Exit; + } + + bPassedEval = bPassed; + + if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) + { + goto Exit; + } + + if (bPassed) + { + m_eState = (m_eState == XFLM_QUERY_AT_BOF || + m_eState == XFLM_QUERY_NOT_POSITIONED) + ? XFLM_QUERY_AT_FIRST + : XFLM_QUERY_POSITIONED; + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + else + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped >= uiNumToSkip) + { + goto Exit; + } + else + { + bPassed = FALSE; + } + } + } + + // At this point we know that we failed to pass. If we + // passed evalExpr, though, we need to call it again so it + // can iterate through all possible values. No need to call + // it again if it was eliminated because it was a duplicate howerver. + + if (bPassedEval && !bEliminatedDup) + { + continue; + } + +Next_Index_Node: + + // Get the next node from the index. + // NOTE: m_pCurrDoc will be set to NULL if there are no + // more nodes to get from this particular predicate. + + bFirst = FALSE; + if (RC_BAD( rc = getNextIndexNode( &bFirst, TRUE, + m_pExprXPathSource, FALSE))) + { + goto Exit; + } + } + + // If we get here, the loop above failed to get + // anything. + + flmAssert( !m_pCurrDoc); + + // Try the next predicate. + + if (!useNextPredicate()) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + m_eState = XFLM_QUERY_AT_EOF; + goto Exit; + } + if (RC_BAD( rc = setupCurrPredicate( TRUE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the previous node from the index. +***************************************************************************/ +RCODE F_Query::prevFromIndex( + FLMBOOL bEvalCurrDoc, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bPassed; + FLMBOOL bLast; + + if (!bEvalCurrDoc) + { + bLast = FALSE; + if (RC_BAD( rc = getNextIndexNode( &bLast, FALSE, + m_pExprXPathSource, TRUE))) + { + goto Exit; + } + } + + for (;;) + { + + // If m_pCurrDoc is non-NULL, it means we are set up + // to call evalExpr. + + while (m_pCurrDoc) + { + FLMBOOL bPassedEval; + FLMBOOL bEliminatedDup; + + if (RC_BAD( rc = evalExpr( NULL, FALSE, TRUE, + m_pQuery, &bPassed, ppNode))) + { + if( rc == NE_XFLM_DOM_NODE_DELETED) + { + m_bResetAllXPaths = TRUE; + rc = NE_XFLM_OK; + goto Prev_Index_Node; + } + goto Exit; + } + bPassedEval = bPassed; + + if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) + { + goto Exit; + } + + if (bPassed) + { + m_eState = (m_eState == XFLM_QUERY_AT_EOF || + m_eState == XFLM_QUERY_NOT_POSITIONED) + ? XFLM_QUERY_AT_LAST + : XFLM_QUERY_POSITIONED; + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + else + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped >= uiNumToSkip) + { + goto Exit; + } + else + { + bPassed = FALSE; + } + } + } + + // At this point we know that we failed to pass. If we + // passed evalExpr, though, we need to call it again so it + // can iterate through all possible values. No need to call + // it again if it was eliminated because it was a duplicate howerver. + + if (bPassedEval && !bEliminatedDup) + { + continue; + } + +Prev_Index_Node: + + // Get the previous node from the index. + // NOTE: m_pCurrDoc will be set to NULL if there are no + // more nodes to get from this particular predicate. + + bLast = FALSE; + if (RC_BAD( rc = getNextIndexNode( &bLast, FALSE, + m_pExprXPathSource, FALSE))) + { + goto Exit; + } + } + + // If we get here, the loop above failed to get + // anything. + + flmAssert( !m_pCurrDoc); + + // Try the previous predicate. + + if (!usePrevPredicate()) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + m_eState = XFLM_QUERY_AT_BOF; + goto Exit; + } + if (RC_BAD( rc = setupCurrPredicate( FALSE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next/previous document from the index we are scanning. +***************************************************************************/ +RCODE F_Query::getDocFromIndexScan( + FLMBOOL bFirstLast, + FLMBOOL bForward + ) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector key; + FLMUINT64 ui64DocId; + + for (;;) + { + if (bForward) + { + rc = (RCODE)(bFirstLast + ? m_pFSIndexCursor->firstKey( m_pDb, &key) + : m_pFSIndexCursor->nextKey( m_pDb, &key, FALSE)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_eState = XFLM_QUERY_AT_EOF; + } + goto Exit; + } + } + else + { + rc = (RCODE)(bFirstLast + ? m_pFSIndexCursor->lastKey( m_pDb, &key) + : m_pFSIndexCursor->prevKey( m_pDb, &key, FALSE)); + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_eState = XFLM_QUERY_AT_BOF; + } + goto Exit; + } + } + m_pCurrOpt->ui64KeysRead++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + + // If we do not have a duplicate checking set, we can + // break out of the loop and get the document associated + // with this key. Otherwise, we need to see if the + // document associated with this key has already been + // processed. If so, we will go to the next key. + + if (!m_pDocIdSet) + { + break; + } + + // If we are eliminating duplicates, see if the document + // has already been processed. If so, skip the key. + + ui64DocId = key.getDocumentID(); + if (RC_BAD( rc = m_pDocIdSet->findMatch( &ui64DocId, NULL))) + { + if (rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + + // Document has not been processed. + + rc = NE_XFLM_OK; + break; + } + + // Document has already been passed, go to next/prev key. + + m_pCurrOpt->ui64KeyHadDupDoc++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + bFirstLast = FALSE; + } + + // Retrieve the document node. + + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, + key.getDocumentID(), + &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // If we cannot retrieve the node, we have a corruption + // in the database. + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + else + { + if (RC_BAD( rc = incrNodesRead())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the next node from scanning sequentially through documents. +***************************************************************************/ +RCODE F_Query::nextFromScan( + FLMBOOL bFirstDoc, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bPassed; + FLMBOOL bEliminatedDup; + + if (bFirstDoc || + (m_pQuery && !m_bRemoveDups && + m_pQuery->eNodeType == FLM_XPATH_NODE)) + { + goto Eval_Doc; + } + + // Read until we get a document/node that passes. + + for (;;) + { + if (m_bScan) + { + if (RC_BAD( rc = m_pCurrDoc->getNextDocument( m_pDb, &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + m_eState = XFLM_QUERY_AT_EOF; + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + } + else + { + if (RC_BAD( rc = getDocFromIndexScan( FALSE, TRUE))) + { + goto Exit; + } + } + m_bResetAllXPaths = TRUE; + +Eval_Doc: + + // See if the document passes. + + if (RC_BAD( rc = evalExpr( NULL, TRUE, TRUE, + m_pQuery, &bPassed, ppNode))) + { + if( rc == NE_XFLM_DOM_NODE_DELETED) + { + m_bResetAllXPaths = TRUE; + rc = NE_XFLM_OK; + continue; + } + + goto Exit; + } + + if (bPassed && m_bScanIndex) + { + m_pCurrOpt->ui64KeysPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + + if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) + { + goto Exit; + } + + if (bPassed) + { + m_eState = (m_eState == XFLM_QUERY_AT_BOF || + m_eState == XFLM_QUERY_NOT_POSITIONED) + ? XFLM_QUERY_AT_FIRST + : XFLM_QUERY_POSITIONED; + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + else + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped >= uiNumToSkip) + { + goto Exit; + } + else + { + bPassed = FALSE; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get the previous node from scanning sequentially through documents. +***************************************************************************/ +RCODE F_Query::prevFromScan( + FLMBOOL bLastDoc, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped, + IF_DOMNode ** ppNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bPassed; + FLMBOOL bEliminatedDup; + + if (bLastDoc || + (m_pQuery && !m_bRemoveDups && + m_pQuery->eNodeType == FLM_XPATH_NODE)) + { + goto Eval_Doc; + } + + // Read until we get a document/node that passes. + + for (;;) + { + + // Go to the previous document + + if (m_bScan) + { + if (RC_BAD( rc = m_pCurrDoc->getPreviousDocument( m_pDb, &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + m_eState = XFLM_QUERY_AT_BOF; + rc = RC_SET( NE_XFLM_BOF_HIT); + } + goto Exit; + } + } + else + { + if (RC_BAD( rc = getDocFromIndexScan( FALSE, FALSE))) + { + goto Exit; + } + } + + m_bResetAllXPaths = TRUE; + +Eval_Doc: + + // See if the document passes. + + if (RC_BAD( rc = evalExpr( NULL, FALSE, TRUE, + m_pQuery, &bPassed, ppNode))) + { + if( rc == NE_XFLM_DOM_NODE_DELETED) + { + m_bResetAllXPaths = TRUE; + rc = NE_XFLM_OK; + continue; + } + goto Exit; + } + + if (bPassed && m_bScanIndex) + { + m_pCurrOpt->ui64KeysPassed++; + if (RC_BAD( rc = queryStatus())) + { + goto Exit; + } + } + + if (RC_BAD( rc = testPassed( ppNode, &bPassed, &bEliminatedDup))) + { + goto Exit; + } + + if (bPassed) + { + m_eState = (m_eState == XFLM_QUERY_AT_EOF || + m_eState == XFLM_QUERY_NOT_POSITIONED) + ? XFLM_QUERY_AT_LAST + : XFLM_QUERY_POSITIONED; + if (puiNumSkipped) + { + (*puiNumSkipped)++; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + else + { + + // puiNumSkipped will always be non-NULL in the case + // where uiNumToSkip > 1 + + flmAssert( puiNumSkipped); + if (*puiNumSkipped >= uiNumToSkip) + { + goto Exit; + } + else + { + bPassed = FALSE; + } + } + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Get first node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::getFirst( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit + ) +{ + RCODE rc = NE_XFLM_OK; + + // If we are building on a background thread and this is not the + // background thread, we need to get the results from the result + // set that is being built. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getFirstFromResultSet( ifpDb, ppNode, uiTimeLimit); + goto Exit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + // If the query can never evaluate to TRUE, return EOF without + // doing anything. + + if (m_bEmpty) + { + m_eState = XFLM_QUERY_AT_EOF; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // See if we need to build a result set. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getFirstFromResultSet( ifpDb, ppNode, uiTimeLimit); + goto Exit; + } + + // Anytime we go back to the first, we must free whatever list + // of document IDs we have collected so far. + + if (m_bRemoveDups && m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } + + if ((m_uiTimeLimit = uiTimeLimit) != 0) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, m_uiTimeLimit); + m_uiStartTime = FLM_GET_TIMER(); + } + if (m_bScan) + { + + // Start at the beginning of the collection. + + if (RC_BAD( rc = m_pDb->getFirstDocument( m_uiCollection, &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + m_eState = XFLM_QUERY_AT_EOF; + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + if (RC_BAD( rc = nextFromScan( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + else if (m_bScanIndex) + { + if (RC_BAD( rc = getDocFromIndexScan( TRUE, TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = nextFromScan( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + else + { + m_pCurrContext = m_pQuery->pContext; + + // Position the context to lowest selected context, + // context path, and predicate. + + // NOTE: Because the m_bScan flag is not set, we are guaranteed + // that the following traversal scheme will not encounter any + // contexts, context paths, or predicates that require scanning, even + // though there may have been some during optimization. They cannot + // have been the selected contexts, context paths, or predicates + // without ultimately causing the m_bScan flag to have been set. + + useLeafContext( TRUE); + + // Setup predicate and get the first node. + + if (RC_BAD( rc = setupCurrPredicate( TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = nextFromIndex( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + +Exit: + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + + if (RC_BAD( rc)) + { + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + } + else + { + m_pCurrNode = *ppNode; + m_pCurrNode->AddRef(); + } + + m_uiTimeLimit = 0; + return( rc); +} + +/*************************************************************************** +Desc: Get last node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::getLast( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit + ) +{ + RCODE rc = NE_XFLM_OK; + + // If we are building on a background thread and this is not the + // background thread, we need to get the results from the result + // set that is being built. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getLastFromResultSet( ifpDb, ppNode, uiTimeLimit); + goto Exit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!m_bOptimized) + { + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + // If the query can never evaluate to TRUE, return EOF without + // doing anything. + + if (m_bEmpty) + { + m_eState = XFLM_QUERY_AT_BOF; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getLastFromResultSet( ifpDb, ppNode, uiTimeLimit); + goto Exit; + } + + // Anytime we go to the last, we must free whatever list + // of document IDs we have collected so far. + + if (m_bRemoveDups && m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } + + if ((m_uiTimeLimit = uiTimeLimit) != 0) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, m_uiTimeLimit); + m_uiStartTime = FLM_GET_TIMER(); + } + if (m_bScan) + { + + // Start at the end of the collection. + + if (RC_BAD( rc = m_pDb->getLastDocument( m_uiCollection, &m_pCurrDoc))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + m_eState = XFLM_QUERY_AT_BOF; + rc = RC_SET( NE_XFLM_BOF_HIT); + } + goto Exit; + } + if (RC_BAD( rc = prevFromScan( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + else if (m_bScanIndex) + { + if (RC_BAD( rc = getDocFromIndexScan( TRUE, FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = prevFromScan( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + else + { + m_pCurrContext = m_pQuery->pContext; + + // Position the context to lowest selected context, + // context path, and predicate. + + // NOTE: Because the m_bScan flag is not set, we are guaranteed + // that the following traversal scheme will not encounter any + // contexts, context paths, or predicates that require scanning, even + // though there may have been some during optimization. They cannot + // have been the selected contexts, context paths, or predicates + // without ultimately causing the m_bScan flag to have been set. + + useLeafContext( FALSE); + + // Setup predicate and get the last node. + + if (RC_BAD( rc = setupCurrPredicate( FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = prevFromIndex( TRUE, 0, NULL, ppNode))) + { + goto Exit; + } + } + +Exit: + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + + if (RC_BAD( rc)) + { + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + } + else + { + m_pCurrNode = *ppNode; + m_pCurrNode->AddRef(); + } + + m_uiTimeLimit = 0; + return( rc); +} + +/*************************************************************************** +Desc: Get next node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::getNext( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumSkipped; + + // If we are building on a background thread and this is not the + // background thread, we need to get the results from the result + // set that is being built. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getNextFromResultSet( ifpDb, ppNode, uiTimeLimit, uiNumToSkip, + puiNumSkipped); + goto Exit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!puiNumSkipped) + { + + // puiNumSkipped has to be non-NULL so it can be incremented only + // if uiNumToSkip > 1 + + if (uiNumToSkip > 1) + { + uiNumSkipped = 0; + puiNumSkipped = &uiNumSkipped; + } + } + else + { + *puiNumSkipped = 0; + } + switch (m_eState) + { + case XFLM_QUERY_AT_EOF: + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + case XFLM_QUERY_AT_BOF: + case XFLM_QUERY_NOT_POSITIONED: + if (RC_OK( rc = getFirst( ifpDb, ppNode, uiTimeLimit))) + { + if (puiNumSkipped) + { + *puiNumSkipped = 1; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + } + else + { + goto Exit; + } + break; + default: + if( !m_pCurrNode) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + goto Exit; + } + break; + } + + // Optimization has to already have occurred. + + flmAssert( m_bOptimized); + + // Make sure the passed in F_Db matches the one associated with + // the query. + + if (m_pDb->m_pDatabase != m_pDatabase) + { + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // If we have been positioned, we better have a current document and node + + flmAssert( m_pCurrDoc && m_pCurrNode); + + if ((m_uiTimeLimit = uiTimeLimit) != 0) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, m_uiTimeLimit); + m_uiStartTime = FLM_GET_TIMER(); + } + if (m_bScan || m_bScanIndex) + { + if (RC_BAD( rc = nextFromScan( FALSE, uiNumToSkip, + puiNumSkipped, ppNode))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = nextFromIndex( + (m_pQuery && !m_bRemoveDups && + m_pQuery->eNodeType == FLM_XPATH_NODE && + !m_pQuery->nd.pXPath->pLastComponent->bIsSource) + ? TRUE + : FALSE, uiNumToSkip, puiNumSkipped, ppNode))) + { + goto Exit; + } + } + +Exit: + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + + if (RC_BAD( rc)) + { + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + } + else + { + m_pCurrNode = *ppNode; + m_pCurrNode->AddRef(); + } + + m_uiTimeLimit = 0; + return( rc); +} + +/*************************************************************************** +Desc: Get previous node/document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::getPrev( + IF_Db * ifpDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiNumToSkip, + FLMUINT * puiNumSkipped + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumSkipped; + + // If we are building on a background thread and this is not the + // background thread, we need to get the results from the result + // set that is being built. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getPrevFromResultSet( ifpDb, ppNode, uiTimeLimit, uiNumToSkip, + puiNumSkipped); + goto Exit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + if (!puiNumSkipped) + { + + // puiNumSkipped has to be non-NULL so it can be incremented only + // if uiNumToSkip > 1 + + if (uiNumToSkip > 1) + { + uiNumSkipped = 0; + puiNumSkipped = &uiNumSkipped; + } + } + else + { + *puiNumSkipped = 0; + } + switch (m_eState) + { + case XFLM_QUERY_AT_BOF: + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + case XFLM_QUERY_AT_EOF: + case XFLM_QUERY_NOT_POSITIONED: + if (RC_OK( rc = getLast( ifpDb, ppNode, uiTimeLimit))) + { + if (puiNumSkipped) + { + *puiNumSkipped = 1; + } + if (uiNumToSkip <= 1) + { + goto Exit; + } + } + else + { + goto Exit; + } + break; + default: + if( !m_pCurrNode) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + goto Exit; + } + break; + } + + // Optimization has to already have occurred. + + flmAssert( m_bOptimized); + + // Make sure the passed in F_Db matches the one associated with + // the query. + + if (m_pDb->m_pDatabase != m_pDatabase) + { + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // If we have been positioned, we better have a current document and node + + flmAssert( m_pCurrDoc && m_pCurrNode); + + if ((m_uiTimeLimit = uiTimeLimit) != 0) + { + FLM_MILLI_TO_TIMER_UNITS( uiTimeLimit, m_uiTimeLimit); + m_uiStartTime = FLM_GET_TIMER(); + } + if (m_bScan || m_bScanIndex) + { + if (RC_BAD( rc = prevFromScan( FALSE, uiNumToSkip, + puiNumSkipped, ppNode))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = prevFromIndex( + (m_pQuery && !m_bRemoveDups && + m_pQuery->eNodeType == FLM_XPATH_NODE && + !m_pQuery->nd.pXPath->pLastComponent->bIsSource) + ? TRUE + : FALSE, uiNumToSkip, puiNumSkipped, ppNode))) + { + goto Exit; + } + } + +Exit: + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + + if (RC_BAD( rc)) + { + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + } + else + { + m_pCurrNode = *ppNode; + m_pCurrNode->AddRef(); + } + + m_uiTimeLimit = 0; + return( rc); +} + +/*************************************************************************** +Desc: Get current document that passes query expression. +***************************************************************************/ +RCODE XFLMAPI F_Query::getCurrent( + IF_Db * ifpDb, + IF_DOMNode ** ppNode) +{ + RCODE rc = NE_XFLM_OK; + + // If we are building on a background thread and this is not the + // background thread, we need to get the results from the result + // set that is being built. + + if ((m_pSortResultSet && m_uiBuildThreadId != f_threadId()) || + m_bResultSetPopulated) + { + rc = getCurrentFromResultSet( ifpDb, ppNode); + goto Exit; + } + + m_pDb = (F_Db *)ifpDb; + if (ppNode && *ppNode) + { + (*ppNode)->Release(); + *ppNode = NULL; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + switch (m_eState) + { + case XFLM_QUERY_AT_BOF: + case XFLM_QUERY_NOT_POSITIONED: + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + case XFLM_QUERY_AT_EOF: + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + default: + if( !m_pCurrNode) + { + rc = RC_SET( NE_XFLM_Q_NOT_POSITIONED); + goto Exit; + } + break; + } + + // Optimization has to already have occurred. + + flmAssert( m_bOptimized); + + // Make sure the passed in F_Db matches the one associated with + // the query. + + if (m_pDb->m_pDatabase != m_pDatabase) + { + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // If we have been positioned, we better have a current document and + // a current node id. + + flmAssert( m_pCurrDoc && m_pCurrNode); + + if( *ppNode) + { + (*ppNode)->Release(); + } + + *ppNode = m_pCurrNode; + (*ppNode)->AddRef(); + +Exit: + + if (RC_BAD( rc)) + { + if (m_pCurrDoc) + { + m_pCurrDoc->Release(); + m_pCurrDoc = NULL; + } + + if (m_pCurrNode) + { + m_pCurrNode->Release(); + m_pCurrNode = NULL; + } + } + + m_uiTimeLimit = 0; + return( rc); +} + +/*************************************************************************** +Desc: Get statistics and optimization information. +***************************************************************************/ +RCODE XFLMAPI F_Query::getStatsAndOptInfo( + FLMUINT * puiNumOptInfos, + XFLM_OPT_INFO ** ppOptInfo) +{ + RCODE rc = NE_XFLM_OK; + XFLM_OPT_INFO * pOptInfo; + FLMUINT uiOptInfoCount; + + if (!m_bOptimized) + { + *puiNumOptInfos = 0; + *ppOptInfo = NULL; + goto Exit; + } + + if (m_bScan || m_bEmpty) + { + if (RC_BAD( rc = f_alloc( sizeof( XFLM_OPT_INFO), ppOptInfo))) + { + goto Exit; + } + f_memcpy( *ppOptInfo, &m_scanOptInfo, sizeof( XFLM_OPT_INFO)); + *puiNumOptInfos = 1; + } + else + { + OP_CONTEXT * pSaveCurrContext = m_pCurrContext; + CONTEXT_PATH * pSaveCurrContextPath = m_pCurrContextPath; + PATH_PRED * pSaveCurrPred = m_pCurrPred; + FQNODE * pSaveExprXPathSource = m_pExprXPathSource; + + // Count the number of contexts. + + m_pCurrContext = m_pQuery->pContext; + *puiNumOptInfos = 0; + useLeafContext( TRUE); + do + { + if (m_pCurrPred->pNodeSource) + { + if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfoCount( + (IF_Db *)m_pDb, + &uiOptInfoCount))) + { + goto Exit; + } + (*puiNumOptInfos) += uiOptInfoCount; + } + else + { + (*puiNumOptInfos)++; + } + } while (useNextPredicate()); + + // Allocate the opt info array. + + if (RC_OK( rc = f_alloc( sizeof( XFLM_OPT_INFO) * (*puiNumOptInfos), + ppOptInfo))) + { + pOptInfo = *ppOptInfo; + m_pCurrContext = m_pQuery->pContext; + useLeafContext( TRUE); + do + { + if (m_pCurrPred->pNodeSource) + { + if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfoCount( + (IF_Db *)m_pDb, + &uiOptInfoCount))) + { + goto Exit; + } + if (RC_BAD( rc = m_pCurrPred->pNodeSource->getOptInfo( + (IF_Db *)m_pDb, + pOptInfo, uiOptInfoCount))) + { + goto Exit; + } + pOptInfo += uiOptInfoCount; + } + else + { + f_memcpy( pOptInfo, m_pCurrOpt, sizeof( XFLM_OPT_INFO)); + pOptInfo++; + } + } while (useNextPredicate()); + } + + // Restore the current predicate. + + m_pCurrContext = pSaveCurrContext; + m_pCurrContextPath = pSaveCurrContextPath; + m_pCurrPred = pSaveCurrPred; + m_pExprXPathSource = pSaveExprXPathSource; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Free the optimization info structure. +***************************************************************************/ +void XFLMAPI F_Query::freeStatsAndOptInfo( + XFLM_OPT_INFO ** ppOptInfo) +{ + if (*ppOptInfo) + { + f_free( ppOptInfo); + } +} + + +/**************************************************************************** +Desc: Create an empty query object and return it's interface... +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::createIFQuery( + IF_Query ** ppQuery) +{ + RCODE rc = NE_XFLM_OK; + + if ((*ppQuery = f_new F_Query) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Parse a query using the passed in query string. The uiQueryType + parameter is intended to identify the type of query syntax used. + The XPATH syntax is currently the only sypported type. +****************************************************************************/ +RCODE F_Query::setupQueryExpr( + FLMBOOL bUnicode, + IF_Db * ifpDb, + const void * pvQuery) +{ + RCODE rc = NE_XFLM_OK; + F_XPath XPath; + F_Db * pDb = (F_Db *)ifpDb; + + // Reset the query object totally. + + clearQuery(); + + if (!bUnicode) + { + if (RC_BAD( rc = XPath.parseQuery( pDb, (char *)pvQuery, this))) + { + goto Exit; + } + } + else + { + flmAssert( 0); + } + + // We need to make sure that from this point forward, we are using the same + // database object. + + m_pDatabase = pDb->m_pDatabase; + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Comparison function for comparing node ids. +****************************************************************************/ +FSTATIC int nodeIdCompareFunc( + void * pvData1, + void * pvData2, + void * // pvUserData + ) +{ + if (*((FLMUINT64 *)pvData1) < *((FLMUINT64 *)pvData2)) + { + return( -1); + } + else if (*((FLMUINT64 *)pvData1) > *((FLMUINT64 *)pvData2)) + { + return( 1); + } + else + { + return( 0); + } +} + +/**************************************************************************** +Desc: Allocate a result set for duplicate checking. +****************************************************************************/ +RCODE F_Query::allocDupCheckSet( void) +{ + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + char szTmpDir [F_PATH_MAX_SIZE]; + + if (m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } + + if ((m_pDocIdSet = f_new FDynSearchSet) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = dbSystem.getTempDir( szTmpDir))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if (!szTmpDir [0]) + { + if (RC_BAD( rc = gv_pFileSystem->pathReduce( + m_pDb->m_pDatabase->m_pszDbPath, szTmpDir, NULL))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pDocIdSet->setup( szTmpDir, sizeof( FLMUINT64)))) + { + goto Exit; + } + + m_pDocIdSet->setCompareFunc( nodeIdCompareFunc, NULL); + +Exit: + + if (RC_BAD( rc)) + { + if (m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Check to see if we have already passed the document that *ppNode + is in. If so, return FALSE in *pbPassed. +NOTE: This routine will return the root node of the document if it still + passes. +****************************************************************************/ +RCODE F_Query::checkIfDup( + IF_DOMNode ** ppNode, + FLMBOOL * pbPassed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64DocId; + + // If we have not yet allocated the result set, do it now. + + if (!m_pDocIdSet) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + // See if we can add the document id to the result set + + if( RC_BAD( rc = m_pCurrDoc->getNodeId( m_pDb, &ui64DocId))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDocIdSet->addEntry( &ui64DocId))) + { + if (rc == NE_XFLM_EXISTS) + { + *pbPassed = FALSE; + rc = NE_XFLM_OK; + m_pCurrOpt->ui64DupDocsEliminated++; + } + goto Exit; + } + + // When eliminating duplicates, we always return the root node + // of the document. + + (*ppNode)->Release(); + *ppNode = m_pCurrDoc; + (*ppNode)->AddRef(); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup duplicate handling for a query. +****************************************************************************/ +void XFLMAPI F_Query::setDupHandling( + FLMBOOL bRemoveDups + ) +{ + // Should not be able to change this after optimization has occurred. + + flmAssert( !m_bOptimized); + m_bRemoveDups = bRemoveDups; + if (!bRemoveDups && m_pDocIdSet) + { + m_pDocIdSet->Release(); + m_pDocIdSet = NULL; + } +} + +/**************************************************************************** +Desc: Set an index for the query. +****************************************************************************/ +RCODE XFLMAPI F_Query::setIndex( + FLMUINT uiIndex + ) +{ + RCODE rc = NE_XFLM_OK; + + // Cannot set the index if we have already optimized the query + + if (m_bOptimized) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + m_uiIndex = uiIndex; + m_bIndexSet = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set an index for the query. +****************************************************************************/ +RCODE XFLMAPI F_Query::getIndex( + IF_Db * ifpDb, + FLMUINT * puiIndex, + FLMBOOL * pbHaveMultiple + ) +{ + RCODE rc = NE_XFLM_OK; + + if (m_bIndexSet) + { + *puiIndex = m_uiIndex; + *pbHaveMultiple = FALSE; + goto Exit; + } + + // Optimize the query to determine the index. + + m_pDb = (F_Db *)ifpDb; + if (!m_bOptimized) + { + + if (m_pDatabase && m_pDb->m_pDatabase != m_pDatabase) + { + + // Make sure the passed in F_Db matches the one associated with + // the query. + + rc = RC_SET( NE_XFLM_Q_MISMATCHED_DB); + goto Exit; + } + + // See if the database is being forced to close + + if (RC_BAD( rc = m_pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // See if we have a transaction going which should be aborted. + + if (RC_BAD( m_pDb->m_AbortRc)) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + + // If we are not in a transaction, we cannot read. + + if (m_pDb->m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + if (RC_BAD( rc = optimize())) + { + goto Exit; + } + } + + *pbHaveMultiple = FALSE; + if (m_bScan || m_bEmpty) + { + *puiIndex = 0; + } + else + { + OP_CONTEXT * pSaveCurrContext = m_pCurrContext; + CONTEXT_PATH * pSaveCurrContextPath = m_pCurrContextPath; + PATH_PRED * pSaveCurrPred = m_pCurrPred; + FQNODE * pSaveExprXPathSource = m_pExprXPathSource; + + // See if more than one index is being used. + + m_pCurrContext = m_pQuery->pContext; + useLeafContext( TRUE); + *puiIndex = 0; + do + { + if (m_pCurrPred->pNodeSource) + { + FLMUINT uiIndex; + + if (RC_BAD( rc = m_pCurrPred->pNodeSource->getIndex( ifpDb, &uiIndex, + pbHaveMultiple))) + { + goto Exit; + } + if (uiIndex) + { + if (*puiIndex == 0) + { + *puiIndex = uiIndex; + } + if (*pbHaveMultiple) + { + break; + } + if (uiIndex != *puiIndex) + { + *pbHaveMultiple = TRUE; + break; + } + } + } + else if (m_pCurrOpt->uiIxNum) + { + if (*puiIndex == 0) + { + *puiIndex = m_pCurrOpt->uiIxNum; + } + else if (m_pCurrOpt->uiIxNum != *puiIndex) + { + *pbHaveMultiple = TRUE; + break; + } + } + } while (useNextPredicate()); + + // Restore the current predicate. + + m_pCurrContext = pSaveCurrContext; + m_pCurrContextPath = pSaveCurrContextPath; + m_pCurrPred = pSaveCurrPred; + m_pExprXPathSource = pSaveExprXPathSource; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make a copy of a value. +****************************************************************************/ +RCODE F_Query::copyValue( + FQVALUE * pDestValue, + FQVALUE * pSrcValue + ) +{ + RCODE rc = NE_XFLM_OK; + + pDestValue->eValType = pSrcValue->eValType; + pDestValue->uiFlags = pSrcValue->uiFlags; + + // Cannot copy stream values. + + flmAssert( !(pDestValue->uiFlags & VAL_IS_STREAM)); + pDestValue->uiDataLen = pSrcValue->uiDataLen; + switch (pDestValue->eValType) + { + case XFLM_BOOL_VAL: + pDestValue->val.eBool = pSrcValue->val.eBool; + break; + case XFLM_UINT_VAL: + pDestValue->val.uiVal = pSrcValue->val.uiVal; + break; + case XFLM_UINT64_VAL: + pDestValue->val.ui64Val = pSrcValue->val.ui64Val; + break; + case XFLM_INT_VAL: + pDestValue->val.iVal = pSrcValue->val.iVal; + break; + case XFLM_INT64_VAL: + pDestValue->val.i64Val = pSrcValue->val.i64Val; + break; + case XFLM_BINARY_VAL: + case XFLM_UTF8_VAL: + if (pDestValue->uiDataLen) + { + if (RC_BAD( rc = m_Pool.poolAlloc( pDestValue->uiDataLen, + (void **)&pDestValue->val.pucBuf))) + { + goto Exit; + } + f_memcpy( pDestValue->val.pucBuf, pSrcValue->val.pucBuf, + pDestValue->uiDataLen); + } + break; + default: + break; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make a copy of a value. +****************************************************************************/ +RCODE F_Query::copyXPath( + XPATH_COMPONENT * pXPathContext, + FQNODE * pDestNode, + FXPATH ** ppDestXPath, + FXPATH * pSrcXPath + ) +{ + RCODE rc = NE_XFLM_OK; + FXPATH * pDestXPath; + XPATH_COMPONENT * pXPathComponent; + XPATH_COMPONENT * pTmpXPathComponent; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FXPATH), + (void **)&pDestXPath))) + { + goto Exit; + } + *ppDestXPath = pDestXPath; + pXPathComponent = pSrcXPath->pFirstComponent; + while (pXPathComponent) + { + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( XPATH_COMPONENT), + (void **)&pTmpXPathComponent))) + { + goto Exit; + } + if ((pTmpXPathComponent->pPrev = pDestXPath->pLastComponent) != NULL) + { + pTmpXPathComponent->pPrev->pNext = pTmpXPathComponent; + } + else + { + pDestXPath->pFirstComponent = pTmpXPathComponent; + } + pDestXPath->pLastComponent = pTmpXPathComponent; + + pTmpXPathComponent->pXPathContext = pXPathContext; + pTmpXPathComponent->pXPathNode = pDestNode; + pTmpXPathComponent->eXPathAxis = pXPathComponent->eXPathAxis; + pTmpXPathComponent->eNodeType = pXPathComponent->eNodeType; + pTmpXPathComponent->uiDictNum = pXPathComponent->uiDictNum; + pTmpXPathComponent->uiContextPosNeeded = pXPathComponent->uiContextPosNeeded; + if (pXPathComponent->pNodeSource) + { + if (RC_BAD( rc = pXPathComponent->pNodeSource->copy( + &pTmpXPathComponent->pNodeSource))) + { + goto Exit; + } + + // Call objectAddRef to add the node source to the list of objects + // we have an AddRef on. Then call Release, because the copy() + // call above would have also done an AddRef() + + if (RC_BAD( rc = objectAddRef( pTmpXPathComponent->pNodeSource))) + { + goto Exit; + } + pTmpXPathComponent->pNodeSource->Release(); + } + if (pXPathComponent->pContextPosExpr) + { + if (RC_BAD( rc = copyExpr( pTmpXPathComponent, + &pTmpXPathComponent->pContextPosExpr, + pXPathComponent->pContextPosExpr))) + { + goto Exit; + } + } + if (pXPathComponent->pExpr) + { + if (RC_BAD( rc = copyExpr( pTmpXPathComponent, + &pTmpXPathComponent->pExpr, + pXPathComponent->pExpr))) + { + goto Exit; + } + } + pXPathComponent = pXPathComponent->pNext; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make a copy of a function. +****************************************************************************/ +RCODE F_Query::copyFunction( + XPATH_COMPONENT * pXPathContext, + FQFUNCTION ** ppDestFunc, + FQFUNCTION * pSrcFunc + ) +{ + RCODE rc = NE_XFLM_OK; + FQFUNCTION * pDestFunc; + FQEXPR * pExpr; + FQEXPR * pTmpExpr; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQFUNCTION), + (void **)&pDestFunc))) + { + goto Exit; + } + *ppDestFunc = pDestFunc; + pDestFunc->eFunction = pSrcFunc->eFunction; + if (pSrcFunc->pFuncObj) + { + + // Need to clone the object, because it may have state info. + // on where it is at in iterating through values. + + if (RC_BAD( pSrcFunc->pFuncObj->cloneSelf( &pDestFunc->pFuncObj))) + { + goto Exit; + } + if (RC_BAD( rc = objectAddRef( pDestFunc->pFuncObj))) + { + goto Exit; + } + + // Need to release once because cloneSelf will have done + // an AddRef() - only need the AddRef done by objectAddRef() + + pDestFunc->pFuncObj->Release(); + } + + pExpr = pSrcFunc->pFirstArg; + while (pExpr) + { + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQEXPR), + (void **)&pTmpExpr))) + { + goto Exit; + } + if ((pTmpExpr->pPrev = pDestFunc->pLastArg) != NULL) + { + pTmpExpr->pPrev->pNext = pTmpExpr; + } + else + { + pDestFunc->pFirstArg = pTmpExpr; + } + pDestFunc->pLastArg = pTmpExpr; + if (RC_BAD( rc = copyExpr( pXPathContext, &pTmpExpr->pExpr, + pExpr->pExpr))) + { + goto Exit; + } + pExpr = pExpr->pNext; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make a copy of a node. +****************************************************************************/ +RCODE F_Query::copyNode( + XPATH_COMPONENT * pXPathContext, + FQNODE ** ppDestNode, + FQNODE * pSrcNode + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pDestNode; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( FQNODE), (void **)&pDestNode))) + { + goto Exit; + } + *ppDestNode = pDestNode; + pDestNode->eNodeType = pSrcNode->eNodeType; + pDestNode->bNotted = pSrcNode->bNotted; + switch (pSrcNode->eNodeType) + { + case FLM_OPERATOR_NODE: + pDestNode->nd.op.eOperator = pSrcNode->nd.op.eOperator; + pDestNode->nd.op.uiCompareRules = pSrcNode->nd.op.uiCompareRules; + pDestNode->nd.op.pOpComparer = pSrcNode->nd.op.pOpComparer; + if (pDestNode->nd.op.pOpComparer) + { + if (RC_BAD( rc = objectAddRef( pDestNode->nd.op.pOpComparer))) + { + goto Exit; + } + } + break; + case FLM_VALUE_NODE: + if (RC_BAD( rc = copyValue( &pDestNode->currVal, + &pSrcNode->currVal))) + { + goto Exit; + } + break; + case FLM_XPATH_NODE: + if (RC_BAD( rc = copyXPath( pXPathContext, pDestNode, + &pDestNode->nd.pXPath, + pSrcNode->nd.pXPath))) + { + goto Exit; + } + break; + case FLM_FUNCTION_NODE: + if (RC_BAD( rc = copyFunction( pXPathContext, + &pDestNode->nd.pQFunction, + pSrcNode->nd.pQFunction))) + { + goto Exit; + } + break; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copy an expression +****************************************************************************/ +RCODE F_Query::copyExpr( + XPATH_COMPONENT * pXPathContext, + FQNODE ** ppDestExpr, + FQNODE * pSrcExpr + ) +{ + RCODE rc = NE_XFLM_OK; + FQNODE * pTmpQNode; + FQNODE * pQNode = pSrcExpr; + FQNODE * pParent = NULL; + FQNODE * pPrevSib = NULL; + + if (!pQNode) + { + *ppDestExpr = NULL; + goto Exit; // rc = NE_XFLM_OK; + } + + for (;;) + { + if (RC_BAD( rc = copyNode( pXPathContext, &pTmpQNode, pQNode))) + { + goto Exit; + } + if (!(*ppDestExpr)) + { + *ppDestExpr = pTmpQNode; + } + pTmpQNode->pParent = pParent; + if (pParent) + { + if ((pTmpQNode->pPrevSib = pPrevSib) == NULL) + { + pParent->pFirstChild = pTmpQNode; + } + else + { + pParent->pLastChild = pTmpQNode; + } + } + if (pQNode->pFirstChild) + { + pParent = pTmpQNode; + pPrevSib = NULL; + pQNode = pQNode->pFirstChild; + } + else + { + while (!pQNode->pNextSib) + { + pParent = pTmpQNode->pParent; + if ((pQNode = pQNode->pParent) == NULL) + { + + flmAssert( !pParent); + break; + } + } + if (!pQNode) + { + break; + } + pQNode = pQNode->pNextSib; + pPrevSib = pParent; + pParent = pPrevSib->pParent; + } + } + + if (RC_BAD( rc = getPredicates( ppDestExpr, NULL, pXPathContext))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copy criteria from another query object. +****************************************************************************/ +RCODE XFLMAPI F_Query::copyCriteria( + IF_Query * pSrcQuery + ) +{ + RCODE rc = NE_XFLM_OK; + EXPR_STATE * pExprState = ((F_Query *)pSrcQuery)->m_pCurExprState; + + // Verify that the source query is in a "copyable" state + + if (pExprState) + { + if (pExprState->pPrev || pExprState->uiNestLevel || + (pExprState->pLastNode && + pExprState->pLastNode->eNodeType == FLM_OPERATOR_NODE)) + { + rc = RC_SET( NE_XFLM_Q_INCOMPLETE_QUERY_EXPR); + goto Exit; + } + } + + // Clear out the existing query, if any. + + clearQuery(); + + if (RC_BAD( rc = copyExpr( NULL, &m_pQuery, + ((F_Query *)pSrcQuery)->m_pQuery))) + { + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version5/src/fquery.h b/version5/src/fquery.h new file mode 100644 index 0000000..5839b50 --- /dev/null +++ b/version5/src/fquery.h @@ -0,0 +1,314 @@ +//------------------------------------------------------------------------------ +// Desc: Contains defines, structures and prototypes for FLAIM cursors. +// +// Tabs: 3 +// +// Copyright (c) 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: fquery.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FQUERY_H +#define FQUERY_H + +#define FLM_MAX_POS_KEYS 1000 +#define FLM_MIN_POS_KEYS 250 +#define FLM_ADDR_GROW_SIZE 100 +#define FLM_KEYS_GROW_SIZE 100 + +/**************************************************************************** +Macro: +Desc: Macros used for determining the nature and precedence of OP codes. +****************************************************************************/ + +class F_DataVector; +class FSIndexCursor; +class FSDataCursor; +class F_CollIStream; + +typedef struct QueryValue * FQVALUE_p; +typedef struct QueryXPathPath * FXPATH_p; +typedef struct XPathComponent * XPATH_COMPONENT_p; +typedef struct QueryExpr * FQEXPR_p; +typedef struct QueryNode * FQNODE_p; + +typedef enum QueryNodeTypes +{ + FLM_OPERATOR_NODE = 0, + FLM_VALUE_NODE, + FLM_XPATH_NODE, + FLM_FUNCTION_NODE +} eNodeTypes; + +/**************************************************************************** +Structures used for the query tree and other stuff +****************************************************************************/ + +typedef struct QueryValue +{ + eValTypes eValType; + FLMUINT uiFlags; +#define VAL_IS_STREAM 0x0001 +#define VAL_IS_CONSTANT 0x0002 // During query evaluation, this indicates + // that this value is a constant. If it + // is a FLM_UTF8_VAL, then asterisks will + // be treated as a wildcard, unless + // escaped (\*). If the value is NOT + // a constant, the asterisk is NEVER + // treated as a wildcard, and the + // backslash is NEVER treated as an + // escape character. +#define VAL_HAS_WILDCARDS 0x0004 // This is only set if the value is a + // constant, FLM_UTF8_VAL, that has + // wildcards. + FLMUINT uiDataLen; // Length in bytes if the type is text + // or binary + union + { + XFlmBoolType eBool; + FLMUINT uiVal; + FLMUINT64 ui64Val; + FLMINT iVal; + FLMINT64 i64Val; + FLMBYTE * pucBuf; + IF_PosIStream * pIStream; + } val; // Holds or points to the atom value. +} FQVALUE; + +/*************************************************************************** +Desc: Can two values be compared? +***************************************************************************/ +FINLINE FLMBOOL fqCanCompare( + FQVALUE * pValue1, + FQVALUE * pValue2 + ) +{ + if (!pValue1 || !pValue2 || + pValue1->eValType == pValue2->eValType) + { + return( TRUE); + } + else + { + switch (pValue1->eValType) + { + case XFLM_UINT_VAL: + case XFLM_UINT64_VAL: + case XFLM_INT_VAL: + case XFLM_INT64_VAL: + return( (FLMBOOL)(pValue2->eValType == XFLM_UINT_VAL || + pValue2->eValType == XFLM_UINT64_VAL || + pValue2->eValType == XFLM_INT_VAL || + pValue2->eValType == XFLM_INT64_VAL + ? TRUE + : FALSE)); + default: + return( FALSE); + } + } +} + +typedef struct QueryExpr +{ + FQNODE_p pExpr; + FQEXPR_p pNext; + FQEXPR_p pPrev; +} FQEXPR; + +typedef struct PathPred * PATH_PRED_p; +typedef struct PathPredNode * PATH_PRED_NODE_p; + +typedef struct XPathComponent +{ + FLMBOOL bIsSource; // Indicates component is query source + PATH_PRED_p pOptPred; // Optimization predicate + IF_DOMNode * pCurrNode; // Used when evaluating expressions + IF_DOMNode * pKeyNode; + XPATH_COMPONENT_p pXPathContext; + XPATH_COMPONENT_p pNext; + XPATH_COMPONENT_p pPrev; + FQNODE_p pXPathNode; + eXPathAxisTypes eXPathAxis; + eDomNodeType eNodeType; + IF_QueryNodeSource * pNodeSource; + FLMUINT uiDictNum; + FLMUINT uiContextPosNeeded; + FQNODE_p pContextPosExpr; + FQNODE_p pExpr; + FQNODE_p pExprXPathSource; // XPATH node that is expression's source +} XPATH_COMPONENT; + +typedef struct QueryXPath +{ + FLMBOOL bGettingNodes; // Used when evaluating expressions + FLMBOOL bIsSource; // Indicates XPATH is query source + XPATH_COMPONENT_p pSourceComponent; // Used when XPATH is query source + FLMBOOL bHavePassingNode; + XPATH_COMPONENT_p pFirstComponent; + XPATH_COMPONENT_p pLastComponent; +} FXPATH; + +typedef struct PathPredNode +{ + FQNODE_p pXPathNode; + PATH_PRED_NODE_p pNext; +} PATH_PRED_NODE; + +typedef struct PathPred +{ + PATH_PRED_NODE * pXPathNodeList;// List of XPATHs sharing this predicate. + eQueryOperators eOperator; // Operator of the predicate + FLMUINT uiCompareRules;// Comparison rules + IF_OperandComparer * pOpComparer; // Function to perform comparison + FQNODE_p pContextNode; // Context node for this predicate, if + // any + FLMBOOL bNotted; // Has operator been notted? + FQVALUE * pFromValue; // Points to FQVALUE that has the FROM value for + // this predicate. Will be NULL for unary + // operators such as exists + FLMBOOL bInclFrom; // Flag indicating if the from value is + // inclusive. + FQVALUE * pUntilValue; // Points to FQValue that has the UNTIL value + // for this predicate. + FLMBOOL bInclUntil; // Flag indicating if until value is + // inclusive. + XFLM_OPT_INFO OptInfo; // Optimization information. + FSIndexCursor * pFSIndexCursor;// Used if OptInfo.eOptType is + // QOPT_USING_INDEX + FSCollectionCursor * pFSCollectionCursor;// Used if OptInfo.eOptType is + // QOPT_USING_NODE_ID + IF_QueryNodeSource * pNodeSource; // Used if OptInfo.eOptType is + // QOPT_USING_APP_SOURCE + PATH_PRED_p pNext; + PATH_PRED_p pPrev; +} PATH_PRED; + +typedef struct CONTEXT_PATH +{ + XPATH_COMPONENT * pXPathComponent; + FLMUINT uiCost; + FLMBOOL bMustScan; + PATH_PRED * pSelectedPred; // Only used for intersect contexts + PATH_PRED * pFirstPred; + PATH_PRED * pLastPred; + CONTEXT_PATH * pNext; + CONTEXT_PATH * pPrev; +} CONTEXT_PATH; + +typedef struct OP_CONTEXT +{ + FLMBOOL bIntersect; + FLMBOOL bMustScan; + FLMBOOL bForceOptToScan; + FQNODE_p pQRootNode; // Root node of this context. + FLMUINT uiCost; + OP_CONTEXT * pSelectedChild; // Only used for intersect contexts + CONTEXT_PATH * pSelectedPath; // Only used for intersect contexts + OP_CONTEXT * pParent; + OP_CONTEXT * pFirstChild; + OP_CONTEXT * pLastChild; + OP_CONTEXT * pNextSib; + OP_CONTEXT * pPrevSib; + CONTEXT_PATH * pFirstPath; + CONTEXT_PATH * pLastPath; +} OP_CONTEXT; + +typedef struct QueryFunction +{ + eQueryFunctions eFunction; + IF_QueryValFunc * pFuncObj; + FQEXPR_p pFirstArg; + FQEXPR_p pLastArg; +} FQFUNCTION; + +typedef struct QueryOperator +{ + eQueryOperators eOperator; + FLMUINT uiCompareRules; + IF_OperandComparer * pOpComparer; +} FQOPERATOR; + +typedef struct QueryNode +{ + eNodeTypes eNodeType; // Type of node this is + FLMUINT uiNestLevel; // Nesting level of node - only used when + // setting up the query + OP_CONTEXT * pContext; + FQVALUE currVal; // Current value - used during evaluation + // and for value types + FLMBOOL bUsedValue; // Used during evaluation + FLMBOOL bLastValue; // Used during evaluation + FLMBOOL bNotted; + FQNODE_p pParent; // Parent of this query node + FQNODE_p pPrevSib; // Previous sibling of this query node + FQNODE_p pNextSib; // Next sibling of this query node + FQNODE_p pFirstChild; // First child of this query node + FQNODE_p pLastChild; // Last child of this query node + union + { + FQOPERATOR op; + FQFUNCTION * pQFunction; + FXPATH * pXPath; + } nd; +} FQNODE; + +RCODE fqCompare( // fqeval.cpp + FQVALUE * pValue1, + FQVALUE * pValue2, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FLMUINT uiLanguage, + FLMINT * piCmp); + +RCODE fqCompareOperands( // fqeval.cpp + FLMUINT uiLanguage, + FQVALUE * pLValue, + FQVALUE * pRValue, + eQueryOperators eOperator, + FLMUINT uiCompareRules, + IF_OperandComparer * pOpComparer, + FLMBOOL bNotted, + XFlmBoolType * peBool); + +RCODE fqArithmeticOperator( // fqeval.cpp + FQVALUE * pLValue, + FQVALUE * pRValue, + eQueryOperators eOperator, + FQVALUE * pResult); + +RCODE fqCompareCollStreams( // fqeval.cpp + F_CollIStream * pLStream, + F_CollIStream * pRStream, + FLMBOOL bOpIsMatch, + FLMUINT uiLanguage, + FLMINT * piResult); + +RCODE flmBuildFromAndUntilKeys( // kybldkey.cpp + IXD * pIxd, + PATH_PRED * pPred, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbDoNodeMatch, + FLMBOOL * pbCanCompareOnKey); + +#endif // FQUERY_H + diff --git a/version5/src/frecread.cpp b/version5/src/frecread.cpp new file mode 100644 index 0000000..b239a5d --- /dev/null +++ b/version5/src/frecread.cpp @@ -0,0 +1,5718 @@ +//------------------------------------------------------------------------------ +// Desc: Routines for reading records from FLXIM 4.x databases +// +// 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: frecread.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +static FLMBYTE gv_ucMaxBcdINT32[] = {0x21, 0x47, 0x48, 0x36, 0x47}; +static FLMBYTE gv_ucMinBcdINT32[] = {0xB2, 0x14, 0x74, 0x83, 0x64, 0x8F}; +static FLMBYTE gv_ucMaxBcdUINT32[] = {0x42, 0x94, 0x96, 0x72, 0x95}; + +typedef struct +{ + const char * pszTagName; + FLMUINT uiTagNum; + FLMUINT uiFieldType; +} FLM_4x_DICT_TAG_INFO; + +FLM_4x_DICT_TAG_INFO Flm4xDictTagInfo[] = +{ + {FLM_4x_FIELD_TAG_NAME, FLM_4x_FIELD_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_INDEX_TAG_NAME, FLM_4x_INDEX_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_TYPE_TAG_NAME, FLM_4x_TYPE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_CONTAINER_TAG_NAME, FLM_4x_CONTAINER_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_LANGUAGE_TAG_NAME, FLM_4x_LANGUAGE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_OPTIONAL_TAG_NAME, FLM_4x_OPTIONAL_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_UNIQUE_TAG_NAME, FLM_4x_UNIQUE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_KEY_TAG_NAME, FLM_4x_KEY_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_REFS_TAG_NAME, FLM_4x_REFS_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_AREA_TAG_NAME, FLM_4x_AREA_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_STATE_TAG_NAME, FLM_4x_STATE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_BLOB_TAG_NAME, FLM_4x_BLOB_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_THRESHOLD_TAG_NAME, FLM_4x_THRESHOLD_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_SUFFIX_TAG_NAME, FLM_4x_SUFFIX_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_SUBDIRECTORY_TAG_NAME, FLM_4x_SUBDIRECTORY_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_RESERVED_TAG_NAME, FLM_4x_RESERVED_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_SUBNAME_TAG_NAME, FLM_4x_SUBNAME_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_NAME_TAG_NAME, FLM_4x_NAME_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_BASE_TAG_NAME, FLM_4x_BASE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_CASE_TAG_NAME, FLM_4x_CASE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_COMBINATIONS_TAG_NAME, FLM_4x_COMBINATIONS_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_COUNT_TAG_NAME, FLM_4x_COUNT_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_POSITIONING_TAG_NAME, FLM_4x_POSITIONING_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_PAIRED_TAG_NAME, FLM_4x_PAIRED_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_PARENT_TAG_NAME, FLM_4x_PARENT_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_POST_TAG_NAME, FLM_4x_POST_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_REQUIRED_TAG_NAME, FLM_4x_REQUIRED_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_USE_TAG_NAME, FLM_4x_USE_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_FILTER_TAG_NAME, FLM_4x_FILTER_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_LIMIT_TAG_NAME, FLM_4x_LIMIT_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_DICT_TAG_NAME, FLM_4x_DICT_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_RECINFO_TAG_NAME, FLM_4x_RECINFO_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_DRN_TAG_NAME, FLM_4x_DRN_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_DICT_SEQ_TAG_NAME, FLM_4x_DICT_SEQ_TAG, FLM_4x_TEXT_TYPE}, + {FLM_4x_LAST_CONTAINER_INDEXED_TAG_NAME, FLM_4x_LAST_CONTAINER_INDEXED_TAG, FLM_4x_NUMBER_TYPE}, + {FLM_4x_LAST_DRN_INDEXED_TAG_NAME, FLM_4x_LAST_DRN_INDEXED_TAG, FLM_4x_NUMBER_TYPE}, + {FLM_4x_ONLINE_TRANS_ID_TAG_NAME, FLM_4x_ONLINE_TRANS_ID_TAG, FLM_4x_NUMBER_TYPE}, + {NULL, 0} +}; + +/*************************************************************************** +Desc: +****************************************************************************/ +F_4xReader::F_4xReader() +{ + m_tmpPool.poolInit( 32 * 1024); + m_pSuperHdl = NULL; + m_pLckFile = NULL; + m_uiMaxFileSize = 0; + m_pLFileTbl = NULL; + m_uiLFileCnt = 0; + m_uiFieldTblSize = 0; + m_puiFieldTbl = NULL; + m_ppBlockTbl = NULL; + m_uiBlockTblSize = 0; + m_uiDefaultContainer = 0; + m_pNameTable = NULL; + f_memset( &m_fileHdr, 0, sizeof( F_4x_FILE_HDR)); + f_memset( &m_logHdr, 0, sizeof( F_4x_LOG_HDR)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +F_4xReader::~F_4xReader() +{ + closeDatabase(); + m_tmpPool.poolFree(); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::openDatabase( + char * pszPath) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucReadBuf = NULL; + FLMBYTE * pucPrefix; + FLMBYTE * pucFileHdr; + FLMBYTE * pucLogHdr; + FLMUINT uiBytesRead; + FLMUINT uiTmp; + F_FileHdl * pFileHdl = NULL; + + flmAssert( !m_pSuperHdl); + + if( (m_pSuperHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pSuperHdl->Setup( NULL, pszPath, NULL))) + { + goto Exit; + } + + // We must have exclusive access. + + if( RC_BAD( rc = createLckFile( pszPath))) + { + goto Exit; + } + + // Read and verify the file and log headers. + + if( RC_BAD( rc = m_pSuperHdl->GetFileHdl( 0, FALSE, + (IF_FileHdl **)&pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = f_alloc( 2048, &pucReadBuf))) + { + goto Exit; + } + + // Read the fixed information area + + if( RC_BAD( rc = pFileHdl->Read( 0, 2048, pucReadBuf, &uiBytesRead))) + { + goto Exit; + } + + *pucReadBuf = 0xFF; + pucPrefix = pucReadBuf; + pucFileHdr = &pucReadBuf[ FLM_4x_FLAIM_HEADER_START]; + + // Make sure we have a valid prefix + + if( pucPrefix[ 1] != f_toascii('W') || + pucPrefix[ 2] != f_toascii('P') || + pucPrefix[ 3] != f_toascii('C')) + { + rc = RC_SET( NE_XFLM_NOT_FLAIM); + goto Exit; + } + + // Extract the file header info + + m_fileHdr.uiBlockSize = (FLMUINT)FB2UW( &pucFileHdr[ FLM_4x_DB_BLOCK_SIZE]); + m_fileHdr.uiAppMajorVer = pucPrefix[ 10]; + m_fileHdr.uiAppMinorVer = pucPrefix[ 11]; + m_fileHdr.uiDefaultLanguage = pucFileHdr[ FLM_4x_DB_DEFAULT_LANGUAGE]; + m_fileHdr.uiVersionNum = + ((FLMUINT16)(pucFileHdr[ FLM_4x_VER_POS] - ASCII_ZERO) * 100 + + (FLMUINT16)(pucFileHdr[ FLM_4x_MINOR_VER_POS] - ASCII_ZERO) * 10 + + (FLMUINT16)(pucFileHdr[ FLM_4x_SMINOR_VER_POS] - ASCII_ZERO)); + + // Is the block size valid? + + if( m_fileHdr.uiBlockSize != 4096 && + m_fileHdr.uiBlockSize != 8192) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Supported version? + + switch( m_fileHdr.uiVersionNum) + { + case FLM_VER_4_0: + case FLM_VER_4_3: + case FLM_VER_4_31: + case FLM_VER_4_50: + case FLM_VER_4_51: + break; + default: + rc = RC_SET( NE_XFLM_UNSUPPORTED_VERSION); + goto Exit; + } + + // Get other log header elements. + + m_fileHdr.uiFirstLFHBlkAddr = + (FLMUINT)FB2UD( &pucFileHdr[ FLM_4x_DB_1ST_LFH_ADDR]); + + if( pucFileHdr[ FLM_4x_FLAIM_NAME_POS ] != f_toascii( 'F') || + pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 1 ] != f_toascii( 'L') || + pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 2 ] != f_toascii( 'A') || + pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 3 ] != f_toascii( 'I') || + pucFileHdr[ FLM_4x_FLAIM_NAME_POS + 4 ] != f_toascii( 'M')) + { + rc = RC_SET( NE_XFLM_NOT_FLAIM); + goto Exit; + } + + // Set up the uiSigBitsInBlkSize member of the file + // header + + m_fileHdr.uiSigBitsInBlkSize = 0; + uiTmp = m_fileHdr.uiBlockSize; + while( !(uiTmp & 0x0001)) + { + m_fileHdr.uiSigBitsInBlkSize++; + uiTmp >>= 1; + } + + // Get the log file header information + + pucLogHdr = &pucReadBuf[ FLM_4x_DB_LOG_HEADER_START]; + + // Verify the checksums in the log header + + if( lgHdrCheckSum( pucLogHdr, TRUE) != 0) + { + rc = RC_SET( NE_XFLM_BLOCK_CRC); + goto Exit; + } + + m_logHdr.uiCurrTransID = + (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_CURR_TRANS_ID]); + + m_logHdr.uiLogicalEOF = + (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_LOGICAL_EOF]); + + m_logHdr.uiFirstAvailBlkAddr = + (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PF_AVAIL_BLKS]); + + m_logHdr.uiAvailBlkCount = + (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PF_NUM_AVAIL_BLKS]); + + // Get the maximum file size + + if( m_fileHdr.uiVersionNum >= FLM_VER_4_3) + { + m_uiMaxFileSize = (FLMUINT)(FB2UW(&((pucLogHdr)[ + FLM_4x_LOG_MAX_FILE_SIZE]))) << 16; + } + else + { + m_uiMaxFileSize = 0x7FF00000; + } + + // Make sure that no recovery needs to be done. + + if( (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_ROLLBACK_EOF]) != + m_fileHdr.uiBlockSize || + (FLMUINT)FB2UD( &pucLogHdr[ FLM_4x_LOG_PL_FIRST_CP_BLOCK_ADDR])) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Set the block size + + m_pSuperHdl->ReleaseFile( (FLMUINT)0, TRUE); + m_pSuperHdl->SetBlockSize( m_fileHdr.uiBlockSize); + + // Set up the block table + + m_uiBlockTblSize = 1024; + if( RC_BAD( rc = f_calloc( + sizeof( F_Block *) * m_uiBlockTblSize, &m_ppBlockTbl))) + { + m_uiBlockTblSize = 0; + goto Exit; + } + + // Set the default container + + m_uiDefaultContainer = FLM_4x_DATA_CONTAINER;; + + // Read the LFile table + + if( RC_BAD( rc = readLFiles())) + { + goto Exit; + } + + // Read the dictionary + + if( RC_BAD( rc = readDictionary())) + { + goto Exit; + } + +Exit: + + if( pFileHdl) + { + m_pSuperHdl->ReleaseFile( (FLMUINT)0, TRUE); + } + + if( pucReadBuf) + { + f_free( &pucReadBuf); + } + + if( RC_BAD( rc)) + { + closeDatabase(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FLMUINT F_4xReader::lgHdrCheckSum( + FLMBYTE * pucLogHdr, + FLMBOOL bCompare) +{ + FLMUINT uiCnt; + FLMUINT uiTempSum; + FLMUINT uiCurrCheckSum; + FLMUINT uiTempSum2; + FLMUINT uiBytesToChecksum; + + uiBytesToChecksum = (FB2UW( &pucLogHdr[ + FLM_4x_LOG_FLAIM_VERSION]) < FLM_VER_4_3) + ? 88 + : 156; + + if( (uiCurrCheckSum = (FLMUINT)FB2UW( + &pucLogHdr[ FLM_4x_LOG_HDR_CHECKSUM])) == 0xFFFF) + { + uiCurrCheckSum = 0; + } + + if( bCompare && !uiCurrCheckSum) + { + return( 0); + } + + for( uiTempSum = 0 - (FLMUINT)FB2UW( &pucLogHdr[ FLM_4x_LOG_HDR_CHECKSUM]), + uiCnt = 1 + uiBytesToChecksum / sizeof( FLMUINT16); --uiCnt != 0;) + { + uiTempSum += (FLMUINT)FB2UW( pucLogHdr); + pucLogHdr += sizeof( FLMUINT16); + } + + if( (0 == (uiTempSum2 = (uiTempSum & 0xFFFF))) || (uiTempSum2 == 0xFFFF)) + { + uiTempSum2 = 1; + } + + return( (FLMUINT)(((bCompare) && (uiTempSum2 == uiCurrCheckSum)) + ? (FLMUINT)0 + : uiTempSum2) ); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::createLckFile( + char * pszFilePath) +{ + RCODE rc = NE_XFLM_OK; + char szLockPath[ F_PATH_MAX_SIZE]; + char szDbBaseName[ F_FILENAME_SIZE]; + char * pszFileExt; + F_FileHdl * pLockFileHdl = NULL; + + // Extract the 8.3 name and put a .lck extension on it to create + // the full path for the .lck file. + + if( RC_BAD( rc = gv_pFileSystem->pathReduce( + pszFilePath, szLockPath, szDbBaseName))) + { + goto Exit; + } + + pszFileExt = &szDbBaseName[ 0]; + + while( (*pszFileExt) && (*pszFileExt != '.')) + { + pszFileExt++; + } + + f_strcpy( pszFileExt, ".lck"); + + if (RC_BAD( rc = gv_pFileSystem->pathAppend( szLockPath, szDbBaseName))) + { + goto Exit; + } + + if( (pLockFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_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, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) + { +#ifndef FLM_UNIX + if (RC_BAD( gv_pFileSystem->Delete( szLockPath))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + else if (RC_BAD( pLockFileHdl->Create( szLockPath, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#else + + if( RC_BAD( pLockFileHdl->Open( szLockPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYRW))) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + } + +#ifdef FLM_UNIX + if( RC_BAD( pLockFileHdl->Lock())) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } +#endif + + m_pLckFile = pLockFileHdl; + pLockFileHdl = NULL; + +Exit: + + if (pLockFileHdl) + { + pLockFileHdl->Close(); + pLockFileHdl->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void F_4xReader::closeDatabase( void) +{ + FLMUINT uiLoop; + + m_tmpPool.poolReset( NULL); + + if( m_pLckFile) + { + m_pLckFile->Release(); + m_pLckFile = NULL; + } + + if( m_pSuperHdl) + { + m_pSuperHdl->Release(); + } + + if( m_pLFileTbl) + { + f_free( &m_pLFileTbl); + m_pLFileTbl = NULL; + } + m_uiLFileCnt = 0; + + if( m_puiFieldTbl) + { + f_free( &m_puiFieldTbl); + m_puiFieldTbl = NULL; + } + m_uiFieldTblSize = 0; + + if( m_ppBlockTbl) + { + for( uiLoop = 0; uiLoop < m_uiBlockTblSize; uiLoop++) + { + if( m_ppBlockTbl[ uiLoop]) + { + m_ppBlockTbl[ uiLoop]->Release(); + } + } + + f_free( &m_ppBlockTbl); + } + m_uiBlockTblSize = 0; + + if( m_pNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + + m_uiMaxFileSize = 0; + f_memset( &m_fileHdr, 0, sizeof( F_4x_FILE_HDR)); + f_memset( &m_logHdr, 0, sizeof( F_4x_LOG_HDR)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::readLFiles( void) +{ + RCODE rc = NE_XFLM_OK; + F_Block * pBlock = NULL; + FLMBYTE * pucBlk; + FLMUINT uiBlkAddress; + FLMUINT uiPos; + FLMUINT uiEndPos; + FLMUINT uiEstCount; + FLMUINT uiLFileCnt; + FLMUINT uiLFHCnt; + FLMUINT uiBlkSize = m_fileHdr.uiBlockSize; + F_4x_LFILE TmpLFile; + F_4x_LFILE * pLFile; + F_4x_LFILE * pLFiles = NULL; + + f_memset( &TmpLFile, 0, sizeof( F_4x_LFILE)); + + for( uiEstCount = 0, uiLFileCnt = 4, + uiBlkAddress = m_fileHdr.uiFirstLFHBlkAddr; + uiBlkAddress != FLM_4x_BT_END;) + { + if( RC_BAD( rc = readBlock( uiBlkAddress, &pBlock))) + { + goto Exit; + } + + pucBlk = pBlock->m_pucBlk; + uiPos = FLM_4x_BH_OVHD; + + if( (uiEndPos = (FLMUINT)FB2UW( + &pucBlk[ FLM_4x_BH_ELM_END])) <= FLM_4x_BH_OVHD) + { + uiEndPos = FLM_4x_BH_OVHD; + uiLFHCnt = 0; + } + else + { + if( uiEndPos > uiBlkSize) + { + uiEndPos = uiBlkSize; + } + + uiLFHCnt = (FLMUINT)((uiEndPos - FLM_4x_BH_OVHD) / FLM_4x_LFH_SIZE); + uiEndPos = (FLMUINT)(FLM_4x_BH_OVHD + uiLFHCnt * FLM_4x_LFH_SIZE); + } + + // May allocate too many like the inactive ones but OK for now. + // Allocate an additional 2 for the default data and dict containers. + + if( !uiEstCount) + { + uiEstCount = uiLFHCnt + uiLFileCnt; + if( uiEstCount) + { + if( RC_BAD( rc = f_calloc( + uiEstCount * sizeof( F_4x_LFILE), &pLFiles))) + { + goto Exit; + } + } + } + else if( uiLFHCnt) + { + uiEstCount += uiLFHCnt; + + if( RC_BAD(rc = f_recalloc( uiEstCount * sizeof( F_4x_LFILE), + &pLFiles))) + { + goto Exit; + } + } + + // Read through all of the logical file definitions in the block + + for( ; uiPos < uiEndPos; uiPos += FLM_4x_LFH_SIZE) + { + FLMUINT uiLfNum; + + // Have to fix up the offsets later when they are read in + + TmpLFile.uiBlkAddress = uiBlkAddress; + TmpLFile.uiOffsetInBlk = uiPos; + + if( (TmpLFile.uiLfType = + (FLMUINT)pucBlk[ uiPos + FLM_4x_LFH_TYPE_OFFSET]) == FLM_4x_LF_INVALID) + { + TmpLFile.uiLfType = FLM_4x_LF_INVALID; + continue; + } + + TmpLFile.uiLfNum = + (FLMUINT)FB2UW( &pucBlk[ uiPos + FLM_4x_LFH_LF_NUMBER_OFFSET]); + + TmpLFile.uiRootBlk = + (FLMUINT)FB2UD( &pucBlk[ uiPos + FLM_4x_LFH_ROOT_BLK_OFFSET]); + + TmpLFile.uiNextDrn = + (FLMUINT) FB2UD( &pucBlk[ uiPos + FLM_4x_LFH_NEXT_DRN_OFFSET]); + + uiLfNum = TmpLFile.uiLfNum; + + if( uiLfNum == FLM_4x_DATA_CONTAINER) + { + pLFile = pLFiles + FLM_4x_LFILE_DATA_CONTAINER_OFFSET; + } + else if( uiLfNum == FLM_4x_DICT_CONTAINER) + { + pLFile = pLFiles + FLM_4x_LFILE_DICT_CONTAINER_OFFSET; + } + else if( uiLfNum == FLM_4x_DICT_INDEX) + { + pLFile = pLFiles + FLM_4x_LFILE_DICT_INDEX_OFFSET; + } + else if( uiLfNum == FLM_4x_TRACKER_CONTAINER) + { + pLFile = pLFiles + FLM_4x_LFILE_TRACKER_CONTAINER_OFFSET; + } + else + { + pLFile = pLFiles + uiLFileCnt++; + } + f_memcpy( pLFile, &TmpLFile, sizeof( F_4x_LFILE)); + } + + // Get the next block in the chain + + uiBlkAddress = (FLMUINT)FB2UD( &pucBlk[ FLM_4x_BH_NEXT_BLK]); + } + + m_pLFileTbl = pLFiles; + m_uiLFileCnt = uiLFileCnt; + pLFiles = NULL; + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + if( pLFiles) + { + f_free( &pLFiles); + } + + return( rc ); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getLFile( + FLMUINT uiLFile, + F_4x_LFILE ** ppLFile) +{ + RCODE rc = NE_XFLM_OK; + F_4x_LFILE * pLFile = NULL; + FLMUINT uiLoop; + + if( uiLFile == FLM_4x_DATA_CONTAINER) + { + pLFile = &m_pLFileTbl[ FLM_4x_LFILE_DATA_CONTAINER_OFFSET]; + } + else if( uiLFile == FLM_4x_DICT_CONTAINER) + { + pLFile = &m_pLFileTbl[ FLM_4x_LFILE_DICT_CONTAINER_OFFSET]; + } + else if( uiLFile == FLM_4x_TRACKER_CONTAINER) + { + pLFile = &m_pLFileTbl[ FLM_4x_LFILE_TRACKER_CONTAINER_OFFSET]; + } + else + { + for( uiLoop = 0; uiLoop < m_uiLFileCnt; uiLoop++) + { + if( m_pLFileTbl[ uiLoop].uiLfNum == uiLFile) + { + pLFile = &m_pLFileTbl[ uiLoop]; + break; + } + } + } + + if( !pLFile) + { + rc = RC_SET( NE_XFLM_BAD_COLLECTION); + goto Exit; + } + + *ppLFile = pLFile; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getNextDrn( + FLMUINT uiContainer, + FLMUINT * puiDrn) +{ + RCODE rc = NE_XFLM_OK; + BTSK stackBuf[ FLM_4x_BH_MAX_LEVELS ]; + BTSK * pStack = stackBuf; + FLMBYTE * pucElm; + FLMBOOL bUsedStack = FALSE; + F_4x_LFILE * pLFile; + + if( RC_BAD( rc = getLFile( uiContainer, &pLFile))) + { + goto Exit; + } + + bUsedStack = TRUE; + initStack( &stackBuf[ 0]); + + if( RC_BAD( rc = btSearchEnd( pLFile, + FLM_4x_DRN_LAST_MARKER, &pStack))) + { + goto Exit; + } + + if( pLFile->uiRootBlk == FLM_4x_BT_END) + { + *puiDrn = pLFile->uiNextDrn; + } + else + { + if( pLFile->uiLfNum != + FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM])) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + pucElm = FLM_4x_CURRENT_ELM( pStack); + pucElm += FLM_4x_BBE_GETR_KL( pucElm) + FLM_4x_BBE_KEY; + *puiDrn = FB2UD( pucElm); + } + +Exit: + + if( bUsedStack) + { + releaseStack( stackBuf); + } + + return( rc); +} + +/*************************************************************************** +Desc: Search the right-most end of the b-tree. +****************************************************************************/ +RCODE F_4xReader::btSearchEnd( + F_4x_LFILE * pLFile, + FLMUINT uiDrn, + BTSK ** ppStack) +{ + RCODE rc = NE_XFLM_OK; + BTSK * pStack = *ppStack; + FLMBYTE ucKey[ FLM_4x_DIN_KEY_SIZ]; + FLMUINT uiBlkAddr; + + if( RC_BAD( rc = getRootBlock( pLFile, pStack))) + { + goto Exit; + } + + longToByte( uiDrn, ucKey); + for(;;) + { + if( pStack->uiLevel) + { + pStack->uiCurElm = pStack->uiBlkEnd; + btPrevElm( pStack, pLFile); + } + else + { + if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) + { + if( RC_BAD( rc = btScan( pStack, ucKey))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = btScanNonLeafData( pStack, uiDrn))) + { + goto Exit; + } + } + } + + if( !pStack->uiLevel) + { + break; + } + + uiBlkAddr = childBlkAddr( pStack); + pStack++; + + if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) + { + goto Exit; + } + } + + *ppStack = pStack; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::readBlock( + FLMUINT uiBlkAddr, + F_Block ** ppBlock) +{ + RCODE rc = NE_XFLM_OK; + F_Block * pBlock = NULL; + F_Block * pReuseBlock = NULL; + F_Block ** ppTblSlot = NULL; + + if( *ppBlock) + { + (*ppBlock)->Release(); + *ppBlock = NULL; + } + + if( m_ppBlockTbl) + { + ppTblSlot = getHashBucket( uiBlkAddr); + pBlock = *ppTblSlot; + + if( pBlock) + { + if( FLM_4x_GET_BH_ADDR( pBlock->m_pucBlk) != uiBlkAddr) + { + if( pBlock->getRefCount() == 1) + { + pReuseBlock = *ppTblSlot; + } + else + { + (*ppTblSlot)->Release(); + } + + pBlock = NULL; + *ppTblSlot = NULL; + } + else + { + pBlock->AddRef(); + } + } + } + + if( !pBlock) + { + if( pReuseBlock) + { + pBlock = pReuseBlock; + pReuseBlock = NULL; + } + else + { + if( (pBlock = f_new F_Block) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if( RC_BAD( rc = pBlock->allocBlockBuf( m_fileHdr.uiBlockSize))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pSuperHdl->ReadBlock( uiBlkAddr, + m_fileHdr.uiBlockSize, pBlock->m_pucBlk, NULL))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + // Verify the block checksum + + if( RC_BAD( rc = blkCheckSum( + pBlock->m_pucBlk, uiBlkAddr, m_fileHdr.uiBlockSize))) + { + goto Exit; + } + + // See if we even got the block we thought we wanted + + if( FLM_4x_GET_BH_ADDR( pBlock->m_pucBlk) != uiBlkAddr) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + flmAssert( *ppTblSlot == NULL); + *ppTblSlot = pBlock; + pBlock->AddRef(); + } + + flmAssert( *ppTblSlot == pBlock); + *ppBlock = pBlock; + pBlock = NULL; + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + if( pReuseBlock) + { + pReuseBlock->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::blkCheckSum( + FLMBYTE * pucBlkPtr, + FLMUINT uiBlkAddress, + FLMUINT uiBlkSize) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCnt; + FLMUINT uiAdds; + FLMUINT uiXORs; + FLMUINT uiCurrCheckSum; + FLMUINT uiNewCheckSum; + FLMUINT uiEncryptSize; + FLMBYTE * pucSaveBlkPtr = pucBlkPtr; + + // Check the block length against the max. block size + + uiEncryptSize = (FLMUINT)getEncryptSize( pucBlkPtr); + if( uiEncryptSize > uiBlkSize || uiEncryptSize < FLM_4x_BH_OVHD) + { + rc = RC_SET( NE_XFLM_BLOCK_CRC); + goto Exit; + } + + uiCurrCheckSum = (FLMUINT)(((FLMUINT)pucBlkPtr[ + FLM_4x_BH_CHECKSUM_HIGH] << 8) + + (FLMUINT)pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW]); + + uiAdds = 0 - (pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] + + pucBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]); + + uiXORs = pucBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] ^ + pucBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]; + + if( uiBlkAddress != FLM_4x_BT_END) + { + uiAdds += (FLMBYTE)uiBlkAddress; + uiXORs ^= (FLMBYTE)uiBlkAddress; + } + + for( uiCnt = uiEncryptSize; uiCnt--;) + { + uiAdds += *pucBlkPtr; + uiXORs ^= *(pucBlkPtr++); + } + + uiNewCheckSum = (((uiAdds << 8) + uiXORs) & 0xFFFF); + + if( uiBlkAddress == FLM_4x_BT_END ) + { + FLMBYTE byXor; + FLMBYTE byAdd; + FLMBYTE byDelta; + + // If there is a one byte value that will satisfy both + // sides of the checksum, the checksum is OK and that value + // is the first byte value. + + byXor = (FLMBYTE) uiNewCheckSum; + byAdd = (FLMBYTE) (uiNewCheckSum >> 8); + byDelta = byXor ^ pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW]; + + // This is the big check, if byDelta is also what is + // off with the add portion of the checksum, we have + // a good value. + + if( ((FLMBYTE) (byAdd + byDelta)) == + pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_HIGH]) + { + // Set the low checksum value with the computed value. + + pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] = byDelta; + goto Exit; + } + } + else + { + // This has the side effect of setting the low block address byte + // in the block thus getting rid of the low checksum byte. + // + // NOTE: We are allowing the case where the calculated checksum is + // zero and the stored checksum is one because we used to change + // a calculated zero to a one in old databases and store the one. + // This is probably a somewhat rare case (1 out of 65536 checksums + // will be zero), so forgiving it will be OK most of the time. + // So that those don't cause us to report block checksum errors, + // we just allow it - checksumming isn't a perfect check anyway. + + if( uiNewCheckSum == uiCurrCheckSum || + ((!uiNewCheckSum) && (uiCurrCheckSum == 1))) + { + pucSaveBlkPtr[ FLM_4x_BH_CHECKSUM_LOW] = (FLMBYTE)uiBlkAddress; + goto Exit; + } + } + + // Otherwise, we have a checksum error. + + rc = RC_SET( NE_XFLM_BLOCK_CRC); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::retrieveRec( + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiFlags, + F_Record ** ppRecord) +{ + RCODE rc = NE_XFLM_OK; + BTSK stackBuf[ FLM_4x_BH_MAX_LEVELS]; + BTSK * pStack = NULL; + F_4x_LFILE * pLFile; + + initStack( &stackBuf[ 0]); + pStack = stackBuf; + + if( uiDrn >= (FLM_4x_DRN_LAST_MARKER - 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( RC_BAD( rc = getLFile( uiContainer, &pLFile))) + { + goto Exit; + } + + if( uiFlags & XFLM_INCL) + { + // Search the for the record + + if( RC_BAD( rc = btSearch( pLFile, uiDrn, &pStack))) + { + goto Exit; + } + + if( byteToLong( pStack->ucKeyBuf) == FLM_4x_DRN_LAST_MARKER) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + flmAssert( byteToLong( pStack->ucKeyBuf) >= uiDrn); + } + else if( uiFlags & XFLM_EXCL) + { + // Search the for the record + + if( RC_BAD( rc = btSearch( pLFile, uiDrn + 1, &pStack))) + { + goto Exit; + } + + if( byteToLong( pStack->ucKeyBuf) == FLM_4x_DRN_LAST_MARKER) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + flmAssert( byteToLong( pStack->ucKeyBuf) > uiDrn); + } + else + { + // Search the for the record + + if( RC_BAD( rc = btSearch( pLFile, uiDrn, &pStack))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + } + + goto Exit; + } + + if( !pStack->uiKeyLen || + byteToLong( pStack->ucKeyBuf) != uiDrn) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + } + + // Read the record + + if( RC_BAD( rc = readRecElements( pStack, pLFile, ppRecord))) + { + goto Exit; + } + +Exit: + + releaseStack( stackBuf); + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_4xReader::retrieveNextRec( + F_Record ** ppRecord) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDrn = 0; + FLMUINT uiContainer = m_uiDefaultContainer; + + if( *ppRecord) + { + uiDrn = (*ppRecord)->getID(); + uiContainer = (*ppRecord)->getContainerID(); + } + + if( RC_BAD( rc = retrieveRec( uiContainer, uiDrn, XFLM_EXCL, ppRecord))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/************************************************************************** +Desc: +**************************************************************************/ +void F_4xReader::releaseStack( + BTSK * pStack) +{ + FLMUINT uiNumLevels = FLM_4x_BH_MAX_LEVELS; + + while( uiNumLevels) + { + if( pStack->pBlk) + { + pStack->pBlk->Release(); + pStack->pBlk = NULL; + } + uiNumLevels--; + pStack++; + } +} + +/************************************************************************** +Desc: +**************************************************************************/ +RCODE F_4xReader::readRecElements( + BTSK * pStack, + F_4x_LFILE * pLFile, + F_Record ** ppRecord) +{ + RCODE rc = NE_XFLM_OK; + F_Record * pRecord = NULL; + FLMBYTE * pucCurElm; + void * pvMark = m_tmpPool.poolMark(); + FLMUINT uiElmRecLen; + FLMUINT uiFieldLen; + FLMUINT uiFieldCount; + FLMUINT uiTrueDataSpace; + FLMUINT uiFieldPos; + TFIELD * pField; + FLDGROUP * pFldGroup = NULL; + FLDGROUP * pFirstFldGroup = NULL; + DATAPIECE * pDataPiece; + LOCKED_BLOCK * pLockedBlock = NULL; + FSTATE state; + + // Initialize variables + + state.uiLevel = 0; + uiFieldCount = 0; + uiTrueDataSpace = 0; + uiFieldPos = NUM_FIELDS_IN_ARRAY; + + // Check to make sure we are positioned at the first element. + + pucCurElm = FLM_4x_CURRENT_ELM( pStack); + + if( !FLM_4x_BBE_IS_FIRST( pucCurElm)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Loop on each element in the record + + for( ;;) + { + // Setup all variables to process the current element + + uiElmRecLen = FLM_4x_BBE_GET_RL( pucCurElm); + + if( !uiElmRecLen) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + break; + } + + pucCurElm += FLM_4x_BBE_REC_OFS( pucCurElm); + state.uiPosInElm = 0; + + // Loop on each field within this element. + + while( state.uiPosInElm < uiElmRecLen) + { + state.pElement = pucCurElm; + if( RC_BAD( rc = getFldOverhead( &state))) + { + goto Exit; + } + + uiFieldLen = state.uiFieldLen; + + // Old record info data - skip past for now + + if( !state.uiTagNum) + { + state.uiPosInElm += uiFieldLen; + continue; + } + + if( !pRecord) + { + // Create a new data record or use the existing data record. + + if( *ppRecord) + { + // Reuse the existing F_Record object. + + pRecord = *ppRecord; + *ppRecord = NULL; + pRecord->clear(); + } + else + { + if( (pRecord = f_new F_Record) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + pRecord->setContainerID( + FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM])); + pRecord->setID( byteToLong( pStack->ucKeyBuf)); + } + + // Check if out of fields in the tempoary field group + + if( uiFieldPos >= NUM_FIELDS_IN_ARRAY) + { + FLDGROUP * pTempFldGroup; + + uiFieldPos = 0; + + // Allocate the first field group from the pool. + + if( RC_BAD( rc = m_tmpPool.poolAlloc( + sizeof( FLDGROUP), (void **)&pTempFldGroup))) + { + goto Exit; + } + + pTempFldGroup->pNext = NULL; + if( pFldGroup) + { + pFldGroup->pNext = pTempFldGroup; + } + else + { + pFirstFldGroup = pTempFldGroup; + } + pFldGroup = pTempFldGroup; + } + + flmAssert( state.uiFieldType != FLM_4x_UNKNOWN_TYPE); + uiFieldCount++; + pField = &pFldGroup->pFields[ uiFieldPos++]; + pField->uiLevel = state.uiLevel; + pField->uiFieldID = state.uiTagNum; + pField->uiFieldType = state.uiFieldType; + pField->uiFieldLen = state.uiFieldLen; + pDataPiece = &pField->DataPiece; + + if( uiFieldLen) + { + FLMUINT uiDataPos = 0; + + if( state.uiFieldLen > 4) + { + // Binary data needs to account for alignment issues. + + if( state.uiFieldType == FLM_4x_BINARY_TYPE) + { + if( state.uiFieldLen >= 255) + { + // Align so that the data is aligned - not the length + + uiTrueDataSpace += 2; + uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & + (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); + uiTrueDataSpace -= 2; + } + else + { + uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & + (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF)); + } + } + + uiTrueDataSpace += state.uiFieldLen; + + // For read only records, greater than 255 bytes are + // stored length preceded. + + if( state.uiFieldLen >= 255) + { + uiTrueDataSpace += 2; + } + } + + // Value may start in the next element. + + while( uiDataPos < uiFieldLen) + { + // Need to read next element for the value portion? + + if( state.uiPosInElm >= uiElmRecLen) + { + if( FLM_4x_BBE_IS_LAST( FLM_4x_CURRENT_ELM( pStack))) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // If we are going to the next block, lock down this block + // beacause data pointers are pointing to it. + + if( RC_BAD( blkNextElm( pStack))) + { + LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; + + if( RC_BAD( rc = m_tmpPool.poolAlloc( + sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) + { + goto Exit; + } + + pLockedBlock->pBlock = pStack->pBlk; + pLockedBlock->pBlock->AddRef(); + pLockedBlock->pNext = pLastLockedBlock; + + if( RC_BAD( rc = btNextElm( pStack, pLFile))) + { + goto Exit; + } + } + + pucCurElm = FLM_4x_CURRENT_ELM( pStack); + uiElmRecLen = FLM_4x_BBE_GET_RL( pucCurElm); + pucCurElm += FLM_4x_BBE_REC_OFS( pucCurElm); + state.uiPosInElm = 0; + } + + // Compare number of bytes left if value <= # bytes left in element + + if( (uiFieldLen - uiDataPos) <= + (uiElmRecLen - state.uiPosInElm)) + { + FLMUINT uiDelta = uiFieldLen - uiDataPos; + + pDataPiece->pData = &pucCurElm[ state.uiPosInElm]; + pDataPiece->uiLength = uiDelta; + state.uiPosInElm += uiDelta; + pDataPiece->pNext = NULL; + break; + } + else + { + // Take what is there and get next element to grab some more. + + FLMUINT uiBytesToMove = uiElmRecLen - state.uiPosInElm; + DATAPIECE * pNextDataPiece; + + pDataPiece->pData = &pucCurElm[ state.uiPosInElm]; + pDataPiece->uiLength = uiBytesToMove; + state.uiPosInElm += uiBytesToMove; + uiDataPos += uiBytesToMove; + + if( RC_BAD( rc = m_tmpPool.poolAlloc( + sizeof( DATAPIECE), (void **)&pNextDataPiece))) + { + goto Exit; + } + pDataPiece->pNext = pNextDataPiece; + pDataPiece = pNextDataPiece; + } + } + } + } + + // Done? + + if( FLM_4x_BBE_IS_LAST( FLM_4x_CURRENT_ELM( pStack))) + { + break; + } + + // Position to next element + + if( RC_BAD( blkNextElm( pStack))) + { + LOCKED_BLOCK * pLastLockedBlock = pLockedBlock; + + if( RC_BAD( rc = m_tmpPool.poolAlloc( + sizeof( LOCKED_BLOCK), (void **)&pLockedBlock))) + { + goto Exit; + } + + pLockedBlock->pBlock = pStack->pBlk; + pLockedBlock->pBlock->AddRef(); + pLockedBlock->pNext = pLastLockedBlock; + + if( RC_BAD( rc = btNextElm( pStack, pLFile))) + { + goto Exit; + } + } + + // Corruption Check. + + pucCurElm = FLM_4x_CURRENT_ELM( pStack); + if( FLM_4x_BBE_IS_FIRST( pucCurElm)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + if( pRecord) + { + void * pvField; + + if( RC_BAD( rc = pRecord->preallocSpace( uiTrueDataSpace))) + { + goto Exit; + } + pFldGroup = pFirstFldGroup; + + for( uiFieldPos = 0; uiFieldCount--; uiFieldPos++) + { + + if( uiFieldPos >= NUM_FIELDS_IN_ARRAY) + { + uiFieldPos = 0; + if( (pFldGroup = pFldGroup->pNext) == NULL) + { + break; + } + } + pField = &pFldGroup->pFields[ uiFieldPos]; + + if( RC_BAD( rc = pRecord->insertLast( pField->uiLevel, pField->uiFieldID, + pField->uiFieldType, &pvField))) + { + goto Exit; + } + + if( pField->uiFieldLen) + { + FLMBYTE * pDataPtr; // Points to where the data will go. + + pDataPiece = &pField->DataPiece; + pDataPtr = pRecord->getImportDataPtr( pvField, + pField->uiFieldType, pField->uiFieldLen); + + if( !pDataPtr) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + do + { + f_memcpy( pDataPtr, pDataPiece->pData, pDataPiece->uiLength); + pDataPtr += pDataPiece->uiLength; + pDataPiece = pDataPiece->pNext; + } + while( pDataPiece); + } + } + } + + if( *ppRecord) + { + flmAssert( 0); + (*ppRecord)->Release(); + } + + *ppRecord = pRecord; + pRecord = NULL; + +Exit: + + // Release all locked down blocks except the current block. + + while( pLockedBlock) + { + pLockedBlock->pBlock->Release(); + pLockedBlock = pLockedBlock->pNext; + } + + m_tmpPool.poolReset( pvMark); + + if( pRecord) + { + pRecord->Release(); + } + + return( rc); +} + +/************************************************************************** +Desc: +**************************************************************************/ +RCODE F_4xReader::getFldOverhead( + FSTATE * pState) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pFieldOvhd = &pState->pElement[ pState->uiPosInElm]; + FLMBYTE * pElement = pState->pElement; + FLMBOOL bDoesntHaveFieldDef = TRUE; + FLMUINT uiFieldLen; + FLMUINT uiFieldType = 0; + FLMUINT uiTagNum; + FLMBYTE ucTemp; + + if( FLM_4x_FOP_IS_STANDARD( pFieldOvhd)) + { + if( FLM_4x_FSTA_LEVEL( pFieldOvhd)) + { + pState->uiLevel++; + } + + uiFieldLen = FLM_4x_FSTA_FLD_LEN( pFieldOvhd); + uiTagNum = FLM_4x_FSTA_FLD_NUM( pFieldOvhd); + pFieldOvhd += FLM_4x_FSTA_OVHD; + } + else if( FLM_4x_FOP_IS_OPEN( pFieldOvhd)) + { + if( FLM_4x_FOPE_LEVEL( pFieldOvhd)) + { + pState->uiLevel++; + } + + ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd++)); + uiTagNum = (FLMUINT)*pFieldOvhd++; + + if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) + { + uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; + } + + uiFieldLen = (FLMUINT) *pFieldOvhd++; + if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) + { + uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; + } + } + else if( FLM_4x_FOP_IS_NO_VALUE( pFieldOvhd)) + { + if( FLM_4x_FNOV_LEVEL( pFieldOvhd)) + { + pState->uiLevel++; + } + + ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd++)); + uiTagNum = (FLMUINT)*pFieldOvhd++; + + if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) + { + uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; + } + uiFieldLen = uiFieldType = 0; + } + else if( FLM_4x_FOP_IS_SET_LEVEL( pFieldOvhd)) + { + pState->uiLevel -= FLM_4x_FSLEV_GET( pFieldOvhd++); + pState->uiPosInElm = (FLMUINT)( pFieldOvhd - pElement); + rc = getFldOverhead( pState); + goto Exit; + } + else if( FLM_4x_FOP_IS_TAGGED( pFieldOvhd)) + { + bDoesntHaveFieldDef = FALSE; + + if( FLM_4x_FTAG_LEVEL( pFieldOvhd)) + { + pState->uiLevel++; + } + + ucTemp = (FLMBYTE)(FLM_4x_FOP_GET_FLD_FLAGS( pFieldOvhd)); + uiFieldType = (FLMUINT)(FLM_4x_FTAG_FLD_TYPE( pFieldOvhd)); + pFieldOvhd += FLM_4x_FTAG_OVHD; + uiTagNum = (FLMUINT) *pFieldOvhd++; + + if( FLM_4x_FOP_2BYTE_FLDNUM( ucTemp)) + { + uiTagNum += ((FLMUINT) *pFieldOvhd++) << 8; + } + + uiTagNum ^= 0x8000; + uiFieldLen = (FLMUINT)*pFieldOvhd++; + if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) + { + uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; + } + } + else if( FLM_4x_FOP_IS_RECORD_INFO( pFieldOvhd)) + { + bDoesntHaveFieldDef = FALSE; + ucTemp = *pFieldOvhd++; + uiFieldLen = *pFieldOvhd++; + + if( FLM_4x_FOP_2BYTE_FLDLEN( ucTemp)) + { + uiFieldLen += ((FLMUINT) *pFieldOvhd++) << 8; + } + uiTagNum = 0; + } + else + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( bDoesntHaveFieldDef) + { + if( RC_BAD( rc = getFieldType( uiTagNum, &uiFieldType))) + { + goto Exit; + } + } + + pState->uiFieldType = uiFieldType; + pState->uiFieldLen = uiFieldLen; + pState->uiPosInElm = (FLMUINT)(pFieldOvhd - pElement); + pState->uiTagNum = uiTagNum; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMUINT F_4xReader::childBlkAddr( + BTSK * pStack) +{ + FLMBYTE * pucChildBlkPtr; + FLMUINT uiElmOvhd = pStack->uiElmOvhd; + + if( uiElmOvhd == FLM_4x_BNE_DATA_OVHD) + { + pucChildBlkPtr = FLM_4x_BLK_ELM_ADDR( + pStack, pStack->uiCurElm + FLM_4x_BNE_DATA_CHILD_BLOCK); + return( FB2UD( pucChildBlkPtr)); + } + else + { + // Corruption + + flmAssert( 0); + return( 0); + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::btSearch( + F_4x_LFILE * pLFile, + FLMUINT uiDrn, + BTSK ** ppStack) +{ + RCODE rc = NE_XFLM_OK; + BTSK * pStack = *ppStack; + FLMBYTE ucKey[ FLM_4x_DIN_KEY_SIZ]; + FLMUINT uiBlkAddr; + + // Get the root block + + if( RC_BAD( rc = getRootBlock( pLFile, pStack))) + { + goto Exit; + } + + longToByte( uiDrn, ucKey); + + // Read each block going down the b-tree. + // Save state information in the stack. + + for(;;) + { + if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) + { + if( RC_BAD( rc = btScan( pStack, ucKey))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = btScanNonLeafData( pStack, uiDrn))) + { + goto Exit; + } + } + + if( !pStack->uiLevel) + { + break; + } + + uiBlkAddr = childBlkAddr( pStack); + pStack++; + + if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) + { + goto Exit; + } + } + + *ppStack = pStack; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Scan a b-tree block for a matching key at any b-tree block level. +****************************************************************************/ +RCODE F_4xReader::btScan( + BTSK * pStack, + FLMBYTE * pucSearchKey) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucCurElm; + FLMBYTE * pBlk; + FLMBYTE * pucKeyBuf; + FLMBYTE * pElmKey; + FLMUINT uiRecLen = 0; + FLMUINT uiPrevKeyCnt; + FLMUINT uiElmKeyLen; + FLMUINT uiBlkType; + FLMUINT uiElmOvhd; + FLMUINT uiBytesMatched; + + uiBlkType = pStack->uiBlkType; + flmAssert( uiBlkType != FLM_4x_BHT_NON_LEAF_DATA); + + pucKeyBuf = pStack->ucKeyBuf; + pBlk = pStack->pBlk->m_pucBlk; + uiElmOvhd = pStack->uiElmOvhd; + pStack->uiCurElm = FLM_4x_BH_OVHD; + pStack->uiKeyLen = 0; + pStack->uiPKC = 0; + pStack->uiPrevElmPKC = 0; + uiBytesMatched = 0; + + for( ;;) + { + pucCurElm = &pBlk[ pStack->uiCurElm]; + uiElmKeyLen = FLM_4x_BBE_GETR_KL( pucCurElm); + + // Read in RAW mode - doesn't do all bit checking + + if( (uiPrevKeyCnt = (FLM_4x_BBE_GETR_PKC( pucCurElm))) > + FLM_4x_BBE_PKC_MAX) + { + uiElmKeyLen += (uiPrevKeyCnt & FLM_4x_BBE_KL_HBITS) << + FLM_4x_BBE_KL_SHIFT_BITS; + uiPrevKeyCnt &= FLM_4x_BBE_PKC_MAX; + } + + // Should not have a non-zero PKC if we are on the first element + // of a block + + if( uiPrevKeyCnt && pStack->uiCurElm == FLM_4x_BH_OVHD) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Get the record portion length when on the leaf blocks. + + if( uiBlkType == FLM_4x_BHT_LEAF) + { + uiRecLen = FLM_4x_BBE_GET_RL( pucCurElm); + } + + pStack->uiPrevElmPKC = pStack->uiPKC; + + // The zero length key is the terminating + // element in a right-most block. + + if( (pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen) == 0) + { + pStack->uiPrevElmPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); + pStack->uiPKC = 0; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // Handle special case of left-end compression maxing out. + + if( uiPrevKeyCnt == FLM_4x_BBE_PKC_MAX && + FLM_4x_BBE_PKC_MAX < uiBytesMatched) + { + uiBytesMatched = FLM_4x_BBE_PKC_MAX; + } + + // Check out this element to see if the key matches. + + if( uiPrevKeyCnt == uiBytesMatched) + { + pElmKey = &pucCurElm[ uiElmOvhd]; + + for(;;) + { + // All bytes of the search key are matched? + + if( uiBytesMatched == FLM_4x_DIN_KEY_SIZ) + { + pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); + + if( pStack->uiKeyLen != FLM_4x_DIN_KEY_SIZ) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Current key is equal to the search key. + + f_memcpy( pucKeyBuf, pucSearchKey, FLM_4x_DIN_KEY_SIZ); + goto Exit; + } + + if( uiBytesMatched == pStack->uiKeyLen) + { + pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); + goto Next_Element; + } + + // Compare the next byte in the search key and element + + if( pucSearchKey[ uiBytesMatched] != *pElmKey) + { + break; + } + + uiBytesMatched++; + pElmKey++; + } + + pStack->uiPKC = f_min( uiBytesMatched, FLM_4x_BBE_PKC_MAX); + + // Check if we are done comparing + + if( pucSearchKey[ uiBytesMatched] < *pElmKey) + { + if( uiBytesMatched) + { + flmAssert( uiBytesMatched <= FLM_4x_DIN_KEY_SIZ); + f_memcpy( pucKeyBuf, pucSearchKey, uiBytesMatched); + } + + flmAssert( pStack->uiKeyLen <= FLM_4x_DIN_KEY_SIZ); + f_memcpy( &pucKeyBuf[ uiBytesMatched], pElmKey, + pStack->uiKeyLen - uiBytesMatched); + goto Exit; + } + } + else if( uiPrevKeyCnt < uiBytesMatched) + { + // Current key > search key. Set pucKeyBuf and break out. + + pStack->uiPKC = uiPrevKeyCnt; + if( uiPrevKeyCnt) + { + flmAssert( uiPrevKeyCnt <= FLM_4x_DIN_KEY_SIZ); + f_memcpy( pucKeyBuf, pucSearchKey, uiPrevKeyCnt); + } + + flmAssert( uiPrevKeyCnt + uiElmKeyLen <= FLM_4x_DIN_KEY_SIZ); + f_memcpy( &pucKeyBuf[ uiPrevKeyCnt], + &pucCurElm[ uiElmOvhd], uiElmKeyLen); + + if( byteToLong( pucKeyBuf) == FLM_4x_BT_END) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + } + + goto Exit; + } + +Next_Element: + + // Position to the next element + + pStack->uiCurElm += uiElmKeyLen + ((uiBlkType == FLM_4x_BHT_LEAF ) + ? (FLM_4x_BBE_KEY + uiRecLen) + : (FLM_4x_BNE_IS_DOMAIN( pucCurElm) + ? (FLM_4x_BNE_DOMAIN_LEN + uiElmOvhd) + : uiElmOvhd)); + + // Most common check first. + + if( pStack->uiCurElm < pStack->uiBlkEnd) + { + continue; + } + + if( pStack->uiCurElm == pStack->uiBlkEnd) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // Marched off the end of the block + + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Binary search into a non-leaf data record block. +****************************************************************************/ +RCODE F_4xReader::btScanNonLeafData( + BTSK * pStack, + FLMUINT uiDrn) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBlk = pStack->pBlk->m_pucBlk; + FLMUINT uiLow = 0; + FLMUINT uiMid; + FLMUINT uiHigh = ((pStack->uiBlkEnd - FLM_4x_BH_OVHD) >> 3) - 1; + FLMUINT uiTblSize = uiHigh; + FLMUINT uiCurDrn; + + for(;;) + { + uiMid = (uiLow + uiHigh) >> 1; + + uiCurDrn = byteToLong( &pucBlk[ FLM_4x_BH_OVHD + (uiMid << 3)]); + if( !uiCurDrn) + { + // Special case - at the end of a rightmost block. + break; + } + + if( uiDrn == uiCurDrn) + { + // Remember a data record can span multiple blocks (same DRN). + + while( uiMid) + { + uiCurDrn = byteToLong( + &pucBlk[ FLM_4x_BH_OVHD + ((uiMid - 1) << 3)]); + if( uiDrn != uiCurDrn) + { + break; + } + uiMid--; + } + break; + } + + // Down to one item if too high then position to next item. + + if( uiLow >= uiHigh) + { + if( (uiDrn > uiCurDrn) && uiMid < uiTblSize) + { + uiMid++; + } + + break; + } + + // If too high then try lower section + + if( uiDrn < uiCurDrn) + { + // First item too high? + + if( !uiMid) + { + break; + } + + uiHigh = uiMid - 1; + } + else + { + if( uiMid == uiTblSize) + { + uiMid++; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + uiLow = uiMid + 1; + } + } + + // Set curElm and the key buffer. + + pStack->uiCurElm = FLM_4x_BH_OVHD + (uiMid << 3); + longToByte( uiCurDrn, pStack->ucKeyBuf); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Goto the next element within the block +****************************************************************************/ +RCODE F_4xReader::blkNextElm( + BTSK * pStack) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucElm; + FLMUINT uiElmSize; + + pucElm = &pStack->pBlk->m_pucBlk[ pStack->uiCurElm]; + + if( pStack->uiBlkType == FLM_4x_BHT_LEAF) + { + uiElmSize = FLM_4x_BBE_LEN( pucElm); + if( pStack->uiCurElm + FLM_4x_BBE_LEM_LEN < pStack->uiBlkEnd) + { + if( ((pStack->uiCurElm += uiElmSize) + + FLM_4x_BBE_LEM_LEN < pStack->uiBlkEnd) == 0) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + } + else + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + else + { + if( pStack->uiBlkType != FLM_4x_BHT_NON_LEAF_DATA) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + uiElmSize = FLM_4x_BNE_DATA_OVHD; + + if( pStack->uiCurElm < pStack->uiBlkEnd) + { + // Check if this is not the last element within the block + + if( (pStack->uiCurElm += uiElmSize) >= pStack->uiBlkEnd) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Go to the next element in the logical b-tree +****************************************************************************/ +RCODE F_4xReader::btNextElm( + BTSK * pStack, + F_4x_LFILE * pLFile) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucCurElm; + FLMUINT uiLFile; + + uiLFile = FB2UW( &pStack->pBlk->m_pucBlk[ FLM_4x_BH_LOG_FILE_NUM]); + + if( pStack->uiCurElm < FLM_4x_BH_OVHD) + { + pStack->uiCurElm = FLM_4x_BH_OVHD; + } + else + { + if( RC_BAD( rc = blkNextElm( pStack))) + { + if( rc == NE_XFLM_EOF_HIT) + { + FLMBYTE * pucBlk = FLM_4x_BLK_ELM_ADDR( pStack, FLM_4x_BH_NEXT_BLK); + FLMUINT uiBlkNum = FB2UD( pucBlk); + + if( uiBlkNum != FLM_4x_BT_END) + { + // Current element was last element in the block - goto next block */ + + if( RC_BAD( rc = getBlock( pLFile, uiBlkNum, pStack))) + { + goto Exit; + } + + pucBlk = pStack->pBlk->m_pucBlk; + pStack->uiBlkEnd = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END ]); + pStack->uiCurElm = FLM_4x_BH_OVHD; + btAdjustStack( pStack, pLFile, TRUE); + } + } + } + } + + pucCurElm = FLM_4x_CURRENT_ELM( pStack); + + // Copy the key + + f_memcpy( pStack->ucKeyBuf, pucCurElm, FLM_4x_DIN_KEY_SIZ); + +Exit: + + return(rc ); +} + +/*************************************************************************** +Desc: Go to the previous element in the logical b-tree +****************************************************************************/ +RCODE F_4xReader::btPrevElm( + BTSK * pStack, + F_4x_LFILE * pLFile) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkAddr; + FLMUINT uiTargetElm; + FLMUINT uiPrevElm = 0; + FLMUINT uiPrevKeyCnt = 0; + FLMUINT uiElmKeyLen = 0; + FLMUINT uiElmOvhd = pStack->uiElmOvhd; + FLMBYTE * pucCurElm; + FLMBYTE * pucBlk; + + // Check if we are at or before the first element in the block + + if( pStack->uiCurElm <= FLM_4x_BH_OVHD) + { + pucBlk = pStack->pBlk->m_pucBlk; + + // We're at or before the first element, so read in the previous + // block and go to the last element + + if( (uiBlkAddr = (FLMUINT)FB2UD( &pucBlk[ + FLM_4x_BH_PREV_BLK])) == FLM_4x_BT_END) + { + // We are at the end + + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + else + { + if( RC_BAD( rc = getBlock( pLFile, uiBlkAddr, pStack))) + { + // Set uiBlkEnd and uiCurElm. + // Adjust the parent block to the previous element + + pucBlk = pStack->pBlk->m_pucBlk; + pStack->uiCurElm = pStack->uiBlkEnd; + btAdjustStack( pStack, pLFile, FALSE); + goto Exit; + } + } + } + + // Move down 1 before the current element + + if( pStack->uiBlkType == FLM_4x_BHT_NON_LEAF_DATA) + { + pStack->uiCurElm -= FLM_4x_BNE_DATA_OVHD; + pucBlk = pStack->pBlk->m_pucBlk; + pucCurElm = &pucBlk[ pStack->uiCurElm]; + f_memcpy( pStack->ucKeyBuf, pucCurElm, FLM_4x_DIN_KEY_SIZ); + goto Exit; + } + + // Set up to point to first element in the block + + uiTargetElm = pStack->uiCurElm; + pStack->uiCurElm = FLM_4x_BH_OVHD; + pucBlk = pStack->pBlk->m_pucBlk; + + while( pStack->uiCurElm < uiTargetElm) + { + pucCurElm = &pucBlk[ pStack->uiCurElm]; + uiPrevKeyCnt = (FLMUINT)(FLM_4x_BBE_GET_PKC( pucCurElm)); + uiElmKeyLen = (FLMUINT)(FLM_4x_BBE_GET_KL( pucCurElm)); + + if( uiElmKeyLen + uiPrevKeyCnt > FLM_4x_DIN_KEY_SIZ) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( uiElmKeyLen) + { + flmAssert( uiPrevKeyCnt + uiElmKeyLen <= FLM_4x_DIN_KEY_SIZ); + f_memcpy( &pStack->ucKeyBuf[ uiPrevKeyCnt], + &pucCurElm[ uiElmOvhd], uiElmKeyLen); + } + + uiPrevElm = pStack->uiCurElm; + if( RC_BAD( rc = blkNextElm( pStack))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + break; + } + } + + pStack->uiKeyLen = uiPrevKeyCnt + uiElmKeyLen; + pStack->uiCurElm = uiPrevElm; + flmAssert( pStack->uiKeyLen == FLM_4x_DIN_KEY_SIZ); + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::btAdjustStack( + BTSK * pStack, + F_4x_LFILE * pLFile, + FLMBOOL bMovedNext) +{ + RCODE rc = NE_XFLM_OK; + + pStack--; + if( RC_BAD( rc = getBlock( pLFile, pStack->uiBlkAddr, pStack))) + { + goto Exit; + } + + if( bMovedNext) + { + if( RC_BAD( rc = btNextElm( pStack, pLFile))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = btPrevElm( pStack, pLFile))) + { + goto Exit; + } + } + +Exit: + + pStack++; + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getNameTable( + F_4xNameTable ** ppNameTable) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bAllocated = FALSE; + + if( !m_pNameTable) + { + if( (m_pNameTable = f_new F_4xNameTable) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + bAllocated = TRUE; + + if( RC_BAD( rc = m_pNameTable->setupNameTable( this))) + { + goto Exit; + } + } + + flmAssert( *ppNameTable == NULL); + m_pNameTable->AddRef(); + *ppNameTable = m_pNameTable; + +Exit: + + if( RC_BAD( rc) && bAllocated) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +F_Record::F_Record() +{ + m_uiContainerID = 0; + m_uiRecordID = 0; + m_pool.poolInit( sizeof( m_fieldList)); + m_pFirstFld = m_pLastFld = NULL; + resetFieldList(); + m_pDataBuf = NULL; + m_uiDataBufOffset = 0; + m_uiDataBufLength = 0; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +F_Record::~F_Record() +{ + m_pool.poolFree(); + if( m_pDataBuf) + { + f_free( &m_pDataBuf); + } +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void F_Record::resetFieldList( void) +{ + FLMUINT uiLoop; + FIELD * pCurField; + FIELD * pPrevField = NULL; + + for( uiLoop = 0; uiLoop < FLM_4x_FIELD_LIST_SIZE; uiLoop++) + { + pCurField = &m_fieldList[ uiLoop]; + pCurField->ui16FieldID = 0xFFFF; + + pCurField->pPrev = pPrevField; + pCurField->pNext = &m_fieldList[ uiLoop + 1]; + pPrevField = pCurField; + } + + pCurField->pNext = NULL; + + m_pAvailFld = &m_fieldList[ 0]; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void F_Record::clear() +{ + m_uiDataBufOffset = 0; + resetFieldList(); + + m_pool.poolReset( NULL); + m_pFirstFld = NULL; + m_pLastFld = NULL; + + m_uiContainerID = 0; + m_uiRecordID = 0; +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FIELD * F_Record::lastSubTreeField( + FIELD * pField) +{ + FIELD * pTempField = (FIELD *)lastChild( pField); + FIELD * pLastChild = NULL; + FLMUINT uiStartLevel = pField->ui8Level; + + // Step down through the tree + + for( ; pTempField && pTempField->ui8Level > uiStartLevel; + pTempField = nextField( pTempField)) + { + pLastChild = pTempField; + } + + return( pLastChild); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::insertLast( + FLMUINT uiLevel, + FLMUINT uiFieldID, + FLMUINT uiDataType, + void ** ppvField) +{ + RCODE rc = NE_XFLM_OK; + FIELD * pField = NULL; + + // Insert new field following current last field + + if( RC_BAD( rc = createField( m_pLastFld, &pField))) + { + goto Exit; + } + + // Set up the new field and set as the current field + + pField->ui16FieldID = (FLMUINT16) uiFieldID; + pField->ui8Level = (FLMUINT8) uiLevel; + pField->ui8Type = (FLMUINT8) uiDataType; + + if( ppvField) + { + *ppvField = pField; + } + +Exit: + +#ifdef FLM_DEBUG + if ( pField) + { + flmAssert( pField->pNext != pField); + flmAssert( pField->pPrev != pField); + flmAssert( pField->ui16FieldID != 0xFFFF); + } +#endif + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FIELD * F_Record::nextSiblingField( + FIELD * pField) +{ + FLMUINT8 ui8Level = pField->ui8Level; + + while( (pField = nextField( pField)) != NULL && + pField->ui8Level > ui8Level) + { + ; + } + + return( (pField && pField->ui8Level == ui8Level) + ? pField + : NULL); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void * F_Record::prevSibling( + void * pvField) +{ + if( !pvField) + { + return( NULL); + } + + FIELD * pField = (FIELD *)pvField; + FLMUINT8 ui8Level = pField->ui8Level; + + while( (pField = prevField( pField)) != NULL && + pField->ui8Level > ui8Level) + { + ; + } + + return( (pField && pField->ui8Level == ui8Level) + ? pField + : NULL); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void * F_Record::lastChild( + void * pvField) +{ + FIELD * pField = (FIELD *)pvField; + FIELD * pLastField = NULL; + + if( !pField) + { + return( NULL); + } + + for( pField = firstChildField( pField); + pField; + pField = nextSiblingField( pField)) + { + pLastField = pField; + } + + return( pLastField); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void * F_Record::parent( + void * pvField) +{ + FIELD * pField = (FIELD *) pvField; + FLMUINT8 ui8Level; + + if( !pField) + { + return( NULL); + } + + ui8Level = pField->ui8Level; + + while( (pField = prevField( pField)) != NULL && + pField->ui8Level >= ui8Level) + { + ; + } + + return( pField); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE * F_Record::getImportDataPtr( + void * pvField, + FLMUINT uiDataType, + FLMUINT uiLength) +{ + FLMBYTE * pucData = NULL; + + getNewDataPtr( (FIELD *)pvField, uiDataType, uiLength, &pucData); + ((FIELD *)pvField)->ui8Type = (FLMUINT8) uiDataType; + + return( pucData); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::preallocSpace( + FLMUINT uiDataSize) +{ + RCODE rc = NE_XFLM_OK; + + m_uiDataBufOffset = 0; + if( m_uiDataBufLength >= uiDataSize) + { + goto Exit; + } + + if( !m_pDataBuf) + { + m_uiDataBufLength = 0; + if( RC_BAD( rc = f_alloc( uiDataSize, &m_pDataBuf))) + { + goto Exit; + } + m_uiDataBufLength = uiDataSize; + } + else + { + if( RC_BAD( rc = f_realloc( uiDataSize, &m_pDataBuf))) + { + goto Exit; + } + m_uiDataBufLength = uiDataSize; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void * F_Record::find( + void * pvField, + FLMUINT uiFieldID, + FLMUINT uiOccur, + FLMBOOL bSearchForest) +{ + if( uiOccur == 0) + { + uiOccur = 1; + } + + if( pvField) + { + FLMUINT uiStartLevel = ((FIELD *)pvField)->ui8Level; + do + { + if( (uiFieldID == + ((FIELD *)pvField)->ui16FieldID) && (--uiOccur < 1)) + { + return( pvField); + } + } + while( (pvField = (FIELD *)(pvField + ? ((FIELD *)pvField)->pNext + : NULL)) != NULL && + ((((FIELD *)pvField)->ui8Level > uiStartLevel) || + (bSearchForest && ((FIELD *)pvField)->ui8Level == + uiStartLevel))); + } + + return( NULL); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +void * F_Record::find( + void * pvField, + FLMUINT * puiPathArray, + FLMUINT uiOccur, + FLMBOOL bSearchForest) +{ + void * pvSaveField; + FLMUINT * puiPath; + FLMUINT uiLevel; + + // Handle empty record + + if( !pvField) + { + return( NULL); + } + + if( !uiOccur) + { + uiOccur = 1; + } + + uiLevel = ((FIELD *)pvField)->ui8Level; + for(;;) + { + puiPath = puiPathArray + ( ((FIELD *)pvField)->ui8Level - uiLevel); + pvSaveField = pvField; + + if( *puiPath == ((FIELD *)pvField)->ui16FieldID) + { + if( *(puiPath + 1) == 0 && (--uiOccur < 1)) + { + return( pvField); + } + + // Go down level for rest of path + + if( ( pvField = firstChild( pvField)) != NULL) + { + continue; + } + pvField = pvSaveField; + } + + // Find next sibling/uncle/end + + do + { + pvField = (FIELD *)(pvField ? ((FIELD *)pvField)->pNext : NULL); + } + while( pvField != NULL + && ((FIELD *)pvField)->ui8Level > ((FIELD *)pvSaveField)->ui8Level); + + // Are we at the end? + + if( !pvField || + ((FIELD *)pvField)->ui8Level < uiLevel || + (bSearchForest && ((FIELD *)pvField)->ui8Level == uiLevel)) + { + break; + } + } + + return( NULL); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::createField( + FIELD * pCurField, + FIELD ** ppNewField) +{ + RCODE rc = NE_XFLM_OK; + FIELD * pNewField; + + if( m_pAvailFld) + { + pNewField = m_pAvailFld; + m_pAvailFld = m_pAvailFld->pNext; + if( m_pAvailFld) + m_pAvailFld->pPrev = NULL; + pNewField->pPrev = NULL; + pNewField->pNext = NULL; + } + else + { + if( RC_BAD( rc = m_pool.poolAlloc( sizeof( FIELD), (void **)&pNewField))) + { + goto Exit; + } + } + + if( !pCurField && m_pLastFld) + { + pCurField = m_pLastFld; + } + + if( pCurField) + { + pNewField->pNext = pCurField->pNext; + pNewField->pPrev = pCurField; + + if( pCurField->pNext) + { + pCurField->pNext->pPrev = pNewField; + } + pCurField->pNext = pNewField; + } + + if( !m_pFirstFld) + { + m_pFirstFld = pNewField; + } + + if( !m_pLastFld || pCurField == m_pLastFld) + { + m_pLastFld = pNewField; + } + + pNewField->uiDataLength = 0; + pNewField->uiDataOffset = 0; + *ppNewField = pNewField; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getNewDataPtr( + FIELD * pField, + FLMUINT uiDataType, + FLMUINT uiNewLength, + FLMBYTE ** ppDataPtr) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pDataPtr; + FLMUINT uiTemp; + + if( pField->uiDataLength) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + if( uiNewLength <= sizeof( FLMUINT)) + { + pDataPtr = (FLMBYTE *) &(pField->uiDataOffset); + } + else + { + // If this is a binary field it must start on an aligned byte. + + if( uiDataType == FLM_4x_BINARY_TYPE && + (m_uiDataBufOffset & FLM_ALLOC_ALIGN) != 0) + { + uiTemp = (FLM_ALLOC_ALIGN + 1) - (m_uiDataBufOffset & FLM_ALLOC_ALIGN); + m_uiDataBufOffset += uiTemp; + } + + if( uiNewLength + m_uiDataBufOffset > m_uiDataBufLength) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + pDataPtr = m_pDataBuf + m_uiDataBufOffset; + pField->uiDataOffset = m_uiDataBufOffset; + m_uiDataBufOffset += uiNewLength; + } + + pField->uiDataLength = uiNewLength; + *ppDataPtr = pDataPtr; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getINT( + void * pvField, + FLMINT * piNumber) +{ + return( pvField + ? storage2INT( getDataType( pvField), getDataLength( pvField), + getDataPtr( (FIELD *)pvField), piNumber) + : RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getUINT( + void * pvField, + FLMUINT * puiNumber) +{ + return( pvField + ? storage2UINT( getDataType( pvField), getDataLength( pvField), + getDataPtr( (FIELD *)pvField), puiNumber) + : RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getUINT32( + void * pvField, + FLMUINT32 * pui32Number) +{ + return( pvField + ? storage2UINT32( getDataType( pvField), getDataLength( pvField), + getDataPtr( (FIELD *)pvField), pui32Number) + : RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getUnicode( + void * pvField, + FLMUNICODE * pUnicode, + FLMUINT * puiBufLen) +{ + return( pvField + ? getUnicode( getDataType( pvField), getDataLength( pvField), + getDataPtr( (FIELD *) pvField), puiBufLen, pUnicode) + : RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getNative( + void * pvField, + char * pszString, + FLMUINT * puiBufLen) +{ + return( pvField + ? storage2Native( getDataType( pvField), getDataLength( pvField), + getDataPtr( (FIELD *) pvField), puiBufLen, pszString) + : RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getBinary( + void * pvField, + void * pvBuf, + FLMUINT * puiBufLen) +{ + if( pvField) + { + *puiBufLen = f_min( *puiBufLen, getDataLength( pvField)); + f_memcpy( pvBuf, getDataPtr( (FIELD *)pvField), *puiBufLen); + return( NE_XFLM_OK); + } + + return( RC_SET( NE_XFLM_NOT_FOUND)); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +FLMBYTE * F_Record::getDataPtr( + FIELD * pField) +{ + if( !pField->uiDataLength) + { + return( NULL); + } + else if( pField->uiDataLength <= sizeof( FLMUINT)) + { + return (FLMBYTE *) &(pField->uiDataOffset); + } + + return( m_pDataBuf + pField->uiDataOffset); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::storage2INT( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMINT * piNum) +{ + RCODE rc = NE_XFLM_OK; + BCD_TYPE bcd; + + if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) + { + goto Exit; + } + + if( bcd.bNegFlag) + { + *piNum = -((FLMINT)bcd.uiNum); + if( !((bcd.uiNibCnt < 11) || + (bcd.uiNibCnt == 11 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, + gv_ucMinBcdINT32, 6) <= 0))))) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + } + else + { + *piNum = (FLMINT)bcd.uiNum; + + if( !((bcd.uiNibCnt < 10) || + (bcd.uiNibCnt == 10 && + (!bcd.pucPtr || (f_memcmp( bcd.pucPtr, + gv_ucMaxBcdINT32, 5) <= 0))))) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::storage2UINT( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMUINT * puiNum) +{ + RCODE rc = NE_XFLM_OK; + BCD_TYPE bcd; + + if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) + { + goto Exit; + } + + *puiNum = bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + else if( bcd.uiNibCnt == 10) + { + if( !(!bcd.pucPtr || + (f_memcmp( bcd.pucPtr, gv_ucMaxBcdUINT32, 5) <= 0))) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + } + else if( bcd.uiNibCnt > 10) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::storage2UINT32( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMUINT32 * pui32Num) +{ + RCODE rc = NE_XFLM_OK; + BCD_TYPE bcd; + + if( RC_BAD( rc = bcd2Num( uiType, uiBufLength, pBuf, &bcd))) + { + goto Exit; + } + + *pui32Num = (FLMUINT32)bcd.uiNum; + + if( bcd.bNegFlag) + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + else if( bcd.uiNibCnt == 10) + { + if( !(!bcd.pucPtr || + (f_memcmp( bcd.pucPtr, gv_ucMaxBcdUINT32, 5) <= 0))) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + } + else if( bcd.uiNibCnt > 10) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::bcd2Num( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + BCD_TYPE * bcd) +{ + RCODE rc = NE_XFLM_OK; + + if( !pBuf) + { + rc = RC_SET( NE_XFLM_CONV_NULL_SRC); + goto Exit; + } + + switch( uiType) + { + case FLM_4x_NUMBER_TYPE: + { + FLMUINT uiTotalNum = 0; + FLMUINT uiByte; + FLMUINT uiNibCnt; + + bcd->pucPtr = pBuf; + + // Get each nibble and use to create the number + +#define FLM_MAX_NIB_CNT 11 + + for( bcd->bNegFlag = + (FLMBOOL)(uiNibCnt = ((*pBuf & 0xF0) == 0xB0) ? 1 : 0); + uiNibCnt <= FLM_MAX_NIB_CNT; + uiNibCnt++ ) + { + uiByte = (uiNibCnt & 0x01) + ? (FLMUINT)(0x0F & *pBuf++) + : (FLMUINT)(*pBuf >> 4); + + if( uiByte == 0x0F) + { + break; + } + + // Multiply by 10 and add n + // NOTE: 10y = 8y + 2y = (y << 3) + (y << 1) + // faster than using the long multiply (10 * y) + + uiTotalNum = (uiTotalNum << 3) + (uiTotalNum << 1) + uiByte; + } + + bcd->uiNibCnt = uiNibCnt; + bcd->uiNum = uiTotalNum; + break; + } + + case FLM_4x_TEXT_TYPE: + { + FLMUINT uiNumber = 0; + + // If it is a TEXT Value, convert to a numeric value + // WARNING: The text is not null terminated + + while( uiBufLength--) + { + if( *pBuf < ASCII_ZERO || *pBuf > ASCII_NINE) + { + break; + } + + uiNumber = (uiNumber * 10) + (*pBuf - ASCII_ZERO); + pBuf++; + } + + bcd->uiNum = uiNumber; + bcd->uiNibCnt = 0; + bcd->bNegFlag = FALSE; + break; + } + + case FLM_4x_CONTEXT_TYPE : + { + if( uiBufLength == sizeof( FLMUINT32)) + { + bcd->uiNum = (FLMUINT)( FB2UD( pBuf)); + bcd->bNegFlag = 0; + + // Now set the uiNibCnt, the uiNibCnt will not be totally + // accurate, but it's close enough to get the value out... + + if( bcd->uiNum < FLM_MAX_UINT8) + { + bcd->uiNibCnt = 3; + } + else if( bcd->uiNum < FLM_MAX_UINT16) + { + bcd->uiNibCnt = 5; + } + else + { + bcd->uiNibCnt = 9; + } + } + + break; + } + + default : + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Record::getUnicode( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiStrBufLen, + FLMUNICODE * puzStrBuf) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucChar; + FLMUINT uiBytesProcessed = 0; + FLMUINT uiBytesOutput = 0; + FLMBOOL bOutputData = FALSE; + FLMUINT uiMaxOutLen; + FLMBYTE ucObjType; + FLMUINT uiObjLength = 0; + FLMBYTE tempBuf[ 80]; + FLMBYTE chrSet, chrVal; + FLMUNICODE newChrVal; + + // If the value is a number, convert to text first + + if( uiType != FLM_4x_TEXT_TYPE) + { + if( !pBuffer) + { + uiBufLength = 0; + } + else + { + if( uiType == FLM_4x_NUMBER_TYPE) + { + uiBufLength = sizeof( tempBuf); + if( RC_BAD( rc = numToText( pBuffer, tempBuf, &uiBufLength))) + { + goto Exit; + } + } + else if( uiType == FLM_4x_TEXT_TYPE) + { + uiBufLength = sizeof( tempBuf); + if( RC_BAD( rc = contextToText( pBuffer, tempBuf, &uiBufLength))) + { + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + pBuffer = &tempBuf[ 0]; + } + } + + uiMaxOutLen = *puiStrBufLen; + if( puzStrBuf != NULL && uiMaxOutLen > 1) + { + bOutputData = TRUE; + uiMaxOutLen -= 2; + } + + // Parse through the string outputting data to the buffer as we go + + while( uiBytesProcessed < uiBufLength) + { + // Determine what we are pointing at + + ucChar = *pBuffer; + ucObjType = (FLMBYTE)textObjType( ucChar); + + switch( ucObjType) + { + case FLM_4x_ASCII_CHAR_CODE: + { + uiObjLength = 1; + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + *puzStrBuf++ = ucChar; + } + uiBytesOutput += 2; + break; + } + + case FLM_4x_CHAR_SET_CODE: + { + uiObjLength = 2; + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + + // Convert WP to UNICODE + + chrSet = ucChar & 0x3F; + chrVal = *(pBuffer + 1); + + if( RC_BAD( rc = flmWPToUnicode( + (((FLMUINT16)chrSet) << 8) | chrVal, &newChrVal))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + *puzStrBuf++ = newChrVal; + } + } + + uiBytesOutput += 2; + break; + } + + case FLM_4x_WHITE_SPACE_CODE: + { + uiObjLength = 1; + + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + + if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x0C)) + { + *puzStrBuf = 9; + } + else if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x0D)) + { + *puzStrBuf = 10; + } + else if( ucChar == (FLM_4x_WHITE_SPACE_CODE | 0x07)) + { + *puzStrBuf = 13; + } + else + { + *puzStrBuf = 0x20; + } + puzStrBuf++; + } + uiBytesOutput += 2; + break; + } + + case FLM_4x_EXT_CHAR_CODE: + { + uiObjLength = 3; + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + + // Convert back from WP to UNICODE + + chrSet = *(pBuffer + 1); + chrVal = *(pBuffer + 2); + + if( RC_BAD( rc = flmWPToUnicode( + (((FLMUINT16)chrSet) << 8) | chrVal, &newChrVal))) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + *puzStrBuf++ = newChrVal; + } + } + uiBytesOutput += 2; + + break; + } + + case FLM_4x_OEM_CODE: + { + uiObjLength = 2; + break; + } + + case FLM_4x_UNICODE_CODE: + { + uiObjLength = 3; + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + *puzStrBuf++ = (*(pBuffer + 1) << 8) + *(pBuffer + 2); + } + uiBytesOutput += 2; + break; + } + + case FLM_4x_UNK_EQ_1_CODE: + { + uiObjLength = 2; + if( bOutputData) + { + if( (uiMaxOutLen < 2) || (uiBytesOutput > uiMaxOutLen - 2)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto GetUNICODE_Output; + } + *puzStrBuf++ = *(pBuffer+1); + } + uiBytesOutput += 2; + break; + } + + default: + { + flmAssert( 0); + uiBytesProcessed = uiBufLength; + break; + } + } + + pBuffer += uiObjLength; + uiBytesProcessed += uiObjLength; + } + +GetUNICODE_Output: + + if( bOutputData) + { + *puzStrBuf = 0; + } + + *puiStrBufLen = uiBytesOutput; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Convert a storage text string into a native string +***************************************************************************/ +RCODE F_Record::storage2Native( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiOutBufLenRV, + char * pOutBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * ptr = pBuffer; + FLMBYTE * pucOut; + FLMBYTE ucChar; + FLMUINT uiBytesProcessed; + FLMUINT uiBytesOutput; + FLMUINT uiValLength = uiBufLength; + FLMBOOL bOutputData = FALSE; + FLMUINT uiMaxOutLen = 0; + FLMBYTE ucObjType; + FLMUINT uiObjLength = 0; + FLMBYTE TempBuf[ 80]; + + // If needed, try and convert the data to text + + if( uiType != FLM_4x_TEXT_TYPE) + { + if( !ptr) + { + uiValLength = 0; + } + else if( uiType == FLM_4x_NUMBER_TYPE) + { + uiValLength = sizeof( TempBuf); + if( RC_BAD( rc = numToText( ptr, TempBuf, &uiValLength))) + { + goto Exit; + } + ptr = &TempBuf[ 0]; + } + else if( uiType == FLM_4x_CONTEXT_TYPE) + { + uiValLength = sizeof( TempBuf); + if( RC_BAD( rc = contextToText( ptr, TempBuf, &uiValLength))) + { + goto Exit; + } + ptr = &TempBuf[ 0]; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + if( pOutBuffer != NULL && *puiOutBufLenRV) + { + bOutputData = TRUE; + uiMaxOutLen = *puiOutBufLenRV - 1; + } + + uiBytesProcessed = 0; + uiBytesOutput = 0; + pucOut = (FLMBYTE *)pOutBuffer; + + // Parse through the string outputting data to the buffer + // as we go + + while( uiBytesProcessed < uiValLength) + { + // Determine what we are pointing at + + ucChar = *ptr; + ucObjType = (FLMBYTE)textObjType( ucChar); + switch( ucObjType) + { + case FLM_4x_ASCII_CHAR_CODE: + { + uiObjLength = 1; + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + *pucOut++ = f_tonative( ucChar); + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + case FLM_4x_CHAR_SET_CODE: + { + uiObjLength = 2; + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + if( (ucChar & (~ucObjType)) == 0) + { + *pucOut++ = f_tonative( *(ptr + 1)); + } + else + { + *pucOut++ = 0xFF; + } + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + case FLM_4x_WHITE_SPACE_CODE: + { + uiObjLength = 1; + + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + ucChar &= (~FLM_4x_WHITE_SPACE_MASK); + if( (ucChar == 0x03) || + (ucChar == 0x04) || + (ucChar == 0x05)) + { + ucChar = ASCII_DASH; + } + else if( ucChar == 0x0C) + { + ucChar = ASCII_TAB; + } + else if( ucChar == 0x0D) + { + ucChar = 0x0A; + } + else if( ucChar == 0x07) + { + ucChar = 0x0D; + } + else + { + ucChar = 0x20; + } + + *pucOut++ = f_tonative( ucChar); + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + case FLM_4x_UNK_EQ_1_CODE: + { + uiObjLength = 2; + + // Skip it if it is not a NATIVE code + + if( (ucChar & (~ucObjType)) == 0x02) + { + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + *pucOut++ = f_tonative( *(ptr + 1)); + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + } + break; + } + + case FLM_4x_EXT_CHAR_CODE: + { + uiObjLength = 3; + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + *pucOut += 0xFF; + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + case FLM_4x_OEM_CODE: + { + uiObjLength = 2; + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + *pucOut++ = f_tonative( *(ptr + 1)); + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + case FLM_4x_UNICODE_CODE: + { + uiObjLength = 3; + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen ) + { + *pucOut++ = 0xFF; + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Native_Output; + } + } + uiBytesOutput++; + break; + } + + default: + { + flmAssert( 0); + break; + } + } + + ptr += uiObjLength; + uiBytesProcessed += uiObjLength; + } + + // Add a terminating NULL character, but DO NOT increment the + // uiBytesOutput counter + +Native_Output: + + if( bOutputData) + { + *pucOut = 0; + } + + *puiOutBufLenRV = uiBytesOutput; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Convert a storage number into a storage text string +***************************************************************************/ +RCODE F_Record::numToText( + FLMBYTE * pucNum, + FLMBYTE * pucOutBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucOutput; + FLMBYTE ucChar; + FLMBYTE ucOutChar; + FLMUINT uiBytesOutput; + FLMBOOL bOutputData; + FLMUINT uiMaxOutLen; + FLMBOOL bFirstNibble; + + uiMaxOutLen = *puiBufLen; + bOutputData = ((pucOutBuffer != NULL) && uiMaxOutLen) ? TRUE : FALSE; + uiBytesOutput = 0; + pucOutput = pucOutBuffer; + + // Parse through the string outputting data to the buffer + // as we go + + if( !pucNum) + { + goto Exit; + } + + bFirstNibble = TRUE; + for( ;;) + { + if( bFirstNibble) + { + ucChar = (FLMBYTE)(*pucNum >> 4); + } + else + { + ucChar = (FLMBYTE)(*pucNum++ & 0x0F); + } + + bFirstNibble = !bFirstNibble; + + if( ucChar <= 9) + { + ucOutChar = (FLMBYTE)( ASCII_ZERO + ucChar); + } + else if( ucChar == 0x0F) + { + break; + } + else + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( bOutputData) + { + if( uiBytesOutput < uiMaxOutLen) + { + *pucOutput++ = ucOutChar; + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + + uiBytesOutput++; + } + +Exit: + + *puiBufLen = uiBytesOutput; + return( rc); +} + +/*************************************************************************** +Desc: Convert a context value into a storage text string +***************************************************************************/ +RCODE F_Record::contextToText( + FLMBYTE * pucValue, + FLMBYTE * pucOutBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiMaxOutLen; + FLMUINT uiStrLen; + FLMUINT uiBytesOutput = 0; + FLMBOOL bOutputData; + FLMBYTE ucTmpBuf[ 32]; + + uiMaxOutLen = *puiBufLen; + bOutputData = ((pucOutBuffer != NULL) && uiMaxOutLen) ? TRUE : FALSE; + + if( !pucValue) + { + goto Exit; + } + + f_sprintf( (char *)ucTmpBuf, "%u", FB2UD( pucValue)); + uiStrLen = f_strlen( ucTmpBuf) + 1; + uiBytesOutput = f_min( uiStrLen, uiMaxOutLen); + + if( bOutputData) + { + f_memcpy( pucOutBuffer, ucTmpBuf, uiBytesOutput); + pucOutBuffer[ uiBytesOutput - 1] = 0; + } + + if( uiMaxOutLen < uiBytesOutput) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + +Exit: + + *puiBufLen = uiBytesOutput; + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getRootBlock( + F_4x_LFILE * pLFile, + BTSK * pStack) +{ + RCODE rc = NE_XFLM_OK; + F_Block * pBlock = NULL; + + if( RC_BAD( rc = readBlock( pLFile->uiRootBlk, &pBlock))) + { + goto Exit; + } + + if( !(FLM_4x_BH_IS_ROOT_BLK( pBlock->m_pucBlk)) || + (pLFile->uiLfNum != FB2UW( &pBlock->m_pucBlk[ + FLM_4x_BH_LOG_FILE_NUM]))) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Set up the stack + + blkToStack( &pBlock, pStack); + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getBlock( + F_4x_LFILE * pLFile, + FLMUINT uiBlkAddr, + BTSK * pStack) +{ + RCODE rc = NE_XFLM_OK; + F_Block * pBlock = NULL; + + if( RC_BAD( rc = readBlock( uiBlkAddr, &pBlock))) + { + goto Exit; + } + + if( pLFile->uiLfNum != FB2UW( &pBlock->m_pucBlk[ + FLM_4x_BH_LOG_FILE_NUM])) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Set up the stack + + blkToStack( &pBlock, pStack); + +Exit: + + if( pBlock) + { + pBlock->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_4xReader::blkToStack( + F_Block ** ppBlock, + BTSK * pStack) +{ + FLMBYTE * pucBlk = (*ppBlock)->m_pucBlk; + FLMUINT uiBlkType; + + uiBlkType = (FLMUINT)(FLM_4x_BH_GET_TYPE( pucBlk)); + pStack->uiBlkType = uiBlkType; + + if( uiBlkType == FLM_4x_BHT_LEAF) + { + pStack->uiElmOvhd = FLM_4x_BBE_KEY; + } + else if( uiBlkType == FLM_4x_BHT_NON_LEAF_DATA) + { + pStack->uiElmOvhd = FLM_4x_BNE_DATA_OVHD; + } + else if( uiBlkType == FLM_4x_BHT_NON_LEAF) + { + pStack->uiElmOvhd = FLM_4x_BNE_KEY_START; + } + else if( uiBlkType == FLM_4x_BHT_NON_LEAF_COUNTS) + { + pStack->uiElmOvhd = FLM_4x_BNE_KEY_COUNTS_START; + } + else + { + flmAssert( 0); + pStack->uiElmOvhd = FLM_4x_BNE_KEY_START; + } + + pStack->uiKeyLen = 0; + pStack->uiPKC = 0; + pStack->uiPrevElmPKC = 0; + pStack->uiCurElm = FLM_4x_BH_OVHD; + pStack->uiBlkEnd = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END]); + pStack->uiLevel = (FLMUINT)pucBlk[ FLM_4x_BH_LEVEL ]; + + if( pStack->pBlk) + { + pStack->pBlk->Release(); + } + + pStack->pBlk = *ppBlock; + *ppBlock = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getFieldType( + FLMUINT uiFieldNum, + FLMUINT * puiType) +{ + RCODE rc = NE_XFLM_OK; + + if( uiFieldNum < m_uiFieldTblSize) + { + if( (*puiType = m_puiFieldTbl[ uiFieldNum - 1]) == + FLM_4x_UNKNOWN_TYPE) + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + } + else + { + // Check if the field is a FLAIM dictionary field. + // Most of these fields are TEXT fields. + + if( (uiFieldNum >= FLM_4x_DICT_FIELD_NUMS) && + (uiFieldNum <= FLM_4x_LAST_DICT_FIELD_NUM)) + { + *puiType = FLM_4x_TEXT_TYPE; + } + else if( uiFieldNum >= FLM_4x_UNREGISTERED_TAGS) + { + *puiType = FLM_4x_TEXT_TYPE; + } + else + { + rc = RC_SET( NE_XFLM_BAD_ELEMENT_NUM); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::readDictionary( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiType; + FLMUINT uiFieldNum; + F_Record * pRec = NULL; + void * pvField; + + if( m_puiFieldTbl) + { + f_free( &m_puiFieldTbl); + } + m_uiFieldTblSize = 0; + + if( RC_BAD( rc = f_alloc( FLM_4x_RESERVED_TAG_NUMS, + &m_puiFieldTbl))) + { + goto Exit; + } + + m_uiFieldTblSize = FLM_4x_RESERVED_TAG_NUMS; + f_memset( m_puiFieldTbl, FLM_4x_UNKNOWN_TYPE, m_uiFieldTblSize); + setDefaultContainer( FLM_4x_DICT_CONTAINER); + + for( ;;) + { + if( RC_BAD( rc = retrieveNextRec( &pRec))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if( pRec->getFieldID( pRec->root()) == FLM_4x_FIELD_TAG) + { + pvField = pRec->firstChild( pRec->root()); + if( RC_BAD( rc = getTypeTag( pRec, pvField, &uiType))) + { + goto Exit; + } + + uiFieldNum = pRec->getID(); + flmAssert( uiFieldNum < FLM_4x_RESERVED_TAG_NUMS); + m_puiFieldTbl[ uiFieldNum - 1] = uiType; + } + } + +Exit: + + if( pRec) + { + pRec->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_4xReader::getTypeTag( + F_Record * pRec, + void * pvField, + FLMUINT * puiType) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiType; + FLMUINT uiBufLen; + char szTmpBuf[ 64]; + + uiBufLen = sizeof( szTmpBuf); + if( RC_BAD( rc = pRec->getNative( pvField, szTmpBuf, &uiBufLen))) + { + goto Exit; + } + + if( f_strnicmp( szTmpBuf, "text", 4) == 0) + { + uiType = FLM_4x_TEXT_TYPE; + } + else if( f_strnicmp( szTmpBuf, "numb", 4) == 0) + { + uiType = FLM_4x_NUMBER_TYPE; + } + else if( f_strnicmp( szTmpBuf, "bina", 4) == 0) + { + uiType = FLM_4x_BINARY_TYPE; + } + else if( f_strnicmp( szTmpBuf, "cont", 4) == 0) + { + uiType = FLM_4x_CONTEXT_TYPE; + } + else if( f_strnicmp( szTmpBuf, "blob", 4) == 0) + { + uiType = FLM_4x_BLOB_TYPE; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + *puiType = uiType; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_4xNameTable::F_4xNameTable() +{ + m_pool.poolInit( 1024); + m_ppSortedByTagName = NULL; + m_ppSortedByTagNum = NULL; + m_ppSortedByTagTypeAndName = NULL; + m_uiTblSize = 0; + m_uiNumTags = 0; + m_bTablesSorted = FALSE; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_4xNameTable::~F_4xNameTable() +{ + clearTable(); + m_pool.poolFree(); +} + +/**************************************************************************** +Desc: Free everything in the table +****************************************************************************/ +void F_4xNameTable::clearTable( void) +{ + m_pool.poolFree(); + m_pool.poolInit( 1024); + + // NOTE: Only one allocation is used for m_ppSortedByTagName, + // m_ppSortedByTagNum, and m_ppSortedByTagTypeAndName - there is no + // need to free m_ppSortedByTagNum and m_ppSortedByTagTypeAndName. + + if (m_ppSortedByTagName) + { + f_free( &m_ppSortedByTagName); + m_ppSortedByTagNum = NULL; + m_ppSortedByTagTypeAndName = NULL; + m_uiTblSize = 0; + m_uiNumTags = 0; + } +} + +/**************************************************************************** +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 insensitive for the ASCII characters A-Z. +****************************************************************************/ +FLMINT F_4xNameTable::tagNameCompare( + const FLMUNICODE * puzName1, // If NULL, use pszName1 for comparison + const char * pszName1, + const FLMUNICODE * puzName2) +{ + FLMUNICODE uzChar1; + FLMUNICODE uzChar2; + + if (puzName1) + { + for (;;) + { + uzChar1 = *puzName1; + uzChar2 = *puzName2; + + // Convert to lower case for comparison. + + if (uzChar1 >= 'A' && uzChar1 <= 'Z') + { + uzChar1 = uzChar1 - 'A' + 'a'; + } + if (uzChar2 >= 'A' && uzChar2 <= 'Z') + { + uzChar2 = uzChar2 - 'A' + 'a'; + } + + if (!uzChar1 || !uzChar2 || uzChar1 != uzChar2) + { + break; + } + + puzName1++; + puzName2++; + } + } + else + { + for (;;) + { + uzChar1 = (FLMUNICODE)*pszName1; + uzChar2 = *puzName2; + + // Convert to lower case for comparison. + + if (uzChar1 >= 'A' && uzChar1 <= 'Z') + { + uzChar1 = uzChar1 - 'A' + 'a'; + } + if (uzChar2 >= 'A' && uzChar2 <= 'Z') + { + uzChar2 = uzChar2 - 'A' + 'a'; + } + + if (!uzChar1 || !uzChar2 || uzChar1 != uzChar2) + { + break; + } + + pszName1++; + puzName2++; + } + } + + if (uzChar1) + { + return( (FLMINT)((uzChar2 && uzChar1 < uzChar2) + ? (FLMINT)-1 + : (FLMINT)1)); + } + else if (uzChar2) + { + return( -1); + } + else + { + return( 0); + } +} + +/**************************************************************************** +Desc: Lookup a tag by 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_4x_TAG_INFO * F_4xNameTable::findTagByName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT * puiInsertPos) +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMINT iCmp; + + // 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; + iCmp = tagNameCompare( puzTagName, pszTagName, + m_ppSortedByTagName [uiMid]->puzTagName); + if (iCmp == 0) + { + + // Found Match + + pTagInfo = m_ppSortedByTagName [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: Lookup a tag by tag number. +****************************************************************************/ +FLM_4x_TAG_INFO * F_4xNameTable::findTagByNum( + FLMUINT uiTagNum, + FLMUINT * puiInsertPos) +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblTagNum; + + // 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; + uiTblTagNum = m_ppSortedByTagNum [uiMid]->uiTagNum; + if (uiTagNum == uiTblTagNum) + { + + // Found Match + + pTagInfo = m_ppSortedByTagNum [uiMid]; + if (puiInsertPos) + { + *puiInsertPos = uiMid; + } + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + + // Done, item not found + + if (puiInsertPos) + { + *puiInsertPos = (uiTagNum < uiTblTagNum) + ? uiMid + : uiMid + 1; + } + goto Exit; + } + + if (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_4x_TAG_INFO * F_4xNameTable::findTagByTypeAndName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiType, + FLMUINT * puiInsertPos) +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + FLMUINT uiTblType; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMINT iCmp; + + // 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_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) + { + + // 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. +****************************************************************************/ +void F_4xNameTable::copyTagName( + FLMUNICODE * puzDestTagName, + char * pszDestTagName, + FLMUINT uiDestBufSize, // Bytes, must be enough for null terminator + FLMUNICODE * puzSrcTagName) +{ + if (puzDestTagName) + { + + // Decrement name buffer size by sizeof( FLMUNICODE) to allow for a + // terminating NULL character. uiDestBufSize better be at list big + // enough for a null terminating character. + + flmAssert( uiDestBufSize >= sizeof( FLMUNICODE)); + uiDestBufSize -= sizeof( FLMUNICODE); + + // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters + // will be returned as question marks (?). + + while (uiDestBufSize >= sizeof( FLMUNICODE) && *puzSrcTagName) + { + *puzDestTagName++ = *puzSrcTagName; + uiDestBufSize -= sizeof( FLMUNICODE); + puzSrcTagName++; + } + *puzDestTagName = 0; + } + else + { + // Decrement name buffer size by one to allow for a terminating + // NULL character. uiDestBufSize better be at list big + // enough for a null terminating character. + + flmAssert( uiDestBufSize); + uiDestBufSize--; + + // Copy the name to the NATIVE buffer. Non-Ascii UNICODE characters + // will be returned as question marks (?). + + while (uiDestBufSize && *puzSrcTagName) + { + if (*puzSrcTagName <= 127) + { + *pszDestTagName++ = (char)*puzSrcTagName; + } + else + { + *pszDestTagName++ = '?'; + } + uiDestBufSize--; + puzSrcTagName++; + } + *pszDestTagName = 0; + } +} + +/*************************************************************************** +Desc: Sort an array of SCACHE pointers by their block address. +****************************************************************************/ +void F_4xNameTable::sortTagTbl( + FLM_4x_TAG_INFO ** ppTagInfoTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + FLM_4x_TAG_COMPARE_FUNC fnTagCompare) +{ + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + FLM_4x_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: Allocate a new tag info structure and set it up. +****************************************************************************/ +RCODE F_4xNameTable::allocTag( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiType, + FLMUINT uiSubType, + FLM_4x_TAG_INFO ** ppTagInfo) +{ + RCODE rc = NE_XFLM_OK; + void * pvMark; + FLM_4x_TAG_INFO * pTagInfo; + FLMUINT uiNameSize; + FLMUNICODE * puzTmp; + + // Create a new tag info structure. + + pvMark = m_pool.poolMark(); + if( RC_BAD( rc = m_pool.poolAlloc( sizeof( FLM_4x_TAG_INFO), + (void **)&pTagInfo))) + { + goto Exit; + } + + // 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; + } + 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; + } + puzTmp = pTagInfo->puzTagName; + while (*pszTagName) + { + *puzTmp++ = (FLMUNICODE)*pszTagName; + pszTagName++; + } + *puzTmp = 0; + } + pTagInfo->uiTagNum = uiTagNum; + pTagInfo->uiType = uiType; + pTagInfo->uiSubType = uiSubType; + +Exit: + + if (RC_BAD( rc)) + { + m_pool.poolReset( pvMark); + pTagInfo = NULL; + } + *ppTagInfo = pTagInfo; + + return( rc); +} + +/**************************************************************************** +Desc: Allocate the sort tables. +****************************************************************************/ +RCODE F_4xNameTable::reallocSortTables( + FLMUINT uiNewTblSize) +{ + RCODE rc = NE_XFLM_OK; + FLM_4x_TAG_INFO ** ppNewTbl; + + if( RC_BAD( rc = f_alloc( + sizeof( FLM_4x_TAG_INFO *) * uiNewTblSize * 3, &ppNewTbl))) + { + goto Exit; + } + + // Copy the old tables into the new. + + if (m_uiNumTags) + { + f_memcpy( ppNewTbl, m_ppSortedByTagName, + sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); + f_memcpy( &ppNewTbl [uiNewTblSize], m_ppSortedByTagNum, + sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); + f_memcpy( &ppNewTbl [uiNewTblSize + uiNewTblSize], + m_ppSortedByTagTypeAndName, + sizeof( FLM_4x_TAG_INFO *) * m_uiNumTags); + f_free( &m_ppSortedByTagName); + } + m_ppSortedByTagName = ppNewTbl; + m_ppSortedByTagNum = &ppNewTbl [uiNewTblSize]; + m_ppSortedByTagTypeAndName = &ppNewTbl [uiNewTblSize + uiNewTblSize]; + m_uiTblSize = uiNewTblSize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a tag name, number, etc. using 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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getNextTagNumOrder( + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiType, // May be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + + if (!m_bTablesSorted) + { + sortTags(); + } + + if (*puiNextPos < m_uiNumTags) + { + pTagInfo = m_ppSortedByTagNum [*puiNextPos]; + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if (puiType) + { + *puiType = pTagInfo->uiType; + } + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + + if( puzTagName || pszTagName) + { + copyTagName( puzTagName, pszTagName, uiNameBufSize, + pTagInfo->puzTagName); + } + + // Returned *puiNextPos should be the next one to retrieve. + + (*puiNextPos)++; + } + else + { + // Nothing more in list, but initialize return variables anyway. + + if (puzTagName) + { + *puzTagName = 0; + } + + if (pszTagName) + { + *pszTagName = 0; + } + + if (puiTagNum) + { + *puiTagNum = 0; + } + + if (puiType) + { + *puiType = 0; + } + + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +Desc: Get a tag name, number, etc. using 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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getNextTagNameOrder( + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiType, // May be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + + if (!m_bTablesSorted) + { + sortTags(); + } + + if (*puiNextPos < m_uiNumTags) + { + pTagInfo = m_ppSortedByTagName [*puiNextPos]; + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if (puiType) + { + *puiType = pTagInfo->uiType; + } + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + + if( puzTagName || pszTagName) + { + copyTagName( puzTagName, pszTagName, uiNameBufSize, + pTagInfo->puzTagName); + } + + // Returned *puiNextPos should be the next one to retrieve. + + (*puiNextPos)++; + } + else + { + // Nothing more in list, but initialize return variables anyway. + + if (puzTagName) + { + *puzTagName = 0; + } + + if (pszTagName) + { + *pszTagName = 0; + } + + if (puiTagNum) + { + *puiTagNum = 0; + } + + if (puiType) + { + *puiType = 0; + } + + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +Desc: Get a tag name and number from type. 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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getFromTagType( + FLMUINT uiType, + FLMUINT * puiNextPos, // To get first, initialize to zero. + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, // In bytes - must be at least sizeof( FLMUNICODE) + FLMUINT * puiTagNum, // May be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo = NULL; + + if (!m_bTablesSorted) + { + sortTags(); + } + if (*puiNextPos == 0) + { + + // A value of zero indicates we should try to find the first + // one. + + (void)findTagByTypeAndName( NULL, "", uiType, puiNextPos); + if (*puiNextPos < m_uiNumTags && + m_ppSortedByTagTypeAndName [*puiNextPos]->uiType != uiType) + { + (*puiNextPos)++; + } + } + + if (*puiNextPos < m_uiNumTags && + m_ppSortedByTagTypeAndName [*puiNextPos]->uiType == uiType) + { + pTagInfo = m_ppSortedByTagTypeAndName [*puiNextPos]; + + if (puiTagNum) + { + *puiTagNum = pTagInfo->uiTagNum; + } + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + + if( puzTagName || pszTagName) + { + copyTagName( puzTagName, pszTagName, uiNameBufSize, + pTagInfo->puzTagName); + } + + // Returned *puiNextPos should be the next one to retrieve, so that + // it is not zero. + + (*puiNextPos)++; + } + else + { + // Type was not found, but initialize return variables anyway. + + if (puzTagName) + { + *puzTagName = 0; + } + if (pszTagName) + { + *pszTagName = 0; + } + if (puiTagNum) + { + *puiTagNum = 0; + } + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +Desc: Get a tag name from its tag 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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getFromTagNum( + FLMUINT uiTagNum, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, // In bytes, at least enough for null char. + FLMUINT * puiType, // May be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo; + + if (!m_bTablesSorted) + { + sortTags(); + } + + if ((pTagInfo = findTagByNum( uiTagNum)) != NULL) + { + if (puiType) + { + *puiType = pTagInfo->uiType; + } + + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + + if( puzTagName || pszTagName) + { + copyTagName( puzTagName, pszTagName, uiNameBufSize, + pTagInfo->puzTagName); + } + } + else + { + // Tag number was not found, but initialize return variables anyway. + + if (puzTagName) + { + *puzTagName = 0; + } + + if (pszTagName) + { + *pszTagName = 0; + } + + if (puiType) + { + *puiType = 0; + } + + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +Desc: Get a tag number and type from its tag name. 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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getFromTagName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT * puiTagNum, // Cannot be NULL + FLMUINT * puiType, // May be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo; + + if (!m_bTablesSorted) + { + sortTags(); + } + + if ((pTagInfo = findTagByName( puzTagName, pszTagName)) != NULL) + { + flmAssert( puiTagNum); + *puiTagNum = pTagInfo->uiTagNum; + if (puiType) + { + *puiType = pTagInfo->uiType; + } + + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + } + else + { + // Tag name was not found, but initialize return variables anyway. + + *puiTagNum = 0; + if (puiType) + { + *puiType = 0; + } + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +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. +****************************************************************************/ +FLMBOOL F_4xNameTable::getFromTagTypeAndName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiType, + FLMUINT * puiTagNum, // Cannot be NULL + FLMUINT * puiSubType) // May be NULL +{ + FLM_4x_TAG_INFO * pTagInfo; + + if (!m_bTablesSorted) + { + sortTags(); + } + + if ((pTagInfo = findTagByTypeAndName( puzTagName, pszTagName, + uiType)) != NULL) + { + flmAssert( puiTagNum); + *puiTagNum = pTagInfo->uiTagNum; + if (puiSubType) + { + *puiSubType = pTagInfo->uiSubType; + } + } + else + { + + // Tag name was not found, but initialize return variables anyway. + + *puiTagNum = 0; + if (puiSubType) + { + *puiSubType = 0; + } + } + + return( (FLMBOOL)(pTagInfo ? (FLMBOOL)TRUE : (FLMBOOL)FALSE)); +} + +/**************************************************************************** +Desc: Insert a tag info structure into the sorted tables at the + specified positions. +****************************************************************************/ +RCODE F_4xNameTable::insertTagInTables( + FLM_4x_TAG_INFO * pTagInfo, + FLMUINT uiTagNameTblInsertPos, + FLMUINT uiTagTypeAndNameTblInsertPos, + FLMUINT uiTagNumTblInsertPos) +{ + RCODE rc = NE_XFLM_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-name table + + uiLoop = m_uiNumTags; + while (uiLoop > uiTagNameTblInsertPos) + { + m_ppSortedByTagName [uiLoop] = m_ppSortedByTagName [uiLoop - 1]; + uiLoop--; + } + m_ppSortedByTagName [uiTagNameTblInsertPos] = pTagInfo; + + // Insert into the sorted-by-number table + + uiLoop = m_uiNumTags; + while (uiLoop > uiTagNumTblInsertPos) + { + m_ppSortedByTagNum [uiLoop] = m_ppSortedByTagNum [uiLoop - 1]; + uiLoop--; + } + m_ppSortedByTagNum [uiTagNumTblInsertPos] = pTagInfo; + + // Insert into the sorted-by-tag-name-and-type table + + uiLoop = m_uiNumTags; + while (uiLoop > uiTagTypeAndNameTblInsertPos) + { + m_ppSortedByTagTypeAndName [uiLoop] = + m_ppSortedByTagTypeAndName [uiLoop - 1]; + uiLoop--; + } + m_ppSortedByTagTypeAndName [uiTagTypeAndNameTblInsertPos] = 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 F_4xNameTable::addTag( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiType, + FLMUINT uiSubType, + FLMBOOL bCheckDuplicates) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTagNameTblInsertPos; + FLMUINT uiTagTypeAndNameTblInsertPos; + FLMUINT uiTagNumTblInsertPos; + FLM_4x_TAG_INFO * pTagInfo; + + // 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_XFLM_INVALID_PARM); + goto Exit; + } + + // Tag number of zero not allowed. + + if (!uiTagNum) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + // Tables must be sorted in order for this to work + + if (bCheckDuplicates) + { + if (!m_bTablesSorted) + { + sortTags(); + } + + // Make sure that the tag name is not already used. + + if (findTagByName( puzTagName, pszTagName, &uiTagNameTblInsertPos)) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + + // Make sure that the tag name + type is not already used. + + if (findTagByTypeAndName( puzTagName, pszTagName, + uiType, &uiTagTypeAndNameTblInsertPos)) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + + // Make sure that the tag number is not already used. + + if (findTagByNum( uiTagNum, &uiTagNumTblInsertPos)) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + } + else + { + uiTagNameTblInsertPos = + uiTagTypeAndNameTblInsertPos = + uiTagNumTblInsertPos = m_uiNumTags; + m_bTablesSorted = FALSE; + } + + // Create a new tag info structure. + + if (RC_BAD( rc = allocTag( puzTagName, pszTagName, uiTagNum, uiType, + uiSubType, &pTagInfo))) + { + goto Exit; + } + + // Insert the tag structure into the appropriate places in the + // sorted tables. + + if (RC_BAD( rc = insertTagInTables( pTagInfo, uiTagNameTblInsertPos, + uiTagTypeAndNameTblInsertPos, + uiTagNumTblInsertPos))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Sort the tag tables according to their respective criteria. +****************************************************************************/ +void F_4xNameTable::sortTags( void) +{ + if (!m_bTablesSorted && m_uiNumTags > 1) + { + sortTagTbl( m_ppSortedByTagName, 0, m_uiNumTags - 1, + compareTagNameOnly); + sortTagTbl( m_ppSortedByTagNum, 0, m_uiNumTags - 1, + compareTagNumOnly); + sortTagTbl( m_ppSortedByTagTypeAndName, 0, m_uiNumTags - 1, + compareTagTypeAndName); + } + m_bTablesSorted = TRUE; +} + +/**************************************************************************** +Desc: Initialize a name table from a database. +****************************************************************************/ +RCODE F_4xNameTable::setupNameTable( + F_4xReader * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_Record * pRec = NULL; + FLMUINT uiDrn; + FLMUINT uiLoop; + void * pvField; + FLMUNICODE uzName[ 60]; + FLMUNICODE * puzName = &uzName[ 0]; + FLMUINT uiNameLen = sizeof( uzName); + FLMUINT uiLen; + FLMUINT uiSubType; + + // Clean out all existing tags, if any. + + clearTable(); + + // Find the next DRN in the dictionary container + + if( RC_BAD( rc = pDb->getNextDrn( FLM_4x_DICT_CONTAINER, &uiDrn))) + { + goto Exit; + } + + // Count the reserved tags + + for (uiLoop = 0; Flm4xDictTagInfo[ uiLoop].pszTagName; uiLoop++) + { + ; + } + + // Preallocate space so we don't have to do it over and over. + + if( RC_BAD( rc = reallocSortTables( uiLoop + uiDrn))) + { + goto Exit; + } + + // Add in all of the reserved dictionary tags. + + for( uiLoop = 0; Flm4xDictTagInfo[ uiLoop].pszTagName; uiLoop++) + { + if( RC_BAD( rc = addTag( NULL, + Flm4xDictTagInfo[ uiLoop].pszTagName, + Flm4xDictTagInfo[ uiLoop].uiTagNum, + FLM_4x_FIELD_TAG, + Flm4xDictTagInfo[ uiLoop].uiFieldType, FALSE))) + { + goto Exit; + } + } + + // Read through all of the dictionary records + + pDb->setDefaultContainer( FLM_4x_DICT_CONTAINER); + for( ;;) + { + if( RC_BAD( rc = pDb->retrieveNextRec( &pRec))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + pvField = pRec->root(); + uiDrn = pRec->getID(); + + // Get the unicode name length (does not include NULL terminator) + + if( RC_BAD( rc = pRec->getUnicode( pvField, NULL, &uiLen))) + { + goto Exit; + } + + // Account for NULL character. + + uiLen += sizeof( FLMUNICODE); + + // See if we need a larger buffer to get the name. + + if (uiLen > uiNameLen) + { + FLMUNICODE * puzTmp; + + // Add enough for 60 more unicode characters. + + uiLen += (60 * sizeof( FLMUNICODE)); + + if( RC_BAD( rc = f_alloc( uiLen, &puzTmp))) + { + goto Exit; + } + + if (puzName != &uzName [0]) + { + f_free( &puzName); + } + + puzName = puzTmp; + uiNameLen = uiLen; + } + + // Get the tag name. + + uiLen = uiNameLen; + if (RC_BAD( rc = pRec->getUnicode( pvField, puzName, &uiLen))) + { + goto Exit; + } + + // Get the sub-type. + + if (pRec->getFieldID( pvField) == FLM_4x_FIELD_TAG) + { + void * pvFld = pRec->find( pvField, FLM_4x_TYPE_TAG, 1, FALSE); + + if (!pvFld || + RC_BAD( pDb->getTypeTag( pRec, pvFld, &uiSubType))) + { + uiSubType = FLM_4x_TEXT_TYPE; + } + } + else + { + uiSubType = 0; + } + + // Add tag to table, without sorting yet. + + if (RC_BAD( rc = addTag( puzName, NULL, uiDrn, + pRec->getFieldID( pvField), uiSubType, FALSE))) + { + goto Exit; + } + } + + sortTags(); + +Exit: + + if( RC_BAD( rc)) + { + clearTable(); + } + + if( pRec) + { + pRec->Release(); + } + + if( puzName != &uzName [0]) + { + f_free( &puzName); + } + + return( rc); +} diff --git a/version5/src/frecread.h b/version5/src/frecread.h new file mode 100644 index 0000000..a3955d1 --- /dev/null +++ b/version5/src/frecread.h @@ -0,0 +1,1184 @@ +//------------------------------------------------------------------------------ +// Desc: Contains interfaces for reading FLAIM 4.x databases. +// +// 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: frecread.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FRECREAD_H +#define FRECREAD_H + +class F_Block; +class F_4xNameTable; + +#define FLM_4x_RESERVED_TAG_NUMS 32100 +#define FLM_4x_UNREGISTERED_TAGS 32769 +#define FLM_4x_DICT_CONTAINER 32000 +#define FLM_4x_DATA_CONTAINER 32001 +#define FLM_4x_TRACKER_CONTAINER 32002 +#define FLM_4x_DICT_INDEX 32003 +#define FLM_4x_DICT_FIELD_NUMS 32100 +#define FLM_4x_LAST_DICT_FIELD_NUM 32175 + +#define FLM_4x_BH_MAX_LEVELS 8 +#define FLM_4x_BH_CHECKSUM_LOW 0 +#define FLM_4x_BH_ADDR 0 +#define FLM_4x_BH_PREV_BLK 4 +#define FLM_4x_BH_NEXT_BLK 8 +#define FLM_4x_BH_TYPE 12 +#define FLM_4x_BH_LEVEL 13 +#define FLM_4x_BH_ELM_END 14 +#define FLM_4x_BH_TRANS_ID 16 +#define FLM_4x_BH_PREV_TRANS_ID 20 +#define FLM_4x_BH_PREV_BLK_ADDR 24 +#define FLM_4x_BH_LOG_FILE_NUM 28 +#define FLM_4x_BH_CHECKSUM_HIGH 31 +#define FLM_4x_BH_OVHD 32 + +#define FLM_4x_BT_END 0xFFFFFFFF + +#define FLM_4x_BHT_FREE 0 // Free block - avail list +#define FLM_4x_BHT_LEAF 1 // Leaf block +#define FLM_4x_BHT_LFH_BLK 4 // LFH Header block +#define FLM_4x_BHT_NON_LEAF 6 // Non-leaf block - variable key size +#define FLM_4x_BHT_NON_LEAF_DATA 7 // Non-leaf block data block - fixed key size +#define FLM_4x_BHT_NON_LEAF_COUNTS 8 // Non-leaf index with counts + +#define FLM_4x_BH_GET_TYPE(blk) (((blk)[ FLM_4x_BH_TYPE]) & 0x0F) +#define FLM_4x_BH_IS_ROOT_BLK(blk) (((blk)[ FLM_4x_BH_TYPE]) & 0x80) + +#define FLM_4x_FOP_RECORD_INFO 0xB0 +#define FLM_4x_FOP_IS_RECORD_INFO(p) ((*(p) & 0xFE) == FLM_4x_FOP_RECORD_INFO) + +#define FLM_4x_TEXT_TYPE 0 +#define FLM_4x_NUMBER_TYPE 1 +#define FLM_4x_BINARY_TYPE 2 +#define FLM_4x_CONTEXT_TYPE 3 +#define FLM_4x_BLOB_TYPE 8 +#define FLM_4x_UNKNOWN_TYPE 0xFFFFFFFF + +#define FLM_4x_FIELD_TAG (FLM_4x_RESERVED_TAG_NUMS + 0) +#define FLM_4x_INDEX_TAG (FLM_4x_RESERVED_TAG_NUMS + 1) +#define FLM_4x_TYPE_TAG (FLM_4x_RESERVED_TAG_NUMS + 2) +#define FLM_4x_CONTAINER_TAG (FLM_4x_RESERVED_TAG_NUMS + 4) +#define FLM_4x_LANGUAGE_TAG (FLM_4x_RESERVED_TAG_NUMS + 5) +#define FLM_4x_OPTIONAL_TAG (FLM_4x_RESERVED_TAG_NUMS + 6) +#define FLM_4x_UNIQUE_TAG (FLM_4x_RESERVED_TAG_NUMS + 7) +#define FLM_4x_KEY_TAG (FLM_4x_RESERVED_TAG_NUMS + 8) +#define FLM_4x_REFS_TAG (FLM_4x_RESERVED_TAG_NUMS + 9) +#define FLM_4x_AREA_TAG (FLM_4x_RESERVED_TAG_NUMS + 17) +#define FLM_4x_STATE_TAG (FLM_4x_RESERVED_TAG_NUMS + 25) +#define FLM_4x_BLOB_TAG (FLM_4x_RESERVED_TAG_NUMS + 26) +#define FLM_4x_THRESHOLD_TAG (FLM_4x_RESERVED_TAG_NUMS + 27) +#define FLM_4x_SUFFIX_TAG (FLM_4x_RESERVED_TAG_NUMS + 29) +#define FLM_4x_SUBDIRECTORY_TAG (FLM_4x_RESERVED_TAG_NUMS + 30) +#define FLM_4x_RESERVED_TAG (FLM_4x_RESERVED_TAG_NUMS + 31) +#define FLM_4x_SUBNAME_TAG (FLM_4x_RESERVED_TAG_NUMS + 32) +#define FLM_4x_NAME_TAG (FLM_4x_RESERVED_TAG_NUMS + 33) +#define FLM_4x_BASE_TAG (FLM_4x_RESERVED_TAG_NUMS + 36) +#define FLM_4x_CASE_TAG (FLM_4x_RESERVED_TAG_NUMS + 38) +#define FLM_4x_COMBINATIONS_TAG (FLM_4x_RESERVED_TAG_NUMS + 40) +#define FLM_4x_COUNT_TAG (FLM_4x_RESERVED_TAG_NUMS + 41) +#define FLM_4x_POSITIONING_TAG (FLM_4x_RESERVED_TAG_NUMS + 42) +#define FLM_4x_PAIRED_TAG (FLM_4x_RESERVED_TAG_NUMS + 44) +#define FLM_4x_PARENT_TAG (FLM_4x_RESERVED_TAG_NUMS + 45) +#define FLM_4x_POST_TAG (FLM_4x_RESERVED_TAG_NUMS + 46) +#define FLM_4x_REQUIRED_TAG (FLM_4x_RESERVED_TAG_NUMS + 47) +#define FLM_4x_USE_TAG (FLM_4x_RESERVED_TAG_NUMS + 48) +#define FLM_4x_FILTER_TAG (FLM_4x_RESERVED_TAG_NUMS + 49) +#define FLM_4x_LIMIT_TAG (FLM_4x_RESERVED_TAG_NUMS + 50) +#define FLM_4x_DICT_TAG (FLM_4x_RESERVED_TAG_NUMS + 54) +#define FLM_4x_RECINFO_TAG (FLM_4x_RESERVED_TAG_NUMS + 70) +#define FLM_4x_DRN_TAG (FLM_4x_RESERVED_TAG_NUMS + 71) +#define FLM_4x_DICT_SEQ_TAG (FLM_4x_RESERVED_TAG_NUMS + 72) +#define FLM_4x_LAST_CONTAINER_INDEXED_TAG (FLM_4x_RESERVED_TAG_NUMS + 73) +#define FLM_4x_LAST_DRN_INDEXED_TAG (FLM_4x_RESERVED_TAG_NUMS + 74) +#define FLM_4x_ONLINE_TRANS_ID_TAG (FLM_4x_RESERVED_TAG_NUMS + 75) + +#define FLM_4x_FIELD_TAG_NAME "Field" +#define FLM_4x_INDEX_TAG_NAME "Index" +#define FLM_4x_TYPE_TAG_NAME "Type" +#define FLM_4x_CONTAINER_TAG_NAME "Container" +#define FLM_4x_LANGUAGE_TAG_NAME "Language" +#define FLM_4x_OPTIONAL_TAG_NAME "Optional" +#define FLM_4x_UNIQUE_TAG_NAME "Unique" +#define FLM_4x_KEY_TAG_NAME "Key" +#define FLM_4x_REFS_TAG_NAME "Refs" +#define FLM_4x_AREA_TAG_NAME "Area" +#define FLM_4x_STATE_TAG_NAME "State" +#define FLM_4x_BLOB_TAG_NAME "Blob" +#define FLM_4x_THRESHOLD_TAG_NAME "Threshold" +#define FLM_4x_SUFFIX_TAG_NAME "Suffix" +#define FLM_4x_SUBDIRECTORY_TAG_NAME "Subdirectory" +#define FLM_4x_RESERVED_TAG_NAME "Reserved" +#define FLM_4x_SUBNAME_TAG_NAME "Subname" +#define FLM_4x_NAME_TAG_NAME "Name" +#define FLM_4x_BASE_TAG_NAME "Base" +#define FLM_4x_CASE_TAG_NAME "Case" +#define FLM_4x_COMBINATIONS_TAG_NAME "Combinations" +#define FLM_4x_COUNT_TAG_NAME "Count" +#define FLM_4x_POSITIONING_TAG_NAME "Positioning" +#define FLM_4x_PAIRED_TAG_NAME "Paired" +#define FLM_4x_PARENT_TAG_NAME "Parent" +#define FLM_4x_POST_TAG_NAME "Post" +#define FLM_4x_REQUIRED_TAG_NAME "Required" +#define FLM_4x_USE_TAG_NAME "Use" +#define FLM_4x_FILTER_TAG_NAME "Filter" +#define FLM_4x_LIMIT_TAG_NAME "Limit" +#define FLM_4x_DICT_TAG_NAME "Dict" +#define FLM_4x_RECINFO_TAG_NAME "RecInfo" +#define FLM_4x_DRN_TAG_NAME "Drn" +#define FLM_4x_DICT_SEQ_TAG_NAME "DictSeq" +#define FLM_4x_LAST_CONTAINER_INDEXED_TAG_NAME "LastContainerIndexed" +#define FLM_4x_LAST_DRN_INDEXED_TAG_NAME "LastDrnIndexed" +#define FLM_4x_ONLINE_TRANS_ID_TAG_NAME "OnlineTransId" + +#define FLM_4x_ASCII_CHAR_CODE 0x00 +#define FLM_4x_ASCII_CHAR_MASK 0x80 +#define FLM_4x_CHAR_SET_CODE 0x80 +#define FLM_4x_CHAR_SET_MASK 0xC0 +#define FLM_4x_WHITE_SPACE_CODE 0xC0 +#define FLM_4x_WHITE_SPACE_MASK 0xE0 +#define FLM_4x_UNK_GT_255_CODE 0xE0 +#define FLM_4x_UNK_GT_255_MASK 0xF8 +#define FLM_4x_UNK_EQ_1_CODE 0xF0 +#define FLM_4x_UNK_EQ_1_MASK 0xF8 +#define FLM_4x_UNK_LE_255_CODE 0xF8 +#define FLM_4x_UNK_LE_255_MASK 0xF8 +#define FLM_4x_EXT_CHAR_CODE 0xE8 +#define FLM_4x_OEM_CODE 0xE9 +#define FLM_4x_UNICODE_CODE 0xEA + +#define FLM_4x_FOP_IS_STANDARD(p) (!(*(p) & 0x80) ) +#define FLM_4x_FOP_OPEN 0x90 +#define FLM_4x_FOP_IS_OPEN(p) ((*(p) & 0xF0) == FLM_4x_FOP_OPEN) +#define FLM_4x_FOP_GET_FLD_FLAGS(p) ((*p) & 0x07) +#define FLM_4x_FOP_2BYTE_FLDNUM(bv) ((bv) & 0x02) +#define FLM_4x_FOP_2BYTE_FLDLEN(bv) ((bv) & 0x01) +#define FLM_4x_FOP_TAGGED 0x80 +#define FLM_4x_FOP_IS_TAGGED(p) ((*(p) & 0xF0) == FLM_4x_FOP_TAGGED) +#define FLM_4x_FOP_NO_VALUE 0xA8 +#define FLM_4x_FOP_IS_NO_VALUE(p) ((*(p) & 0xF8) == FLM_4x_FOP_NO_VALUE) +#define FLM_4x_FOP_SET_LEVEL 0xA0 +#define FLM_4x_FOP_IS_SET_LEVEL(p) ((*(p) & 0xF8) == FLM_4x_FOP_SET_LEVEL) +#define FLM_4x_FOP_LEVEL_MAX 0x07 +#define FLM_4x_FSLEV_GET(p) (*(p) & FLM_4x_FOP_LEVEL_MAX) +#define FLM_4x_FTAG_LEVEL(p) ((*p) & 0x08) +#define FLM_4x_FTAG_FLD_TYPE(p) ((*(p+1)) & 0x0F) +#define FLM_4x_FOPE_LEVEL(p) ((*p) & 0x08) +#define FLM_4x_FSTA_FLD_LEN(p) ((*p) & 0x3F) +#define FLM_4x_FSTA_LEVEL(p) ((*p) & 0x40) +#define FLM_4x_FSTA_FLD_NUM(p) (*(p+1)) +#define FLM_4x_FSTA_OVHD 2 +#define FLM_4x_FTAG_OVHD 2 +#define FLM_4x_FNOV_LEVEL(p) ((*p) & 0x04) + +#define FLM_4x_BBE_FIRST_FLAG 0x80 +#define FLM_4x_BBE_LAST_FLAG 0x40 +#define FLM_4x_BBE_IS_FIRST(elm) ((*(elm)) & FLM_4x_BBE_FIRST_FLAG ) +#define FLM_4x_BBE_IS_LAST(elm) ((*(elm)) & FLM_4x_BBE_LAST_FLAG ) +#define FLM_4x_BBE_KL_HBITS 0x30 +#define FLM_4x_BBE_PKC 0 +#define FLM_4x_BBE_PKC_MAX 0x0F +#define FLM_4x_BBE_GET_PKC(elm) ((*(elm)) & FLM_4x_BBE_PKC_MAX) +#define FLM_4x_BBE_KL 1 +#define FLM_4x_BBE_KL_SHIFT_BITS 4 +#define FLM_4x_BBE_GET_KL(elm) (((*(elm) & FLM_4x_BBE_KL_HBITS) << FLM_4x_BBE_KL_SHIFT_BITS) + \ + (elm)[ FLM_4x_BBE_KL]) +#define FLM_4x_BBE_GETR_PKC(elm) (*(elm) & 0x3F) +#define FLM_4x_BBE_GETR_KL(elm) ((elm)[FLM_4x_BBE_KL]) +#define FLM_4x_BBE_RL 2 +#define FLM_4x_BBE_GET_RL(elm) ((elm)[ FLM_4x_BBE_RL]) +#define FLM_4x_BBE_KEY 3 +#define FLM_4x_BBE_LEN(elm) (FLM_4x_BBE_GET_RL(elm) + FLM_4x_BBE_GET_KL(elm) + FLM_4x_BBE_KEY) +#define FLM_4x_BBE_LEM_LEN 3 +#define FLM_4x_BBE_REC_OFS(elm) (FLM_4x_BBE_GET_KL(elm) + FLM_4x_BBE_KEY) +#define FLM_4x_BBE_REC_PTR(elm) (&(elm)[ FLM_4x_BBE_REC_OFS(elm)]) + +#define FLM_4x_BNE_DATA_CHILD_BLOCK 4 +#define FLM_4x_BNE_DATA_OVHD 8 +#define FLM_4x_BNE_DOMAIN 0x80 +#define FLM_4x_BNE_IS_DOMAIN(elm) ((*(elm)) & FLM_4x_BNE_DOMAIN) +#define FLM_4x_BNE_DOMAIN_LEN 3 +#define FLM_4x_BNE_KEY_START 6 +#define FLM_4x_BNE_KEY_COUNTS_START 10 + +#define FLM_4x_DIN_KEY_SIZ 4 +#define FLM_4x_MAX_KEY_SIZ 640 +#define FLM_4x_IXD_UNIQUE 0x00001 + +#define FLM_4x_DRN_LAST_MARKER ((FLMUINT) 0xFFFFFFFF) + +#define FLM_4x_GET_BH_ADDR( pBlk) (FB2UD(&(pBlk)[ FLM_4x_BH_ADDR])) +#define FLM_4x_BLK_ELM( stack,elm) ((stack)->pBlk[ (elm)]) +#define FLM_4x_BLK_ELM_ADDR(stack,elm) (&((stack)->pBlk->m_pucBlk[ (elm)])) +#define FLM_4x_CURRENT_ELM(stack) (&((stack)->pBlk->m_pucBlk[ stack->uiCurElm])) +#define FLM_4x_FOP_IS_OPEN(p) ((*(p) & 0xF0) == FLM_4x_FOP_OPEN) + +#define FLM_4x_ASCII_CHAR_CODE 0x00 // 0nnnnnnn +#define FLM_4x_CHAR_SET_CODE 0x80 // 10nnnnnn +#define FLM_4x_WHITE_SPACE_CODE 0xC0 // 110nnnnn +#define FLM_4x_WHITE_SPACE_MASK 0xE0 // 11100000 +#define FLM_4x_UNK_EQ_1_CODE 0xF0 // 11110nnn +#define FLM_4x_EXT_CHAR_CODE 0xE8 // 11101000 +#define FLM_4x_OEM_CODE 0xE9 // 11101001 +#define FLM_4x_UNICODE_CODE 0xEA // 11101010 + +#define FLM_4x_FLM_FILE_HEADER_SIZE 172 +#define FLM_4x_FLAIM_HEADER_START (2048 - FLM_4x_FLM_FILE_HEADER_SIZE) +#define FLM_4x_DB_LOG_HEADER_START 16 +#define FLM_4x_FLAIM_NAME_POS 0 +#define FLM_4x_VER_POS 5 +#define FLM_4x_MINOR_VER_POS (FLM_4x_VER_POS + 2) +#define FLM_4x_SMINOR_VER_POS (FLM_4x_VER_POS + 3) +#define FLM_4x_DB_DEFAULT_LANGUAGE 13 +#define FLM_4x_DB_BLOCK_SIZE 14 +#define FLM_4x_DB_1ST_LFH_ADDR 32 + +#define FLM_4x_LOG_RFL_FILE_NUM 0 +#define FLM_4x_LOG_RFL_LAST_TRANS_OFFSET 4 +#define FLM_4x_LOG_RFL_LAST_CP_FILE_NUM 8 +#define FLM_4x_LOG_RFL_LAST_CP_OFFSET 12 +#define FLM_4x_LOG_ROLLBACK_EOF 16 +#define FLM_4x_LOG_INC_BACKUP_SEQ_NUM 20 +#define FLM_4x_LOG_CURR_TRANS_ID 24 +#define FLM_4x_LOG_COMMIT_COUNT 28 +#define FLM_4x_LOG_PL_FIRST_CP_BLOCK_ADDR 32 +#define FLM_4x_LOG_LAST_RFL_FILE_DELETED 36 +#define FLM_4x_LOG_RFL_MIN_FILE_SIZE 40 +#define FLM_4x_LOG_HDR_CHECKSUM 44 +#define FLM_4x_LOG_FLAIM_VERSION 46 +#define FLM_4x_LOG_LAST_BACKUP_TRANS_ID 48 +#define FLM_4x_LOG_BLK_CHG_SINCE_BACKUP 52 +#define FLM_4x_LOG_LAST_CP_TRANS_ID 56 +#define FLM_4x_LOG_PF_FIRST_BACKCHAIN 60 +#define FLM_4x_LOG_PF_AVAIL_BLKS 64 +#define FLM_4x_LOG_LOGICAL_EOF 68 +#define FLM_4x_LOG_LAST_RFL_COMMIT_ID 72 +#define FLM_4x_LOG_KEEP_ABORTED_TRANS_IN_RFL 76 +#define FLM_4x_LOG_PF_FIRST_BC_CNT 77 +#define FLM_4x_LOG_KEEP_RFL_FILES 78 +#define FLM_4x_LOG_AUTO_TURN_OFF_KEEP_RFL 79 +#define FLM_4x_LOG_PF_NUM_AVAIL_BLKS 80 +#define FLM_4x_LOG_RFL_MAX_FILE_SIZE 84 +#define FLM_4x_LOG_DB_SERIAL_NUM 88 +#define FLM_4x_LOG_LAST_TRANS_RFL_SERIAL_NUM 104 +#define FLM_4x_LOG_RFL_NEXT_SERIAL_NUM 120 +#define FLM_4x_LOG_INC_BACKUP_SERIAL_NUM 136 +#define FLM_4x_LOG_NU_152_153 152 +#define FLM_4x_LOG_MAX_FILE_SIZE 154 + +#define FLM_4x_LFH_LF_NUMBER_OFFSET 0 +#define FLM_4x_LFH_TYPE_OFFSET 2 +#define FLM_4x_LFH_STATUS_OFFSET 3 +#define FLM_4x_LFH_ROOT_BLK_OFFSET 4 +#define FLM_4x_LFH_NEXT_DRN_OFFSET 12 +#define FLM_4x_LFH_MAX_FILL_OFFSET 16 +#define FLM_4x_LFH_MIN_FILL_OFFSET 17 +#define FLM_4x_LFH_SIZE 32 + +#define FLM_4x_LFILE_DATA_CONTAINER_OFFSET 0 +#define FLM_4x_LFILE_DICT_CONTAINER_OFFSET 1 +#define FLM_4x_LFILE_DICT_INDEX_OFFSET 2 +#define FLM_4x_LFILE_TRACKER_CONTAINER_OFFSET 3 + +#define FLM_VER_4_0 400 +#define FLM_VER_4_3 430 +#define FLM_VER_4_31 431 +#define FLM_VER_4_50 450 +#define FLM_VER_4_51 451 + +/**************************************************************************** +Desc: +****************************************************************************/ +typedef struct +{ + F_Block * pBlk; + FLMBYTE ucKeyBuf[ FLM_4x_DIN_KEY_SIZ]; + FLMUINT uiKeyLen; + FLMUINT uiPKC; + FLMUINT uiPrevElmPKC; + FLMUINT uiBlkAddr; + FLMUINT uiCurElm; + FLMUINT uiBlkEnd; + FLMUINT uiElmOvhd; + FLMUINT uiBlkType; + FLMUINT uiLevel; +} BTSK; + +typedef struct +{ + FLMBYTE * pElement; + FLMUINT uiFieldType; + FLMUINT uiFieldLen; + FLMUINT uiPosInElm; + FLMUINT uiTagNum; + FLMUINT uiLevel; +} FSTATE; + +typedef struct Data_Piece +{ + FLMBYTE * pData; + FLMUINT uiLength; + struct Data_Piece * pNext; +} DATAPIECE; + +typedef struct +{ + FLMUINT uiLevel; + FLMUINT uiFieldID; + FLMUINT uiFieldType; + FLMUINT uiFieldLen; + DATAPIECE DataPiece; +} TFIELD; + +typedef struct Field_Group +{ +#define NUM_FIELDS_IN_ARRAY 16 + TFIELD pFields[ NUM_FIELDS_IN_ARRAY]; // Allocated array of fields + struct Field_Group * pNext; // Next temporary field group +} FLDGROUP; + +typedef struct Locked_Block +{ + F_Block * pBlock; + struct Locked_Block * pNext; +} LOCKED_BLOCK; + +typedef struct FlmField +{ + FLMUINT16 ui16FieldID; + FLMUINT8 ui8Level; + FLMUINT8 ui8Type; + FLMUINT uiDataLength; + FLMUINT uiDataOffset; + FlmField * pNext; + FlmField * pPrev; +} FIELD; + +typedef struct +{ + FLMUNICODE * puzTagName; + FLMUINT uiTagNum; + FLMUINT uiType; + FLMUINT uiSubType; +} FLM_4x_TAG_INFO; + +typedef struct +{ + FLMBYTE * pucPtr; + FLMUINT uiNibCnt; + FLMUINT uiNum; + FLMBOOL bNegFlag; + FLMBYTE ucNumBuf[ 12]; +} BCD_TYPE; + +typedef struct +{ + FLMUINT uiFirstLFHBlkAddr; // Address of first LFH block. + FLMUINT uiVersionNum; // Database version + FLMUINT uiBlockSize; // Block size + FLMUINT uiDefaultLanguage; // Default language + FLMUINT uiAppMajorVer; // Application major version number + FLMUINT uiAppMinorVer; // Application minor version number + FLMUINT uiSigBitsInBlkSize; // Number of significant bits in block + // size. 1K = 10, 2K = 11, 4K = 12 ... +} F_4x_FILE_HDR; + +typedef struct +{ + FLMUINT uiCurrTransID; // Current transaction ID. + FLMUINT uiFirstAvailBlkAddr; // Address of first block in avail list + FLMUINT uiAvailBlkCount; // Avail block count + FLMUINT uiLogicalEOF; // Current logical end of file. New + // blocks are allocated at this address. +} F_4x_LOG_HDR; + +typedef FLMINT (* FLM_4x_TAG_COMPARE_FUNC)( + FLM_4x_TAG_INFO * pTagInfo1, + FLM_4x_TAG_INFO * pTagInfo2); + +/**************************************************************************** +Struct: LFILE (Logical File) +Desc: This keeps track of the logical file information for an index or + a container. +****************************************************************************/ +typedef struct +{ + FLMUINT uiRootBlk; // Address of root block. + FLMUINT uiNextDrn; // Next DRN - only use when root is null + FLMUINT uiBlkAddress; // Block address of LFile entry. + FLMUINT uiOffsetInBlk; // Offset within block of entry. + FLMUINT uiLfNum; // Index number or container number. + FLMUINT uiLfType; // Type of logical file +#define FLM_4x_LF_CONTAINER 1 +#define FLM_4x_LF_INDEX 3 +#define FLM_4x_LF_INVALID 15 +} F_4x_LFILE; + +/************************************************************************** +Desc: +**************************************************************************/ +class F_Record : public XF_RefCount, public XF_Base +{ +public: + + F_Record(); + ~F_Record(); + + FINLINE FLMUINT getID( void) + { + return( m_uiRecordID); + } + + FINLINE void setID( + FLMUINT uiRecordID) + { + m_uiRecordID = uiRecordID; + } + + FINLINE FLMUINT getContainerID( void) + { + return( m_uiContainerID); + } + + FINLINE void setContainerID( + FLMUINT uiContainerID) + { + m_uiContainerID = uiContainerID; + } + + F_Record * copy( void); + + void clear(); + + RCODE getINT( + void * pvField, + FLMINT * piNumber); + + RCODE getUINT( + void * pvField, + FLMUINT * puiNumber); + + RCODE getUINT32( + void * pvField, + FLMUINT32 * pui32Number); + + RCODE getUnicode( + void * pvField, + FLMUNICODE * pUnicode, + FLMUINT * puiBufLen); + + RCODE getNative( + void * pvField, + char * pszString, + FLMUINT * puiBufLen); + + RCODE getBinary( + void * pvField, + void * pvBuf, + FLMUINT * puiBufLen); + + FINLINE FLMUINT getLevel( + void * pvField) + { + return( ((FIELD *)pvField)->ui8Level); + } + + FINLINE FLMUINT getFieldID( + void * pvField) + { + return( ((FIELD *)pvField)->ui16FieldID); + } + + FINLINE FLMUINT getDataType( + void * pvField) + { + return( ((FIELD *)pvField)->ui8Type & 0x0F); + } + + FINLINE FLMUINT getDataLength( + void * pvField) + { + return( ((FIELD *)pvField)->uiDataLength); + } + + FINLINE FLMBYTE * getExportDataPtr( + void * pvField) + { + return( getDataPtr( (FIELD *)pvField)); + } + + FINLINE FLMBOOL hasChild( + void * pvField) + { + return( (firstChildField( (FIELD *)pvField) != NULL) ? TRUE : FALSE); + } + + FINLINE FLMBOOL isLast( + void * pvField) + { + return( (nextField( (FIELD *)pvField) == NULL) ? TRUE : FALSE); + } + + FINLINE void * root( void) + { + return( m_pFirstFld); + } + + FINLINE void * nextSibling( + void * pvField) + { + return( pvField ? nextSiblingField( (FIELD *)pvField) : NULL); + } + + FINLINE void * firstChild( + void * pvField) + { + return( pvField ? firstChildField( (FIELD *)pvField) : NULL); + } + + FINLINE void * next( + void * pvField) + { + return( pvField ? nextField( (FIELD * )pvField) : NULL); + } + + FINLINE void * prev( + void * pvField) + { + return( pvField ? prevField( (FIELD *)pvField) : NULL); + } + + void * prevSibling( + void * pvField); + + void * lastChild( + void * pvField); + + void * parent( + void * pvField); + + void * find( + void * pvStartField, + FLMUINT uiFieldID, + FLMUINT uiOccur = 1, + FLMBOOL bSearchForest = FALSE); + + void * find( + void * pvStartField, + FLMUINT * puiFieldPath, + FLMUINT uiOccur = 1, + FLMBOOL bSearchForest = FALSE); + + RCODE preallocSpace( + FLMUINT uiDataSize); + + FLMBYTE * getImportDataPtr( + void * pvField, + FLMUINT uiDataType, + FLMUINT uiLength); + + RCODE insertLast( + FLMUINT uiLevel, + FLMUINT uiFieldID, + FLMUINT uiDataType, + void ** ppvField); + +private: + + void resetFieldList( void); + + FINLINE FIELD * getFirstField( void) + { + return( m_pFirstFld); + } + + FINLINE FIELD * prevField( + FIELD * pField) + { + return( (FIELD *)(pField ? pField->pPrev : NULL)); + } + + FINLINE FIELD * nextField( + FIELD * pField) + { + return( (FIELD *)(pField ? pField->pNext : NULL)); + } + + FIELD * nextSiblingField( + FIELD * pField); + + FINLINE FIELD * F_Record::firstChildField( + FIELD * pField) + { + FLMUINT8 ui8Level = pField->ui8Level; + + return( ((pField = nextField( pField)) != NULL && + pField->ui8Level > ui8Level) + ? pField + : NULL); + } + + FIELD * lastSubTreeField( + FIELD * pField); + + RCODE createField( + FIELD * pCurField, + FIELD ** ppNewField); + + FLMBYTE * getDataPtr( + FIELD * pField); + + RCODE getNewDataPtr( + FIELD * pField, + FLMUINT uiDataType, + FLMUINT uiNewLength, + FLMBYTE ** ppDataPtr); + + RCODE storage2INT( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMINT * piNum); + + RCODE storage2UINT( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMUINT * puiNum); + + RCODE storage2UINT32( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + FLMUINT32 * pui32Num); + + RCODE bcd2Num( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuf, + BCD_TYPE * bcd); + + RCODE getUnicode( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiStrBufLen, + FLMUNICODE * puzStrBuf); + + RCODE storage2Native( + FLMUINT uiType, + FLMUINT uiBufLength, + FLMBYTE * pBuffer, + FLMUINT * puiOutBufLenRV, + char * pszOutBuffer); + + RCODE numToText( + FLMBYTE * pucNum, + FLMBYTE * pucOutBuffer, + FLMUINT * puiBufLen); + + RCODE contextToText( + FLMBYTE * pucValue, + FLMBYTE * pucOutBuffer, + FLMUINT * puiBufLen); + + FLMUINT textObjType( + FLMBYTE ucObj) + { + if( (ucObj & FLM_4x_ASCII_CHAR_MASK) == + FLM_4x_ASCII_CHAR_CODE) + { + return( FLM_4x_ASCII_CHAR_CODE); + } + else if( (ucObj & FLM_4x_WHITE_SPACE_MASK) == + FLM_4x_WHITE_SPACE_CODE) + { + return( FLM_4x_WHITE_SPACE_CODE); + } + else if( (ucObj & FLM_4x_UNK_EQ_1_MASK) == + FLM_4x_UNK_EQ_1_CODE) + { + return( FLM_4x_UNK_EQ_1_CODE); + } + else if( (ucObj & FLM_4x_CHAR_SET_MASK) == + FLM_4x_CHAR_SET_CODE) + { + return( FLM_4x_CHAR_SET_CODE); + } + + return( ucObj); + } + + FLMUINT m_uiContainerID; + FLMUINT m_uiRecordID; + F_Pool m_pool; +#define FLM_4x_FIELD_LIST_SIZE 100 + FIELD m_fieldList[ FLM_4x_FIELD_LIST_SIZE]; + FIELD * m_pFirstFld; + FIELD * m_pLastFld; + FIELD * m_pAvailFld; + FLMBYTE * m_pDataBuf; + FLMUINT m_uiDataBufOffset; + FLMUINT m_uiDataBufLength; + +friend class F_4xReader; +}; + +/************************************************************************** +Desc: +**************************************************************************/ +class F_4xReader : public XF_RefCount, public XF_Base +{ +public: + + F_4xReader(); + + ~F_4xReader(); + + RCODE openDatabase( + char * pszPath); + + void closeDatabase( void); + + RCODE retrieveRec( + FLMUINT uiContainer, + FLMUINT uiDrn, + FLMUINT uiFlags, + F_Record ** ppRecord); + + RCODE retrieveNextRec( + F_Record ** ppRecord); + + RCODE getNextDrn( + FLMUINT uiContainer, + FLMUINT * puiDrn); + + RCODE getNameTable( + F_4xNameTable ** ppNameTable); + + FINLINE void setDefaultContainer( + FLMUINT uiContainer) + { + m_uiDefaultContainer = uiContainer; + } + +private: + + FINLINE void initStack( + BTSK * pStack) + { + FLMUINT uiNumLevels = FLM_4x_BH_MAX_LEVELS; + + while( uiNumLevels--) + { + pStack->pBlk = NULL; + pStack->uiBlkAddr = 0; + pStack++; + } + } + + void releaseStack( + BTSK * pStack); + + RCODE readRecElements( + BTSK * pStack, + F_4x_LFILE * pLFile, + F_Record ** ppRecord); + + RCODE getFldOverhead( + FSTATE * pState); + + RCODE btSearch( + F_4x_LFILE * pLFile, + FLMUINT uiDrn, + BTSK ** ppStack); + + RCODE blkNextElm( + BTSK * pStack); + + RCODE btNextElm( + BTSK * pStack, + F_4x_LFILE * pLFile); + + RCODE btPrevElm( + BTSK * pStack, + F_4x_LFILE * pLFile); + + RCODE btScan( + BTSK * pStack, + FLMBYTE * pucSearchKey); + + RCODE btScanNonLeafData( + BTSK * pStack, + FLMUINT uiDrn); + + RCODE btSearchEnd( + F_4x_LFILE * pLFile, + FLMUINT uiDrn, + BTSK ** ppStack); + + RCODE btAdjustStack( + BTSK * pStack, + F_4x_LFILE * pLFile, + FLMBOOL bMovedNext); + + FLMUINT childBlkAddr( + BTSK * pStack); + + FLMUINT lgHdrCheckSum( + FLMBYTE * pucLogHdr, + FLMBOOL bCompare); + + RCODE createLckFile( + char * pszFilePath); + + RCODE readBlock( + FLMUINT uiBlkAddr, + F_Block ** ppBlock); + + FINLINE FLMUINT getEncryptSize( + FLMBYTE * pucBlk) + { + FLMUINT uiLen = (FLMUINT)FB2UW( &pucBlk[ FLM_4x_BH_ELM_END]); + + if (uiLen % sizeof( FLMUINT32) != 0) + { + uiLen += (FLMUINT)(sizeof( FLMUINT32) - + (uiLen % sizeof( FLMUINT32))); + } + return( uiLen); + } + + RCODE blkCheckSum( + FLMBYTE * pucBlkPtr, + FLMUINT uiBlkAddress, + FLMUINT uiBlkSize); + + RCODE readLFiles( void); + + RCODE getLFile( + FLMUINT uiLFile, + F_4x_LFILE ** ppLFile); + + RCODE getRootBlock( + F_4x_LFILE * pLFile, + BTSK * pStack); + + RCODE getBlock( + F_4x_LFILE * pLFile, + FLMUINT uiBlkAddr, + BTSK * pStack); + + void blkToStack( + F_Block ** ppBlock, + BTSK * pStack); + + RCODE getFieldType( + FLMUINT uiFieldNum, + FLMUINT * puiType); + + RCODE readDictionary( void); + + RCODE getTypeTag( + F_Record * pRec, + void * pvField, + FLMUINT * puiType); + + FINLINE F_Block ** getHashBucket( + FLMUINT uiBlkAddress) + { + return( &m_ppBlockTbl[ + (((uiBlkAddress) >> m_fileHdr.uiSigBitsInBlkSize) & + (m_uiBlockTblSize - 1))]); + } + + // Data + + F_Pool m_tmpPool; + F_SuperFileHdl * m_pSuperHdl; + F_4x_FILE_HDR m_fileHdr; + F_4x_LOG_HDR m_logHdr; + F_4x_LFILE * m_pLFileTbl; + FLMUINT m_uiLFileCnt; + FLMUINT * m_puiFieldTbl; + FLMUINT m_uiFieldTblSize; + FLMUINT m_uiDefaultContainer; + F_FileHdl * m_pLckFile; + FLMUINT m_uiMaxFileSize; + F_Block ** m_ppBlockTbl; + FLMUINT m_uiBlockTblSize; + F_4xNameTable * m_pNameTable; + +friend class F_4xNameTable; +}; + +/************************************************************************** +Desc: +**************************************************************************/ +class F_Block : public XF_RefCount, public XF_Base +{ +public: + + F_Block() + { + m_pucBlk = NULL; + m_uiBlockSize = 0; + } + + ~F_Block() + { + if( m_pucBlk) + { + f_free( &m_pucBlk); + } + } + + FINLINE RCODE allocBlockBuf( + FLMUINT uiBlockSize) + { + RCODE rc = NE_XFLM_OK; + + flmAssert( getRefCount() == 1); + + if( !m_uiBlockSize) + { + if( RC_BAD( rc = f_alloc( uiBlockSize, &m_pucBlk))) + { + goto Exit; + } + } + else if( m_uiBlockSize != uiBlockSize) + { + if( RC_BAD( rc = f_realloc( uiBlockSize, &m_pucBlk))) + { + goto Exit; + } + } + + m_uiBlockSize = uiBlockSize; + + Exit: + + return( rc); + } + +private: + + FLMUINT m_uiBlockSize; + FLMBYTE * m_pucBlk; + +friend class F_4xReader; +}; + +/************************************************************************** +Desc: +**************************************************************************/ +class F_4xNameTable : public XF_RefCount, public XF_Base +{ +public: + + F_4xNameTable(); + + ~F_4xNameTable(); + + void clearTable( void); + + FLMBOOL getNextTagNumOrder( + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiType = NULL, + FLMUINT * puiSubType = NULL); + + FLMBOOL getNextTagNameOrder( + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiType = NULL, + FLMUINT * puiSubType = NULL); + + FLMBOOL getFromTagType( + FLMUINT uiType, + FLMUINT * puiNextPos, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiTagNum = NULL, + FLMUINT * puiSubType = NULL); + + FLMBOOL getFromTagNum( + FLMUINT uiTagNum, + FLMUNICODE * puzTagName, + char * pszTagName, + FLMUINT uiNameBufSize, + FLMUINT * puiType = NULL, + FLMUINT * puiSubType = NULL); + + FLMBOOL getFromTagName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT * puiTagNum, + FLMUINT * puiType = NULL, + FLMUINT * puiSubType = NULL); + + FLMBOOL getFromTagTypeAndName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiType, + FLMUINT * puiTagNum, + FLMUINT * puiSubType = NULL); + + RCODE addTag( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiType, + FLMUINT uiSubType, + FLMBOOL bCheckDuplicates = TRUE); + + RCODE setupNameTable( + F_4xReader * pDb); + + void sortTags( void); + +private: + + RCODE allocTag( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiTagNum, + FLMUINT uiType, + FLMUINT uiSubType, + FLM_4x_TAG_INFO ** ppTagInfo); + + RCODE reallocSortTables( + FLMUINT uiNewTblSize); + + void copyTagName( + FLMUNICODE * puzDestTagName, + char * pszDestTagName, + FLMUINT uiDestBufSize, + FLMUNICODE * puzSrcTagName); + + FLM_4x_TAG_INFO * findTagByName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT * puiInsertPos = NULL); + + FLM_4x_TAG_INFO * findTagByNum( + FLMUINT uiTagNum, + FLMUINT * puiInsertPos = NULL); + + FLM_4x_TAG_INFO * findTagByTypeAndName( + const FLMUNICODE * puzTagName, + const char * pszTagName, + FLMUINT uiType, + FLMUINT * puiInsertPos = NULL); + + RCODE insertTagInTables( + FLM_4x_TAG_INFO * pTagInfo, + FLMUINT uiTagNameTblInsertPos, + FLMUINT uiTagTypeAndNameTblInsertPos, + FLMUINT uiTagNumTblInsertPos); + + FINLINE void tagInfoSwap( + FLM_4x_TAG_INFO ** ppTagInfoTbl, + FLMUINT uiPos1, + FLMUINT uiPos2) + { + FLM_4x_TAG_INFO * pTmpTagInfo = ppTagInfoTbl [uiPos1]; + + ppTagInfoTbl [uiPos1] = ppTagInfoTbl [uiPos2]; + ppTagInfoTbl [uiPos2] = pTmpTagInfo; + } + + static FLMINT tagNameCompare( + const FLMUNICODE * puzName1, + const char * pszName1, + const FLMUNICODE * puzName2); + + static FINLINE FLMINT compareTagNumOnly( + FLM_4x_TAG_INFO * pTagInfo1, + FLM_4x_TAG_INFO * pTagInfo2) + { + if (pTagInfo1->uiTagNum < pTagInfo2->uiTagNum) + { + return( -1); + } + else if (pTagInfo1->uiTagNum > pTagInfo2->uiTagNum) + { + return( 1); + } + else + { + return( 0); + } + } + + static FINLINE FLMINT compareTagNameOnly( + FLM_4x_TAG_INFO * pTagInfo1, + FLM_4x_TAG_INFO * pTagInfo2) + { + return (tagNameCompare( pTagInfo1->puzTagName, NULL, + pTagInfo2->puzTagName)); + } + + static FINLINE FLMINT compareTagTypeAndName( + FLM_4x_TAG_INFO * pTagInfo1, + FLM_4x_TAG_INFO * pTagInfo2) + { + if (pTagInfo1->uiType < pTagInfo2->uiType) + { + return( -1); + } + else if (pTagInfo1->uiType > pTagInfo2->uiType) + { + return( 1); + } + else + { + return (tagNameCompare( pTagInfo1->puzTagName, NULL, + pTagInfo2->puzTagName)); + } + } + + void sortTagTbl( + FLM_4x_TAG_INFO ** ppTagInfoTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + FLM_4x_TAG_COMPARE_FUNC fnTagCompare); + + F_Pool m_pool; + FLM_4x_TAG_INFO ** m_ppSortedByTagName; + FLM_4x_TAG_INFO ** m_ppSortedByTagNum; + FLM_4x_TAG_INFO ** m_ppSortedByTagTypeAndName; + FLMUINT m_uiTblSize; + FLMUINT m_uiNumTags; + FLMBOOL m_bTablesSorted; +}; + +#endif diff --git a/version5/src/frestore.cpp b/version5/src/frestore.cpp new file mode 100644 index 0000000..5faf99e --- /dev/null +++ b/version5/src/frestore.cpp @@ -0,0 +1,311 @@ +//------------------------------------------------------------------------------ +// Desc: Methods used during restore +// +// 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: frestore.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_FSRestore::F_FSRestore() +{ + m_pFileHdl = NULL; + m_pFileHdl64 = NULL; + m_ui64Offset = 0; + m_bSetupCalled = FALSE; + m_szDbPath[ 0] = 0; + m_uiDbVersion = 0; + m_szBackupSetPath[ 0] = 0; + m_szRflDir[ 0] = 0; + m_bOpen = FALSE; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_FSRestore::~F_FSRestore() +{ + if( m_bOpen) + { + (void)close(); + } +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::setup( + const char * pszDbPath, + const char * pszBackupSetPath, + const char * pszRflDir) +{ + flmAssert( !m_bSetupCalled); + flmAssert( pszDbPath); + flmAssert( pszBackupSetPath); + + f_strcpy( m_szDbPath, pszDbPath); + f_strcpy( m_szBackupSetPath, pszBackupSetPath); + + if( pszRflDir) + { + f_strcpy( m_szRflDir, pszRflDir); + } + + + m_bSetupCalled = TRUE; + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::openBackupSet( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( !m_pFileHdl64); + + if( (m_pFileHdl64 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl64->Open( m_szBackupSetPath))) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + goto Exit; + } + + m_ui64Offset = 0; + m_bOpen = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::openRflFile( + FLMUINT uiFileNum) +{ + RCODE rc = NE_XFLM_OK; + char szRflPath[ F_PATH_MAX_SIZE]; + char szBaseName[ F_FILENAME_SIZE]; + FLMUINT uiBaseNameSize; + XFLM_DB_HDR dbHdr; + IF_FileHdl * pFileHdl = NULL; + + flmAssert( m_bSetupCalled); + flmAssert( uiFileNum); + flmAssert( !m_pFileHdl); + + // Read the database header to determine the version number + + if( !m_uiDbVersion) + { + + if( RC_BAD( rc = gv_pFileSystem->Open( m_szDbPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadAndVerifyHdrInfo( NULL, pFileHdl, &dbHdr))) + { + goto Exit; + } + + pFileHdl->Close(); + pFileHdl->Release(); + pFileHdl = NULL; + + m_uiDbVersion = (FLMUINT)dbHdr.ui32DbVersion; + } + + // Generate the log file name. + + if( RC_BAD( rc = rflGetDirAndPrefix( m_szDbPath, m_szRflDir, szRflPath))) + { + goto Exit; + } + + uiBaseNameSize = sizeof( szBaseName); + rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL); + gv_pFileSystem->pathAppend( szRflPath, szBaseName); + + // Open the file. + + if( RC_BAD( rc = gv_pFileSystem->OpenBlockFile( szRflPath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, 512, &m_pFileHdl))) + { + goto Exit; + } + + m_bOpen = TRUE; + m_ui64Offset = 0; + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::openIncFile( + FLMUINT uiFileNum) +{ + char szIncPath[ F_PATH_MAX_SIZE]; + char szIncFile[ F_FILENAME_SIZE]; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( !m_pFileHdl64); + + /* + Since this is a non-interactive restore, we will "guess" + that incremental backups are located in the same parent + directory as the main backup set. We will further assume + that the incremental backup sets have been named XXXXXXXX.INC, + where X is a hex digit. + */ + + if( RC_BAD( rc = gv_pFileSystem->pathReduce( m_szBackupSetPath, + szIncPath, NULL))) + { + goto Exit; + } + + f_sprintf( szIncFile, "%08X.INC", (unsigned)uiFileNum); + gv_pFileSystem->pathAppend( szIncPath, szIncFile); + + if( (m_pFileHdl64 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl64->Open( szIncPath))) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + goto Exit; + } + + m_ui64Offset = 0; + m_bOpen = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::read( + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + FLMUINT uiBytesRead = 0; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( m_pFileHdl || m_pFileHdl64); + + if( m_pFileHdl64) + { + if( RC_BAD( rc = m_pFileHdl64->Read( m_ui64Offset, + uiLength, pvBuffer, &uiBytesRead))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pFileHdl->Read( (FLMUINT)m_ui64Offset, + uiLength, pvBuffer, &uiBytesRead))) + { + goto Exit; + } + } + +Exit: + + m_ui64Offset += uiBytesRead; + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::close( void) +{ + flmAssert( m_bSetupCalled); + + if( m_pFileHdl64) + { + m_pFileHdl64->Release(); + m_pFileHdl64 = NULL; + } + + if( m_pFileHdl) + { + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + m_bOpen = FALSE; + m_ui64Offset = 0; + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_FSRestore::abortFile( void) +{ + return( close()); +} + + diff --git a/version5/src/frset.cpp b/version5/src/frset.cpp new file mode 100644 index 0000000..96aab67 --- /dev/null +++ b/version5/src/frset.cpp @@ -0,0 +1,1960 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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: +*****************************************************************************/ +FResultSet::FResultSet() +{ + 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_pFileHdl641 = NULL; + m_pFileHdl642 = NULL; + m_bOutput2ndFile = FALSE; + m_bInitialAdding = TRUE; + m_bFinalizeCalled = FALSE; + m_bSetupCalled = FALSE; + m_uiBlkSize = RSBLK_BLOCK_SIZE; +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +FResultSet::FResultSet( + 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_pFileHdl641 = NULL; + m_pFileHdl642 = NULL; + m_bOutput2ndFile = FALSE; + m_bInitialAdding = TRUE; + m_bFinalizeCalled = FALSE; + m_bSetupCalled = FALSE; + m_uiBlkSize = uiBlkSize; +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +FResultSet::~FResultSet() +{ + FResultSetBlk *pCurRSBlk; + FResultSetBlk *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_pFileHdl641 ); + CloseFile( &m_pFileHdl642 ); + + if( m_pCompare) + { + m_pCompare->Release(); + } + + if( m_pSortStatus) + { + m_pSortStatus->Release(); + } +} + +/***************************************************************************** +Desc: Reset the result set so it can be reused. +*****************************************************************************/ +RCODE XFLMAPI FResultSet::resetResultSet( + FLMBOOL bDelete) +{ + RCODE rc = NE_XFLM_OK; + FResultSetBlk * pCurRSBlk; + FResultSetBlk * 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_pFileHdl641, bDelete ); + CloseFile( &m_pFileHdl642 ); + m_bFile1Opened = m_bFile2Opened = FALSE; + m_pFileHdl641 = m_pFileHdl642 = 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 FResultSetBlk) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + else + { + m_pFirstRSBlk->reset(); + } + + m_pLastRSBlk = m_pCurRSBlk = m_pFirstRSBlk; + (void)m_pFirstRSBlk->Setup( &m_pFileHdl641, 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 XFLMAPI FResultSet::setupResultSet( + const char * pszDirPath, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bDropDuplicates, + FLMBOOL bEntriesInOrder, + const char * pszInputFileName) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bNewBlock = FALSE; + FLMBOOL bNewBuffer = FALSE; + + flmAssert( !m_bSetupCalled ); + flmAssert( uiEntrySize <= MAX_FIXED_ENTRY_SIZE); + + // Perform all of the allocations first. + + m_pFirstRSBlk = m_pLastRSBlk = m_pCurRSBlk = f_new FResultSetBlk; + + // Allocation Error? + + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_XFLM_MEM ); + goto Exit; + } + + bNewBlock = TRUE; + m_pCurRSBlk->Setup( &m_pFileHdl641, 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 XFLMAPI FResultSet::setupFromFile( void) +{ + RCODE rc = NE_XFLM_OK; + FResultSetBlk * pNextRSBlk; + FLMUINT uiOffset; + FLMUINT uiBytesRead; + F_BLOCK_HEADER BlkHdr; + + flmAssert( !m_bSetupCalled); + + if( (m_pFileHdl641 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileHdl641->Open( m_szIoFilePath1))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + if( RC_BAD( rc = m_pFileHdl641->Create( m_szIoFilePath1))) + { + rc = NE_XFLM_OK; + m_pFileHdl641->Release(); + m_pFileHdl641 = NULL; + goto Exit; + } + } + else + { + rc = NE_XFLM_OK; + m_pFileHdl641->Release(); + m_pFileHdl641 = 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 FResultSetBlk chain. + + f_memset( (void *)&BlkHdr, 0, sizeof( F_BLOCK_HEADER)); + + for( uiOffset = 0;;) + { + // Read the block header + + if( RC_BAD( rc = m_pFileHdl641->Read( + BlkHdr.ui64FilePos + BlkHdr.uiBlockSize + uiOffset, + sizeof( F_BLOCK_HEADER), &BlkHdr, &uiBytesRead))) + { + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_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 FResultSetBlk) == NULL) + { + rc = RC_SET( NE_XFLM_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_pFileHdl641, 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 FResultSetBlk) == NULL) + { + rc = RC_SET( NE_XFLM_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_pFileHdl641, 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 XFLMAPI FResultSet::flushToFile() +{ + RCODE rc = NE_XFLM_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 XFLMAPI FResultSet::addEntry( + const void * pvEntry, + FLMUINT uiEntryLength) // If zero then entry is fixed length +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( !m_bFinalizeCalled); + + rc = m_pCurRSBlk->AddEntry( (FLMBYTE *)pvEntry, uiEntryLength); + + // See if current block is full + + if( rc == NE_XFLM_EOF_HIT) + { + FResultSetBlk * pNextRSBlk; + F_64BitFileHandle ** ppFileHdl64; + + 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_pFileHdl641))) + { + goto Exit; + } + } + + ppFileHdl64 = (m_bOutput2ndFile) ? &m_pFileHdl642 : &m_pFileHdl641; + + // 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 FResultSetBlk) == NULL) + { + rc = RC_SET( NE_XFLM_MEM ); + goto Exit; + } + + m_pCurRSBlk->m_pNext = pNextRSBlk; + pNextRSBlk->m_pPrev = m_pCurRSBlk; + m_pLastRSBlk = m_pCurRSBlk = pNextRSBlk; + m_pCurRSBlk->Setup( ppFileHdl64, 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_XFLM_EOF_HIT) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + } + + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Done adding entries. Sort all of the entries and perform a merge. +*****************************************************************************/ +RCODE XFLMAPI FResultSet::finalizeResultSet( + FLMUINT64 * pui64TotalEntries) +{ + RCODE rc = NE_XFLM_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) + { + FResultSetBlk * 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_pFileHdl641->Close( TRUE); + m_bFile1Opened = FALSE; + } + + if( m_pFileHdl641) + { + m_pFileHdl641->Release(); + m_pFileHdl641 = NULL; + } + } + + if( !m_bOutput2ndFile || RC_BAD( rc)) + { + if( m_bFile2Opened) + { + m_pFileHdl642->Close( TRUE); + m_bFile2Opened = FALSE; + } + + if( m_pFileHdl642) + { + m_pFileHdl642->Release(); + m_pFileHdl642 = NULL; + } + } + } + + if( RC_OK(rc)) + { + FLMUINT64 ui64Pos; + FResultSetBlk * 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 FResultSet::MergeSort( void) +{ + RCODE rc = NE_XFLM_OK; + FResultSetBlk * pBlkList = NULL; + FResultSetBlk * pTempBlk; + FResultSetBlk * pLeftBlk; + FResultSetBlk * pRightBlk; + F_64BitFileHandle ** ppFileHdl64; + + // 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_pFileHdl642) + : OpenFile( &m_pFileHdl641); + + if( RC_BAD( rc)) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + ppFileHdl64 = ( m_bOutput2ndFile ) ? &m_pFileHdl642 : &m_pFileHdl641; + + // 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 FResultSetBlk) == NULL) + { + rc = RC_SET( NE_XFLM_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( ppFileHdl64, 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 XFLMAPI FResultSet::getCurrent( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bFinalizeCalled); + + if( !m_pCurRSBlk) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI FResultSet::getNext( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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 XFLMAPI FResultSet::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_XFLM_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_XFLM_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 XFLMAPI FResultSet::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_XFLM_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 XFLMAPI FResultSet::getLast( + void * pvBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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 XFLMAPI FResultSet::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_XFLM_OK; + FLMINT iBlkCompare; // 0 if key is/would be in block. + FResultSetBlk * pLowBlk; // Used for locating block. + FResultSetBlk * 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_XFLM_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_XFLM_OK if the entry if found in the block. + // It returns NE_XFLM_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_XFLM_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_XFLM_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. +*****************************************************************************/ +FResultSetBlk * FResultSet::SelectMidpoint( + FResultSetBlk * pLowBlk, + FResultSetBlk * pHighBlk, + FLMBOOL bPickHighIfNeighbors) +{ + FLMUINT uiCount; + FResultSetBlk * 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 = (FResultSetBlk *)(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 XFLMAPI FResultSet::setPosition( + FLMUINT64 ui64Position) +{ + RCODE rc = NE_XFLM_OK; + FResultSetBlk * 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_XFLM_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 FResultSet::GetNextPtr( + FResultSetBlk ** ppCurBlk, + FLMBYTE ** ppucBuffer, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; + FResultSetBlk * pCurBlk = *ppCurBlk; + FResultSetBlk * pNextBlk; + FLMBYTE * pucBuffer; + + flmAssert( pCurBlk); + + while( RC_BAD( rc = pCurBlk->GetNextPtr( ppucBuffer, puiReturnLength))) + { + if( rc == NE_XFLM_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 FResultSet::UnionBlkLists( + FResultSetBlk * pLeftBlk, + FResultSetBlk * pRightBlk) +{ + RCODE rc = NE_XFLM_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_XFLM_EOF_HIT) + { + rc = CopyRemainingItems( pRightBlk); + } + + goto Exit; + } + + if( RC_BAD( rc = GetNextPtr( &pRightBlk, &pucRightEntry, &uiRightLength))) + { + if( rc == NE_XFLM_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_XFLM_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_XFLM_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 FResultSet::CopyRemainingItems( + FResultSetBlk * 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_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Closes and deletes one of two files. +*****************************************************************************/ +void FResultSet::CloseFile( + F_64BitFileHandle ** ppFileHdl64, + FLMBOOL bDelete) +{ + if( ppFileHdl64 == &m_pFileHdl641) + { + if( m_bFile1Opened) + { + m_pFileHdl641->Close( bDelete); + m_bFile1Opened = FALSE; + } + + if( m_pFileHdl641) + { + m_pFileHdl641->Release(); + m_pFileHdl641 = NULL; + } + } + else + { + if( m_bFile2Opened) + { + m_pFileHdl642->Close( TRUE); + m_bFile2Opened = FALSE; + } + + if( m_pFileHdl642) + { + m_pFileHdl642->Release(); + m_pFileHdl642 = NULL; + } + } +} + +/***************************************************************************** +Desc: Close the file if previously opened and creates the file. +*****************************************************************************/ +RCODE FResultSet::OpenFile( + F_64BitFileHandle ** ppFileHdl64) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL * pbFileOpened; + char * pszDirPath; + + // Will close and delete if opened, else will do nothing. + + CloseFile( ppFileHdl64); + + if( ppFileHdl64 == &m_pFileHdl641) + { + pbFileOpened = &m_bFile1Opened; + pszDirPath = &m_szIoFilePath1 [0]; + } + else + { + pbFileOpened = &m_bFile2Opened; + pszDirPath = &m_szIoFilePath2 [0]; + } + + f_strcpy( pszDirPath, m_szIoDefaultPath); + + if( (*ppFileHdl64 = f_new F_64BitFileHandle) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = (*ppFileHdl64)->CreateUnique( pszDirPath, + FRSET_FILENAME_EXTENSION))) + { + (*ppFileHdl64)->Release(); + *ppFileHdl64 = NULL; + goto Exit; + } + + *pbFileOpened = TRUE; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Create and empty data vector and return it's interface... +*****************************************************************************/ +RCODE XFLMAPI F_DbSystem::createIFResultSet( + IF_ResultSet ** ppResultSet) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppResultSet = f_new FResultSet) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version5/src/frset.h b/version5/src/frset.h new file mode 100644 index 0000000..5bb88a1 --- /dev/null +++ b/version5/src/frset.h @@ -0,0 +1,567 @@ +//------------------------------------------------------------------------------ +// Desc: Result sets +// +// Tabs: 3 +// +// Copyright (c) 1996-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.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FRSET_H +#define FRSET_H + +// Forward declarations + +class FResultSet; +class FResultSetBlk; + +/***************************************************************************** +***** +** Definitions +***** +*****************************************************************************/ + +#define RSBLK_BLOCK_SIZE (1024 * 512) + +// Need to allocate on the stack the maximum fixed entry size +// in the recursive quicksort routine. + +#define MAX_FIXED_ENTRY_SIZE 64 + +#define RS_POSITION_NOT_SET FLM_MAX_UINT64 + +/***************************************************************************** +***** +** Result Set Block Definitions +***** +*****************************************************************************/ + +// Block Size Limits: +// 1) 4 GB (whatever we can represent in 32 bits) +// 2) Three blocks are allocated as a maximum during the merge phase. +// Two of these blocks are freed after the merge phase. +// +// Block Layout: +// Block Header Structure +// Variable Length Entries +// An array of [Offset][Length] entry items (F_VAR_HEADER) +// The variable length entries are stored from the end back to front. +// The length of a variable length entry must be less than the block size. +// Fixed Length Entries +// Each entry is stored from the first of the buffer to the last. + +typedef struct FVarHeaderTag +{ + FLMUINT32 ui32Offset; + FLMUINT32 ui32Length; +} F_VAR_HEADER; + +/*=========================================================================== + + Block Header Definition + + Desc: Actually stored as the first section of each block. + We can write this structure because the same process will + read the block header i.e. portability is not a problem. +===========================================================================*/ + +typedef struct FBlockHeaderTag +{ + FLMUINT64 ui64FilePos; // ~0 or file position + FLMUINT uiEntryCount; // Number of entries in block + FLMUINT uiBlockSize; // Total Block size in memory or on disk + FLMBOOL bFirstBlock; // TRUE=first block in chain + FLMBOOL bLastBlock; // TRUE=last block in chain +} F_BLOCK_HEADER; + +#define RSBLK_UNSET_FILE_POS (~((FLMUINT64)0)) + +/*=========================================================================== + + Result Set Block Class Definition + Move to another file if FResultSet is more public. + Source: FRSETBLK.CPP + +Note: Anything member variable in comments is for the future. +===========================================================================*/ +class FResultSetBlk : public XF_RefCount, public XF_Base +{ +public: + + // Constructor + + FResultSetBlk(); + + // Destructor + + FINLINE ~FResultSetBlk() + { + if (m_pNext) + { + m_pNext->m_pPrev = m_pPrev; + } + + if( m_pPrev) + { + m_pPrev->m_pNext = m_pNext; + } + + if (m_pCompare) + { + m_pCompare->Release(); + } + } + + // Setup and termination methods + + void reset( void); + + void Setup( + F_64BitFileHandle ** ppFileHdl64, // file handle to use for temp file. + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, // Entry size if fixed. + FLMBOOL bFirstInList, // Use RSBLK_IS_FIRST_IN_LIST or + // RSBLK_NOT_FIRST_IN_LIST + FLMBOOL bDropDuplicates, // If TRUE drop duplicates + FLMBOOL bEntriesInOrder); // TRUE when entries are in order. + + RCODE SetBuffer( // Set the current working buffer + FLMBYTE * pBuffer, + FLMUINT uiBufferSize = RSBLK_BLOCK_SIZE); + + FINLINE FLMUINT BytesUsedInBuffer( void) + { + if (m_bEntriesInOrder) + { + return( m_BlockHeader.uiBlockSize); + } + else + { + return( m_BlockHeader.uiBlockSize - m_uiLengthRemaining); + } + } + + // Entry Add and Sort Methods + + RCODE AddEntry( // Variable or fixed length entry coming in + FLMBYTE * pEntry, // Entry cannot be all zero values. + FLMUINT uiEntryLength ); // If length is zero then ignore entry. + + RCODE ModifyEntry( // Modify current entry. + FLMBYTE * pEntry, // Points to entry buffer + FLMUINT uiEntryLength = 0); // Zero value means fixed length. + // Existing entry must be the same length + // and sort in the same place. + + + FINLINE RCODE Finalize( + FLMBOOL bForceWrite) + { + return Flush( TRUE, bForceWrite); + } + + RCODE Flush( // Sort and flush block to disk if more data + FLMBOOL bLastBlockInList, + FLMBOOL bForceWrite); + + // Methods to read entries. + + RCODE GetCurrent( // Return current entry + 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_XFLM_EOF_HIT); + } + + m_iEntryPos++; // Else position to next entry + + return CopyCurrentEntry( pucBuffer, uiBufferLength, puiReturnLength); + } + + RCODE GetNextPtr( // Get a pointer to the next entry. + // Internal call not to be exposed. + FLMBYTE * * ppBuffer, + FLMUINT * puiReturnLength); + + RCODE GetPrev( // Position to previous entry and return + 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( // Set the input position. Returns + // SUCCESS or NE_XFLM_NOT_FOUND if position bad. + FLMUINT64 ui64Position ); // (in) Position or RS_POSITION_NOT_SET + + RCODE FindMatch( // Find and return an etnry that + // matches in this block. + FLMBYTE * pMatchEntry, // Entry to match + FLMUINT uiMatchEntryLength, // Variable length of above entry + FLMBYTE * pFoundEntry, // (out) Entry to return + FLMUINT * puiFoundEntryLength, // (out) Length of entry returned + FLMINT * piCompare); // See comments above. + + void adjustState( + FLMUINT uiBlkBufferSize); + + RCODE Truncate( + FLMBYTE * pszPath); + +private: + + RCODE AddEntry( // Fixed length entry coming in. + FLMBYTE * pucEntry); // Entry cannot be all zero values. + + void SqueezeSpace( void); // Squeeze out space if variable length. + + RCODE SortAndRemoveDups( void); // Sort entries in a block and remove dups. + + void RemoveEntry( // Remove an entry. + FLMBYTE * pucEntry); + + RCODE QuickSort( // The great quick sort algorithm. + 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( // Copy current entry to pBuffer. + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength); + + RCODE CompareEntry( // Compares match entry with entry + // identified by uiEntryPos. + FLMBYTE * pMatchEntry, // Entry to match + FLMUINT uiMatchEntryLength,// Variable length of pMatchEntry. + FLMUINT uiEntryPos, // Position of entry in block. + FLMINT * piCompare); // Return from compare. + + // I/O Methods + + RCODE Write(); // Write a block to disk + RCODE Read(); // Read a block of data from disk + + // Member Variables + + F_BLOCK_HEADER m_BlockHeader; // Block Header - written to disk. + + IF_ResultSetCompare * m_pCompare; + + // Variables to track data in the block. + + FLMBYTE * m_pucBlockBuf; // Block buffer. Allocated by owner + FLMBYTE * m_pucEndPoint; // Entry data runs from rear to front + + FResultSetBlk * m_pNext; // Next RSBLK in a chain. + FResultSetBlk * m_pPrev; // Previous RSBlk in a chain. + + F_64BitFileHandle ** + m_ppFileHdl64; // Points to a 64-bit file handle + + FLMUINT64 m_ui64BlkEntryPosition;// Position of first entry in block. + FLMUINT m_uiLengthRemaining; // Bytes left between entry pointers + // and pEndPoint (start of entry data.) + + // Flags and counters + + FLMINT m_iEntryPos; // Entry position when reading. + FLMUINT m_uiEntrySize; // Entry size used in the block (non-0). + + FLMBOOL m_bEntriesInOrder; // TRUE = entries are in order. + FLMBOOL m_bFixedEntrySize; // TRUE = process fixed length entries. + FLMBOOL m_bPositioned; // TRUE = we are positioned through + // ResultSetFirst() or ResultSetLast(). + FLMBOOL m_bModifiedEntry; // 1+ entries in blk modifed or deleted. + FLMBOOL m_bDuplicateFound; // TRUE = duplicate found when sorting. + FLMBOOL m_bDropDuplicates; // TRUE drop duplicate entries. +friend class FResultSet; +}; + +/***************************************************************************** +Desc: Result set class +*****************************************************************************/ +class FResultSet : public IF_ResultSet, public XF_Base +{ +public: + + FResultSet(); + FResultSet( + FLMUINT uiBlkSize); + + virtual ~FResultSet(); // No errors returned when closing files + // or freeing memory. + + RCODE XFLMAPI setupResultSet( + const char * pszPath, // Default I/O path to use for the result + // set files. There may be 2 files when + // a lot of data is to be merged. + IF_ResultSetCompare * pCompare, // Compare callback object + FLMUINT uiEntrySize, // Size of entry if fixed length or zero + // if variable length. Assert will be + // called if entry size changed when setup + // is called multiple times. + FLMBOOL bDropDuplicates = TRUE, + FLMBOOL bEntriesInOrder = FALSE, + // TRUE if entries are in order. + const char * pszInputFileName = NULL); + // Use this file if needed. If specified + // AND it exists, the file will be opened and read. + // If you don't want to use an existing file, you + // should call resetResultSet immediately after this call. + // That will delete the existing file. + + FINLINE void XFLMAPI setSortStatus( + IF_ResultSetSortStatus * pSortStatus) + { + if (m_pSortStatus) + { + m_pSortStatus->Release(); + m_pSortStatus = NULL; + } + if ((m_pSortStatus = pSortStatus) != NULL) + { + m_pSortStatus->AddRef(); + } + } + + FINLINE FLMUINT64 XFLMAPI getTotalEntries( void) + { + FResultSetBlk * pBlk = m_pFirstRSBlk; + FLMUINT64 ui64TotalEntries = 0; + + for( pBlk = m_pFirstRSBlk; pBlk; pBlk = pBlk->m_pNext) + { + ui64TotalEntries += pBlk->m_BlockHeader.uiEntryCount; + } + return ui64TotalEntries; + } + + // Methods for building a result set. + + RCODE XFLMAPI addEntry( // Add fixed or variable length entry. + const void * pvEntry, // Points to entry buffer + FLMUINT uiEntryLength = 0); // zero value means fixed length + + RCODE XFLMAPI finalizeResultSet( // Commit all entries added. + FLMUINT64 * pui64TotalEntries = NULL);// (out) returns total entries. + + // Reading Entries from a Result Set + + RCODE XFLMAPI getFirst( // Returns first entry + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE XFLMAPI getNext( // Returns next entry + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE XFLMAPI getLast( // Returns last entry + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE XFLMAPI getPrev( // Returns previous entry + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL); + + RCODE XFLMAPI getCurrent( // Returns current entry + void * pvEntryBuffer, // Buffer to hold entry + FLMUINT uiBufferLength = 0, // Length of buffer to hold entry + FLMUINT * puiEntryLength = NULL); // (out) Length of returned entry + + FINLINE RCODE XFLMAPI modifyCurrent( + const void * pvEntry, + FLMUINT uiEntryLength = 0) // If zero entry is fixed length. + { + return( m_pCurRSBlk->ModifyEntry( (FLMBYTE *)pvEntry, uiEntryLength)); + } + + FINLINE RCODE XFLMAPI findMatch( + const void * pvMatchEntry, // Fixed length entry to match. + void * pvFoundEntry) // (out) Buffer to return full entry to. + { + return( findMatch( pvMatchEntry, m_uiEntrySize, + pvFoundEntry, NULL)); + } + + RCODE XFLMAPI findMatch( // Find and return an etnry that + // matches in the result set (variable). + 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 + + FINLINE FLMUINT64 XFLMAPI getPosition( void) + { + return( (!m_pCurRSBlk + ? RS_POSITION_NOT_SET + : m_pCurRSBlk->GetPosition())); + } + + RCODE XFLMAPI setPosition( + FLMUINT64 ui64Position); + + RCODE XFLMAPI resetResultSet( + FLMBOOL bDelete = TRUE); + + RCODE XFLMAPI flushToFile( void); + +private: + + // Private Methods + + FINLINE FLMUINT64 NumberOfBlockChains( void) + { + FLMUINT64 ui64Count = 0; + FResultSetBlk * pBlk = m_pFirstRSBlk; + + for (; pBlk ; pBlk = pBlk->m_pNext) + { + if (pBlk->m_BlockHeader.bFirstBlock) + { + ui64Count++; + } + } + return ui64Count; + } + + RCODE MergeSort(); // Sort/Merge all blks into single list. + + RCODE GetNextPtr( // Return ptr to next entry. + FResultSetBlk **ppCurBlk, + FLMBYTE * * ppBuffer, + FLMUINT * puiReturnLength); + + RCODE UnionBlkLists( + FResultSetBlk *pLeftBlk, // (IN) Left block + FResultSetBlk *pRightBlk = NULL);// (IN) May be NULL + + RCODE CopyRemainingItems( // Copy remaining items from a block list. + FResultSetBlk *pCurBlk); // (IN) Inputblock + + void CloseFile( // Close the input file. + F_64BitFileHandle ** ppFileHdl64, + FLMBOOL bDelete = TRUE); + + RCODE OpenFile( // Open the input file + F_64BitFileHandle ** ppFileHdl64); + + FResultSetBlk *SelectMidpoint( // Select the midpoint between blocks. + FResultSetBlk *pLowBlk, + FResultSetBlk *pHighBlk, + FLMBOOL bPickHighIfNeighbors); + + RCODE XFLMAPI setupFromFile( void); + + + // Callback objects + + IF_ResultSetCompare * m_pCompare; + + IF_ResultSetSortStatus * m_pSortStatus; + FLMUINT64 m_ui64EstTotalUnits; // Estimated total number of units to do. + FLMUINT64 m_ui64UnitsDone; // Units completed + + FLMUINT m_uiEntrySize; // Fixed length entry size or 0 for var. + + FLMUINT64 m_ui64TotalEntries; // Total number of entries. + + FResultSetBlk *m_pCurRSBlk, // Current result set block. + *m_pFirstRSBlk, // Points to first of merge list + *m_pLastRSBlk; // Points to last of merge list + + char m_szIoDefaultPath[ F_PATH_MAX_SIZE],// Copy of default path from setup(). + m_szIoFilePath1[ F_PATH_MAX_SIZE], // File created for result set. + m_szIoFilePath2[ F_PATH_MAX_SIZE]; // File create for result set merge. + + F_64BitFileHandle * + m_pFileHdl641; // File handles. + F_64BitFileHandle * + m_pFileHdl642; + + FLMBYTE * m_pucBlockBuf1; // Buffer for initial loading of entries + FLMBYTE * m_pucBlockBuf2; // Buffer for merge step. + FLMBYTE * m_pucBlockBuf3; // Buffer for merge step. + FLMUINT m_uiBlockBuf1Len; + + FLMBOOL m_bFile1Opened; // TRUE when m_IoFile1 is opened. + FLMBOOL m_bFile2Opened; // TRUE when m_IoFile2 is opened. + FLMBOOL m_bOutput2ndFile; // TRUE if output is 2nd file + FLMBOOL m_bInitialAdding; // TRUE when user adding entries. + FLMBOOL m_bFinalizeCalled; // TRUE after finalize step. + FLMBOOL m_bSetupCalled; // TRUE after setup has been called. + FLMBOOL m_bDropDuplicates; // TRUE drop duplicate entries. + FLMBOOL m_bAppAddsInOrder; // TRUE entries are added in order. + FLMBOOL m_bEntriesInOrder; // TRUE entries are in order + FLMUINT m_uiBlkSize; // Default is RSBLK_BLOCK_SIZE + +friend class FResultSetBlk; +}; + +#endif // ifndef FRSET_H diff --git a/version5/src/frsetblk.cpp b/version5/src/frsetblk.cpp new file mode 100644 index 0000000..1bae58b --- /dev/null +++ b/version5/src/frsetblk.cpp @@ -0,0 +1,1403 @@ +//------------------------------------------------------------------------------ +// Desc: Result set block routines +// +// Tabs: 3 +// +// Copyright (c) 1996-2000, 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: frsetblk.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "frset.h" + +/***************************************************************************** +Desc: +******************************************************************************/ +FResultSetBlk::FResultSetBlk() +{ + m_pNext = m_pPrev = NULL; + m_pCompare = NULL; + reset(); +} + +/***************************************************************************** +Desc: Reset a block so it can be reused. +******************************************************************************/ +void FResultSetBlk::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_ppFileHdl64 = NULL; + m_ui64BlkEntryPosition = RS_POSITION_NOT_SET; + m_iEntryPos = 0; + m_bDuplicateFound = FALSE; + m_bPositioned = FALSE; + m_bModifiedEntry = FALSE; + m_pucBlockBuf = NULL; +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void FResultSetBlk::Setup( + F_64BitFileHandle ** ppFileHdl64, // file handle to use for temp file. + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bFirstInList, + FLMBOOL bDropDuplicates, // If TRUE drop duplicates + FLMBOOL bEntriesInOrder) // TRUE when entries are in order. +{ + flmAssert( ppFileHdl64); + m_ppFileHdl64 = ppFileHdl64; + + 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 FResultSetBlk::SetBuffer( + FLMBYTE * pucBuffer, // Working buffer or NULL + FLMUINT uiBufferLength) // Default value is RSBLK_BLOCK_SIZE. +{ + RCODE rc = NE_XFLM_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 FResultSetBlk::AddEntry( + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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 FResultSetBlk::AddEntry( + FLMBYTE * pucEntry) +{ + RCODE rc = NE_XFLM_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_XFLM_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 FResultSetBlk::ModifyEntry( + FLMBYTE * pucEntry, + FLMUINT uiEntryLength) +{ + RCODE rc = NE_XFLM_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 FResultSetBlk::Flush( + FLMBOOL bLastBlockInList, // Last block in a block list. + FLMBOOL bForceWrite) // if TRUE write out to disk. +{ + RCODE rc = NE_XFLM_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 FResultSetBlk::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 FResultSetBlk::SortAndRemoveDups( void) +{ + RCODE rc = NE_XFLM_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 FResultSetBlk::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 FResultSetBlk::QuickSort( + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds) +{ + RCODE rc = NE_XFLM_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[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 FResultSetBlk::Write() +{ + RCODE rc = NE_XFLM_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_ppFileHdl64)->Size( &m_BlockHeader.ui64FilePos))) + { + goto Exit; + } + } + + // Write out the block header definition. + + if( RC_BAD( rc = (*m_ppFileHdl64)->Write( + m_BlockHeader.ui64FilePos, + sizeof( F_BLOCK_HEADER), &m_BlockHeader, + &uiBytesWritten))) + { + goto Exit; + } + + // Write out the data buffer + + if( RC_BAD( rc = (*m_ppFileHdl64)->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 FResultSetBlk::Read() +{ + RCODE rc = NE_XFLM_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_ppFileHdl64)->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_XFLM_FAILURE); + goto Exit; + } + + // Read in the data buffer + + if (RC_BAD( rc = (*m_ppFileHdl64)->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 FResultSetBlk::CopyCurrentEntry( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_OK; // Must be initailized + 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_XFLM_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 FResultSetBlk::GetCurrent( + FLMBYTE * pBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc; + + flmAssert( m_pucBlockBuf); + if( !m_bPositioned ) + { + rc = RC_SET( NE_XFLM_NOT_FOUND ); + goto Exit; + } + + // Check for EOF and BOF conditions - otherwise return current. + + if (m_iEntryPos >= (FLMINT) m_BlockHeader.uiEntryCount) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + if( m_iEntryPos == -1) + { + rc = RC_SET( NE_XFLM_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 FResultSetBlk::GetNextPtr( + FLMBYTE ** ppucBuffer, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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 FResultSetBlk::GetPrev( + FLMBYTE * pucBuffer, + FLMUINT uiBufferLength, + FLMUINT * puiReturnLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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 FResultSetBlk::SetPosition( + FLMUINT64 ui64Position) +{ + RCODE rc= NE_XFLM_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_XFLM_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 FResultSetBlk::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_XFLM_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_XFLM_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_XFLM_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_XFLM_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 FResultSetBlk::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 FResultSetBlk::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 FResultSetBlk::Truncate( + FLMBYTE * pszPath) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = (*m_ppFileHdl64)->Truncate( + m_BlockHeader.ui64FilePos))) + { + goto Exit; + } + + (*m_ppFileHdl64)->Close( FALSE); + + if( RC_BAD( rc = (*m_ppFileHdl64)->Open( ( char *)pszPath))) + { + goto Exit; + } + + m_BlockHeader.ui64FilePos = RSBLK_UNSET_FILE_POS; + +Exit: + + return( rc); +} diff --git a/version5/src/fsblk_u.cpp b/version5/src/fsblk_u.cpp new file mode 100644 index 0000000..2f6728e --- /dev/null +++ b/version5/src/fsblk_u.cpp @@ -0,0 +1,208 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for handling blocks in the avail list - putting +// blocks into the avail list and removing them from the avail list. +// +// 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: fsblk_u.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/*************************************************************************** +Desc: Take current avail block off of free list. Have db header point + to next block in the chain. +*****************************************************************************/ +RCODE F_Database::blockUseNextAvail( + F_Db * pDb, + F_CachedBlock ** ppSCache // Returns pointer to cache structure. + ) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = NULL; + F_CachedBlock * pNextSCache = NULL; + XFLM_DB_HDR * pDbHdr; + + pDbHdr = &m_uncommittedDbHdr; + + if (RC_BAD( rc = getBlock( pDb, NULL, pDb->m_uiFirstAvailBlkAddr, + NULL, &pSCache))) + { + goto Exit; + } + + // A corruption we have seen a couple of times is where a free + // block points to itself in the free list. This will hang the machine + // so this check has been added to verify that the block is a free block. + + if (pSCache->m_pBlkHdr->ui8BlkType != BT_FREE || + isEncryptedBlk( pSCache->m_pBlkHdr)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Log the block because we are changing it! + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + + *ppSCache = pSCache; + + pDb->m_uiFirstAvailBlkAddr = (FLMUINT)pSCache->m_pBlkHdr->ui32NextBlkInChain; + pDbHdr->ui32FirstAvailBlkAddr = (FLMUINT32)pDb->m_uiFirstAvailBlkAddr; + pSCache->m_pBlkHdr->ui32NextBlkInChain = 0; + + // Set the next block's previous to zero. + + if (pDb->m_uiFirstAvailBlkAddr) + { + if (RC_BAD( rc = getBlock( pDb, NULL, pDb->m_uiFirstAvailBlkAddr, + NULL, &pNextSCache))) + { + goto Exit; + } + + if (pNextSCache->m_pBlkHdr->ui8BlkType != BT_FREE) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Log the block because we are changing it + + if (RC_BAD( rc = logPhysBlk( pDb, &pNextSCache))) + { + goto Exit; + } + pNextSCache->m_pBlkHdr->ui32PrevBlkInChain = 0; + ScaReleaseCache( pNextSCache, FALSE); + pNextSCache = NULL; + } + +Exit: + + if (RC_BAD( rc)) + { + if (pSCache) + { + ScaReleaseCache( pSCache, FALSE); + } + if (pNextSCache) + { + ScaReleaseCache( pNextSCache, FALSE); + } + } + + return( rc); +} + +/*************************************************************************** +Desc: Put a block into the avail list. +Notes: This routine assumes that the block pointed to by pSCache has + been locked into memory. Regardless of whether or not the block + is actually freed on disk, its cache will be released. The + cached block should NOT be accessed after a call to FSBlockFree. +*****************************************************************************/ +RCODE F_Database::blockFree( + F_Db * pDb, + F_CachedBlock * pSCache // Pointer to pointer of cache block + // that is to be freed. NOTE: Regardless of whether + // or not the block is actually freed, it will be + // released. + ) +{ + RCODE rc = NE_XFLM_OK; + F_BLK_HDR * pBlkHdr; + F_CachedBlock * pFirstAvailSCache; + XFLM_DB_HDR * pDbHdr; + + pDbHdr = &m_uncommittedDbHdr; + + // Log the block before modifying it. + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + pBlkHdr = pSCache->m_pBlkHdr; + + // Modify header to be an avail block. + + if (isEncryptedBlk( pBlkHdr)) + { + // If block was previously encrypted, need to zero it + // out so that clear data will not be written to disk + // when this avail block is written out. + + unsetBlockEncrypted( pBlkHdr); + f_memset( ((FLMBYTE *)pBlkHdr) + SIZEOF_STD_BLK_HDR, 0, + m_uiBlockSize - SIZEOF_STD_BLK_HDR); + } + pBlkHdr->ui8BlkType = BT_FREE; + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - + SIZEOF_STD_BLK_HDR); + + // Modify back chain of first block in avail list, if any, + // to point back to this block. + + if (pDbHdr->ui32FirstAvailBlkAddr) + { + + // Read the block at head of avail list. + + if (RC_BAD( rc = getBlock( pDb, NULL, + (FLMUINT)pDbHdr->ui32FirstAvailBlkAddr, + NULL, &pFirstAvailSCache))) + { + goto Exit; + } + + // Log the block + + if (RC_OK( rc = logPhysBlk( pDb, &pFirstAvailSCache))) + { + + // Previous block pointer better be zero at this point. + + flmAssert( pFirstAvailSCache->m_pBlkHdr->ui32PrevBlkInChain == 0); + pFirstAvailSCache->m_pBlkHdr->ui32PrevBlkInChain = pBlkHdr->ui32BlkAddr; + } + ScaReleaseCache( pFirstAvailSCache, FALSE); + if (RC_BAD( rc)) + { + goto Exit; + } + } + + // Link block at head of avail list. + + pBlkHdr->ui32PrevBlkInChain = 0; + pBlkHdr->ui32NextBlkInChain = pDbHdr->ui32FirstAvailBlkAddr; + pDbHdr->ui32FirstAvailBlkAddr = pBlkHdr->ui32BlkAddr; + pDb->m_uiFirstAvailBlkAddr = (FLMUINT)pBlkHdr->ui32BlkAddr; + +Exit: + + ScaReleaseCache( pSCache, FALSE); + return( rc); +} diff --git a/version5/src/fscursor.cpp b/version5/src/fscursor.cpp new file mode 100644 index 0000000..73cf268 --- /dev/null +++ b/version5/src/fscursor.cpp @@ -0,0 +1,1301 @@ +//------------------------------------------------------------------------------ +// Desc: Cursor routines to get the complexity of the file system out +// of the search code. +// +// 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: fscursor.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fscursor.h" + +FSTATIC int nodeIdCompFn( + void * pvData1, + void * pvData2, + void * pvUserData); + +/**************************************************************************** +Desc: +****************************************************************************/ +FSIndexCursor::FSIndexCursor() +{ + m_pbTree = NULL; + m_bTreeOpen = FALSE; + m_pucCurKeyDataBuf = NULL; + m_uiCurKeyDataBufSize = 0; + m_uiCurKeyDataLen = 0; + m_pIxd = NULL; + m_pLFile = NULL; + m_pDb = NULL; + m_eTransType = XFLM_NO_TRANS; + m_pNodeIdSet = NULL; + resetCursor(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSIndexCursor::~FSIndexCursor() +{ + closeBTree(); + if (m_pucCurKeyDataBuf) + { + f_free( &m_pucCurKeyDataBuf); + } + if (m_pbTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree); + } + if (m_pNodeIdSet) + { + m_pNodeIdSet->Release(); + m_pNodeIdSet = NULL; + } +} + +/**************************************************************************** +Desc: Resets any allocations, keys, state, etc. +****************************************************************************/ +void FSIndexCursor::resetCursor( void) +{ + closeBTree(); + + m_uiIndexNum = 0; + m_uiBlkChangeCnt = 0; + m_ui64CurrTransId = 0; + m_curKey.uiKeyLen = 0; + m_uiCurKeyDataLen = 0; + m_bAtBOF = TRUE; + m_bAtEOF = FALSE; + m_bSetup = FALSE; + if (m_pNodeIdSet) + { + m_pNodeIdSet->Release(); + m_pNodeIdSet = NULL; + } + m_bElimDups = FALSE; + m_bMovingForward = TRUE; +} + +/**************************************************************************** +Desc: Comparison function for comparing node ids. +****************************************************************************/ +FSTATIC int nodeIdCompFn( + void * pvData1, + void * pvData2, + void * // pvUserData + ) +{ + if (*((FLMUINT64 *)pvData1) < *((FLMUINT64 *)pvData2)) + { + return( -1); + } + else if (*((FLMUINT64 *)pvData1) > *((FLMUINT64 *)pvData2)) + { + return( 1); + } + else + { + return( 0); + } +} + +/**************************************************************************** +Desc: Allocate a result set for duplicate checking. +****************************************************************************/ +RCODE FSIndexCursor::allocDupCheckSet( void) +{ + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + char szTmpDir [F_PATH_MAX_SIZE]; + + // If it is not a compound index, no need for a result set. + + if (!m_bElimDups) + { + goto Exit; + } + if (m_pNodeIdSet) + { + m_pNodeIdSet->Release(); + m_pNodeIdSet = NULL; + } + if ((m_pNodeIdSet = f_new FDynSearchSet) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = dbSystem.getTempDir( szTmpDir))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if (!szTmpDir [0] && m_pDb) + { + if (RC_BAD( rc = gv_pFileSystem->pathReduce( + m_pDb->m_pDatabase->m_pszDbPath, szTmpDir, NULL))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pNodeIdSet->setup( szTmpDir, sizeof( FLMUINT64)))) + { + goto Exit; + } + + m_pNodeIdSet->setCompareFunc( nodeIdCompFn, NULL); + +Exit: + + if (RC_BAD( rc)) + { + if (m_pNodeIdSet) + { + m_pNodeIdSet->Release(); + m_pNodeIdSet = NULL; + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Check to see if we have already returned the node. +****************************************************************************/ +RCODE FSIndexCursor::checkIfDup( + FLMUINT64 ui64NodeId, + FLMBOOL * pbDup + ) +{ + RCODE rc = NE_XFLM_OK; + + *pbDup = FALSE; + + // Should only be called if m_bElimDups is TRUE + + flmAssert( m_bElimDups); + + // If we have not yet allocated the result set, do it now. + + if (!m_pNodeIdSet) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + // See if we can add the node id to the result set + + if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) + { + if (rc == NE_XFLM_EXISTS) + { + *pbDup = TRUE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Resets to a new transaction that may change the read consistency of + the query. +****************************************************************************/ +RCODE FSIndexCursor::resetTransaction( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + LFILE * pLFile; + IXD * pIxd; + + if (RC_BAD( rc = pDb->m_pDict->getIndex( m_uiIndexNum, + &pLFile, &pIxd))) + { + goto Exit; + } + if (m_pDb != pDb || pLFile != m_pLFile || pIxd != m_pIxd) + { + m_pLFile = pLFile; + m_pIxd = pIxd; + if (m_bTreeOpen) + { + closeBTree(); + } + m_pDb = pDb; + m_eTransType = pDb->m_eTransType; + } + m_ixCompare.setIxInfo( pDb, m_pIxd); + m_ui64CurrTransId = pDb->m_ui64CurrTransID; + m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Setup the from and until keys in the cursor. Return counts + after positioning to the from and until key in the index. +****************************************************************************/ +RCODE FSIndexCursor::setupKeys( + F_Db * pDb, + IXD * pIxd, + PATH_PRED * pPred, + FLMBOOL * pbDoNodeMatch, + FLMBOOL * pbCanCompareOnKey, + FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks + FLMUINT * puiTotalRefs, // [out] total references + FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pUntilBTree = NULL; + FLMINT iCompare; + + m_uiIndexNum = pIxd->uiIndexNum; + + // Need to eliminate dups if we are working with + // a compound index or an index that has one or more + // data components along with the key. This is because + // the query code only looks at the 0th key component + // of keys that are returned. So when we have compound + // keys or keys with data components, we could get back + // multiple keys from the index with the same 0th + // component (but different 1st, 2nd, etc. components). + + m_bElimDups = (pIxd->uiNumKeyComponents > 1 || + pIxd->uiNumDataComponents) + ? TRUE + : FALSE; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + + if (RC_BAD( rc = flmBuildFromAndUntilKeys( pIxd, pPred, + &m_fromExtKey, m_fromKey.ucKey, &m_fromKey.uiKeyLen, + &m_untilExtKey, m_untilKey.ucKey, &m_untilKey.uiKeyLen, + pbDoNodeMatch, pbCanCompareOnKey))) + { + goto Exit; + } + + m_curKey.uiKeyLen = 0; + m_bSetup = TRUE; + + // Want any of the counts back? + + if (puiLeafBlocksBetween || puiTotalRefs) + { + + // Get a btree object + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pUntilBTree))) + { + goto Exit; + } + + if (RC_OK( rc = setKeyPosition( pDb, TRUE, FALSE, + &m_fromExtKey, &m_fromKey, &m_curKey, TRUE, NULL, NULL, NULL))) + { + + // All keys between FROM and UNTIL may be gone. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_untilExtKey, + NULL, NULL, + FALSE, FALSE, + m_curKey.ucKey, m_curKey.uiKeyLen, + m_untilKey.ucKey, m_untilKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + if (iCompare <= 0) + { + KEYPOS tmpUntilKey; + + if (RC_OK( rc = pUntilBTree->btOpen( pDb, m_pLFile, + isAbsolutePositionable(), FALSE, + &m_ixCompare))) + { + + // Going forward so position is exclusive + + rc = setKeyPosition( pDb, FALSE, FALSE, + &m_untilExtKey, &m_untilKey, + &tmpUntilKey, FALSE, NULL, pUntilBTree, NULL); + } + } + else + { + rc = RC_SET( NE_XFLM_BOF_HIT); + m_bAtBOF = TRUE; + m_bAtEOF = FALSE; + } + } + else + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + m_bAtBOF = FALSE; + } + else if (rc == NE_XFLM_BOF_HIT) + { + m_bAtEOF = FALSE; + m_bAtBOF = TRUE; + } + } + + if (RC_BAD( rc)) + { + + // Empty tree or empty set case. + + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BOF_HIT) + { + if (puiLeafBlocksBetween) + { + *puiLeafBlocksBetween = 0; + } + if (puiTotalRefs) + { + *puiTotalRefs = 0; + } + if (pbTotalsEstimated) + { + *pbTotalsEstimated = FALSE; + } + rc = NE_XFLM_OK; + } + goto Exit; + } + else + { + if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, + puiLeafBlocksBetween, puiTotalRefs, + pbTotalsEstimated, + (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) + { + goto Exit; + } + } + } + m_bAtBOF = TRUE; + +Exit: + + if (pUntilBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: Open the F_Btree object if not already open. +****************************************************************************/ +RCODE FSIndexCursor::openBTree( + F_Db * pDb + ) +{ + RCODE rc = NE_XFLM_OK; + + if (!m_bTreeOpen) + { + if ( !m_pbTree) + { + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &m_pbTree))) + { + goto Exit; + } + } +Open_Btree: + if (RC_BAD( rc = m_pbTree->btOpen( pDb, m_pLFile, + isAbsolutePositionable(), FALSE, + &m_ixCompare))) + { + goto Exit; + } + m_bTreeOpen = TRUE; + m_pDb = pDb; + m_eTransType = pDb->m_eTransType; + m_ixCompare.setIxInfo( m_pDb, m_pIxd); + } + else + { + if (pDb != m_pDb || pDb->m_eTransType != m_eTransType) + { + closeBTree(); + goto Open_Btree; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a key's data part. +****************************************************************************/ +RCODE FSIndexCursor::getKeyData( + F_Btree * pBTree, + FLMUINT uiDataLen) +{ + RCODE rc = NE_XFLM_OK; + + m_uiCurKeyDataLen = 0; + + // See if there is a data part + + if (m_pIxd->pFirstData && uiDataLen) + { + + // If the data will fit in the search key buffer, just + // reuse it since we are not going to do anything with + // it after this. Otherwise, allocate a new buffer. + + if (uiDataLen > m_uiCurKeyDataBufSize) + { + FLMBYTE * pucNewBuf; + FLMUINT uiNewLen = uiDataLen; + + if (uiNewLen < 256) + { + uiNewLen = 256; + } + + if (RC_BAD( rc = f_alloc( uiNewLen, &pucNewBuf))) + { + goto Exit; + } + if (m_pucCurKeyDataBuf) + { + f_free( &m_pucCurKeyDataBuf); + } + m_pucCurKeyDataBuf = pucNewBuf; + m_uiCurKeyDataBufSize = uiNewLen; + } + + // Retrieve the data + + if (RC_BAD( rc = pBTree->btGetEntry( + m_curKey.ucKey, MAX_KEY_SIZ, m_curKey.uiKeyLen, + m_pucCurKeyDataBuf, m_uiCurKeyDataBufSize, + &m_uiCurKeyDataLen))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set the key position given some KEYPOS structure. + Please note that the blocks in the stack may or may not be used. +****************************************************************************/ +RCODE FSIndexCursor::setKeyPosition( + F_Db * pDb, + FLMBOOL bGoingForward, + FLMBOOL bExcludeKey, + F_DataVector * pExtSrchKey, + KEYPOS * pSearchKey, // Search key + KEYPOS * pFoundKey, + FLMBOOL bGetKeyData, + FLMUINT * puiDataLen, + F_Btree * pBTree, // BTree to use. NULL means use our + // internal one. + FLMUINT * puiAbsolutePos + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDataLen; + FLMINT iCompare; + + // if pBTree is NULL, we are to use m_pbTree. Otherwise, we + // need to open the pBTree and use it. + + if (!pBTree) + { + if (RC_BAD( rc = openBTree( pDb))) + { + goto Exit; + } + pBTree = m_pbTree; + } + + if (pFoundKey != pSearchKey) + { + f_memcpy( pFoundKey->ucKey, pSearchKey->ucKey, + pSearchKey->uiKeyLen); + pFoundKey->uiKeyLen = pSearchKey->uiKeyLen; + } + + m_ixCompare.setSearchKey( pExtSrchKey); + m_ixCompare.setCompareDocId( pExtSrchKey ? FALSE : TRUE); + m_ixCompare.setCompareNodeIds( pExtSrchKey ? FALSE : TRUE); + if (RC_BAD( rc = pBTree->btLocateEntry( pFoundKey->ucKey, MAX_KEY_SIZ, + &pFoundKey->uiKeyLen, + (bGoingForward && bExcludeKey) + ? XFLM_EXCL + : XFLM_INCL, + puiAbsolutePos, &uiDataLen, NULL, NULL))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + } + + if (bGoingForward) + { + if (rc == NE_XFLM_EOF_HIT) + { + goto Exit; + } + } + else + { + + // Going backwards or to last. See if we positioned too far. + + if (rc == NE_XFLM_EOF_HIT) + { + + // Position to last key in tree. + + if (RC_BAD( rc = pBTree->btLastEntry( pFoundKey->ucKey, MAX_KEY_SIZ, + &pFoundKey->uiKeyLen, + &uiDataLen, NULL, NULL))) + { + goto Exit; + } + } + else + { + + // We want to go to the previous key if we went past the key + // we were aiming for, or if we landed on that key, but we + // are doing an exclusive lookup. + + if (!bExcludeKey) + { + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, pExtSrchKey, + NULL, NULL, + pExtSrchKey ? FALSE : TRUE, + pExtSrchKey ? FALSE : TRUE, + pFoundKey->ucKey, pFoundKey->uiKeyLen, + pSearchKey->ucKey, pSearchKey->uiKeyLen, + &iCompare))) + { + goto Exit; + } + } + + if (bExcludeKey || iCompare > 0) + { + + // Position to the previous key. + + if (RC_BAD( rc = pBTree->btPrevEntry( pFoundKey->ucKey, MAX_KEY_SIZ, + &pFoundKey->uiKeyLen, + &uiDataLen, NULL, NULL))) + { + goto Exit; + } + } + } + } + + // See if there is any data to get + + if (bGetKeyData) + { + flmAssert( pFoundKey == &m_curKey); + + if (RC_BAD( rc = getKeyData( pBTree, uiDataLen))) + { + goto Exit; + } + } + if (puiDataLen) + { + *puiDataLen = uiDataLen; + } + +Exit: + + if (RC_BAD( rc)) + { + pFoundKey->uiKeyLen = 0; + if (pBTree == m_pbTree) + { + closeBTree(); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Populate a key from the current key. +****************************************************************************/ +RCODE FSIndexCursor::populateKey( + F_DataVector * pKey + ) +{ + RCODE rc = NE_XFLM_OK; + + pKey->reset(); + if (RC_BAD( rc = pKey->inputKey( m_pIxd, m_curKey.ucKey, m_curKey.uiKeyLen))) + { + goto Exit; + } + + // See if there is a data part + + if (m_pIxd->pFirstData && m_uiCurKeyDataLen) + { + + // Parse the data + + if (RC_BAD( rc = pKey->inputData( m_pIxd, m_pucCurKeyDataBuf, + m_uiCurKeyDataLen))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Return the current record and record id. +****************************************************************************/ +RCODE FSIndexCursor::currentKey( + F_Db * pDb, + F_DataVector * pKey) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + if (m_bAtBOF) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + if (m_bAtEOF) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + // If we get this far, we are positioned on some key. + + flmAssert( m_curKey.uiKeyLen); + + if (RC_BAD( rc = populateKey( pKey))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make sure the current key is positioned in a key set. If not, + move to the next or previous key set until it is. +****************************************************************************/ +RCODE FSIndexCursor::checkIfKeyInRange( + FLMBOOL bPositionForward) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iCompare; + + if (bPositionForward) + { + + // See if the key is within the bounds of the current key set. + // It is already guaranteed to be >= the fromKey, we just need to + // make sure it is <= the until key. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_untilExtKey, + NULL, NULL, + FALSE, FALSE, + m_curKey.ucKey, m_curKey.uiKeyLen, + m_untilKey.ucKey, m_untilKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + if (iCompare > 0) + { + m_bAtEOF = TRUE; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + else + { + + // See if the key is within the bounds of the current key set. + // It is already guaranteed to be <= the untilKey, we just need to + // make sure it is >= the from key. + + if (RC_BAD( rc = ixKeyCompare( m_pDb, m_pIxd, &m_fromExtKey, + NULL, NULL, + FALSE, FALSE, + m_curKey.ucKey, m_curKey.uiKeyLen, + m_fromKey.ucKey, m_fromKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + if (iCompare < 0) + { + m_bAtBOF = TRUE; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Position to and return the first key. +****************************************************************************/ +RCODE FSIndexCursor::firstKey( + F_Db * pDb, + F_DataVector * pKey) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + + // If at BOF and we have a key, then we are positioned on the first + // key already - this would have happened if we had positioned to + // calculate a cost. Rather than do the positioning again, we simply + // set m_bAtBOF to FALSE. + + if (m_bAtBOF && m_curKey.uiKeyLen) + { + m_bAtBOF = FALSE; + } + else + { + m_bAtBOF = m_bAtEOF = FALSE; + if (RC_BAD( rc = setKeyPosition( pDb, TRUE, FALSE, + &m_fromExtKey, &m_fromKey, + &m_curKey, TRUE, NULL, NULL, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + } + goto Exit; + } + } + + // Make sure the current key is within the FROM/UNTIL range. + + if (RC_BAD( rc = checkIfKeyInRange( TRUE))) + { + goto Exit; + } + + // On firstKey, clear out the duplicate elimination result + // set, if there is one. + + if (m_bElimDups) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + if (RC_BAD( rc = populateKey( pKey))) + { + goto Exit; + } + + if (m_bElimDups) + { + FLMUINT64 ui64NodeId = pKey->getID( 0); + + if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) + { + flmAssert( rc != NE_XFLM_EXISTS); + goto Exit; + } + } + m_bMovingForward = TRUE; + +Exit: + + if (RC_BAD( rc)) + { + m_curKey.uiKeyLen = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to the next key. +****************************************************************************/ +RCODE FSIndexCursor::nextKey( + F_Db * pDb, + F_DataVector * pKey, + FLMBOOL bSkipCurrKey) +{ + RCODE rc = NE_XFLM_OK; + KEYPOS saveCurrentKey; + FLMUINT uiDataLen; + FLMINT iCompare; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + if (m_bAtEOF) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + if (m_bAtBOF || !m_curKey.uiKeyLen) + { + rc = firstKey( pDb, pKey); + goto Exit; + } + + // On change of direction, clear out the duplicate elimination result + // set, if there is one. + + if (m_bElimDups && !m_bMovingForward) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + // Save the current key, so we can tell when we have gone beyond it. + + if (bSkipCurrKey) + { + getCurrKey( &saveCurrentKey); + } + + // See if we need to reset the b-tree object we are using + + if (m_bTreeOpen && + (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) + { + closeBTree(); + } + for (;;) + { + + // checkTransaction may have closed the B-tree, in which case we + // need to reopen the b-tree and position exclusive of the current key. + + if (!m_bTreeOpen) + { + if (RC_BAD( rc = setKeyPosition( pDb, TRUE, TRUE, + NULL, &m_curKey, &m_curKey, FALSE, &uiDataLen, + NULL, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + } + goto Exit; + } + } + else + { + + // set the compare object's search key to NULL because m_curKey + // should always be something we obtained from the index, and + // we should not need a search key for doing extended + // comparisons on truncated keys. + + m_ixCompare.setSearchKey( NULL); + m_ixCompare.setCompareNodeIds( TRUE); + m_ixCompare.setCompareDocId( TRUE); + + // Get the next key, if any + + if (RC_BAD( rc = m_pbTree->btNextEntry( m_curKey.ucKey, MAX_KEY_SIZ, + &m_curKey.uiKeyLen, &uiDataLen, NULL, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + } + goto Exit; + } + } + if (!bSkipCurrKey) + { +Check_Key: + if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen))) + { + goto Exit; + } + + // We got to the next key, make sure it is in the key range. + + if (RC_BAD( rc = checkIfKeyInRange( TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = populateKey( pKey))) + { + goto Exit; + } + + if (m_bElimDups) + { + FLMBOOL bDup; + + if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup))) + { + goto Exit; + } + if (bDup) + { + continue; + } + } + break; + } + + // If the bSkipCurrKey flag is TRUE, we want to skip keys until + // we come to one where the key part is different. + + if (RC_BAD( rc = ixKeyCompare( pDb, m_pIxd, NULL, + NULL, NULL, + FALSE, FALSE, + m_curKey.ucKey, m_curKey.uiKeyLen, + saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + // See if the key part is the same. + + if (iCompare != 0) + { + goto Check_Key; + } + } + + m_bMovingForward = TRUE; + +Exit: + + if (RC_BAD( rc)) + { + m_curKey.uiKeyLen = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to and return the last key in the range. +****************************************************************************/ +RCODE FSIndexCursor::lastKey( + F_Db * pDb, + F_DataVector * pKey) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + + // Position to the until key. + + m_bAtBOF = m_bAtEOF = FALSE; + if (RC_BAD( rc = setKeyPosition( pDb, FALSE, FALSE, + &m_untilExtKey, &m_untilKey, + &m_curKey, TRUE, NULL, NULL, NULL))) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_bAtBOF = TRUE; + } + goto Exit; + } + + // Make sure the current key is within one of the FROM/UNTIL sets. + + if (RC_BAD( rc = checkIfKeyInRange( FALSE))) + { + goto Exit; + } + + // On firstKey, clear out the duplicate elimination result + // set, if there is one. + + if (m_bElimDups) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + if (RC_BAD( rc = populateKey( pKey))) + { + goto Exit; + } + + if (m_bElimDups) + { + FLMUINT64 ui64NodeId = pKey->getID( 0); + + if (RC_BAD( rc = m_pNodeIdSet->addEntry( &ui64NodeId))) + { + flmAssert( rc != NE_XFLM_EXISTS); + goto Exit; + } + } + m_bMovingForward = FALSE; + +Exit: + + if (RC_BAD( rc)) + { + m_curKey.uiKeyLen = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to the PREVIOUS key in the range. +****************************************************************************/ +RCODE FSIndexCursor::prevKey( + F_Db * pDb, + F_DataVector * pKey, + FLMBOOL bSkipCurrKey) +{ + RCODE rc = NE_XFLM_OK; + KEYPOS saveCurrentKey; + FLMUINT uiDataLen; + FLMINT iCompare; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + if (m_bAtBOF) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + if (m_bAtEOF || !m_curKey.uiKeyLen) + { + rc = lastKey( pDb, pKey); + goto Exit; + } + + // On change of direction, clear out the duplicate elimination result + // set, if there is one. + + if (m_bElimDups && m_bMovingForward) + { + if (RC_BAD( rc = allocDupCheckSet())) + { + goto Exit; + } + } + + // Save the current key, so we can tell when we have gone beyond it. + + if (bSkipCurrKey) + { + getCurrKey( &saveCurrentKey); + } + + // See if we need to reset the b-tree object we are using + + if (m_bTreeOpen && + (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) + { + closeBTree(); + } + for (;;) + { + + // checkTransaction may have closed the B-tree, in which case we + // need to reopen the b-tree and position exclusive of the current key. + + if (!m_bTreeOpen) + { + if (RC_BAD( rc = setKeyPosition( pDb, FALSE, TRUE, + NULL, &m_curKey, &m_curKey, + FALSE, &uiDataLen, NULL, NULL))) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_bAtBOF = TRUE; + } + goto Exit; + } + } + else + { + + // set the compare object's search key to NULL because m_curKey + // should always be something we obtained from the index, and + // we should not need a search key for doing extended + // comparisons on truncated keys. + + m_ixCompare.setSearchKey( NULL); + m_ixCompare.setCompareNodeIds( TRUE); + m_ixCompare.setCompareDocId( TRUE); + + // Get the previous key, if any + + if (RC_BAD( rc = m_pbTree->btPrevEntry( m_curKey.ucKey, MAX_KEY_SIZ, + &m_curKey.uiKeyLen, &uiDataLen, NULL, NULL))) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_bAtBOF = TRUE; + } + goto Exit; + } + } + if (!bSkipCurrKey) + { +Check_Key: + if (RC_BAD( rc = getKeyData( m_pbTree, uiDataLen))) + { + goto Exit; + } + + // We got to the previous key, make sure it is in the key range. + + if (RC_BAD( rc = checkIfKeyInRange( FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = populateKey( pKey))) + { + goto Exit; + } + + if (m_bElimDups) + { + FLMBOOL bDup; + + if (RC_BAD( rc = checkIfDup( pKey->getID( 0), &bDup))) + { + goto Exit; + } + if (bDup) + { + continue; + } + } + break; + } + + // If the bSkipCurrKey flag is TRUE, we want to skip keys until + // we come to one where the key part is different. + + + if (RC_BAD( rc = ixKeyCompare( pDb, m_pIxd, NULL, + NULL, NULL, + FALSE, FALSE, + m_curKey.ucKey, m_curKey.uiKeyLen, + saveCurrentKey.ucKey, saveCurrentKey.uiKeyLen, + &iCompare))) + { + goto Exit; + } + + // See if the key part is the same. + + if (iCompare != 0) + { + goto Check_Key; + } + } + + m_bMovingForward = FALSE; + +Exit: + + if (RC_BAD( rc)) + { + m_curKey.uiKeyLen = 0; + } + + return( rc); +} + diff --git a/version5/src/fscursor.h b/version5/src/fscursor.h new file mode 100644 index 0000000..69d1a46 --- /dev/null +++ b/version5/src/fscursor.h @@ -0,0 +1,341 @@ +//------------------------------------------------------------------------------ +// Desc: This is the header file that contains the FSIndexCursor class. +// +// 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: fscursor.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FSCURSOR_H +#define FSCURSOR_H + +#include "fdynsset.h" + +typedef struct KeyPosition +{ + FLMBYTE ucKey [MAX_KEY_SIZ]; + FLMUINT uiKeyLen; +} KEYPOS; + +/*============================================================================ +Desc: File system implementation of a cursor for an index. +============================================================================*/ +class FSIndexCursor : public XF_RefCount, public XF_Base +{ +public: + + // Constructors & Destructor + + FSIndexCursor(); + ~FSIndexCursor(); + + void resetCursor( void); + + RCODE resetTransaction( + F_Db * pDb); + + RCODE setupKeys( + F_Db * pDb, + IXD * pIxd, + PATH_PRED * pPred, + FLMBOOL * pbDoNodeMatch, + FLMBOOL * pbCanCompareOnKey, + FLMUINT * puiLeafBlocksBetween, + FLMUINT * puiTotalRefs, + FLMBOOL * pbTotalsEstimated); + + RCODE currentKey( + F_Db * pDb, + F_DataVector * pKey); + + RCODE firstKey( + F_Db * pDb, + F_DataVector * pKey); + + RCODE lastKey( + F_Db * pDb, + F_DataVector * pKey); + + RCODE nextKey( + F_Db * pDb, + F_DataVector * pKey, + FLMBOOL bSkipCurrKey); + + RCODE prevKey( + F_Db * pDb, + F_DataVector * pKey, + FLMBOOL bSkipCurrKey); + +private: + + RCODE allocDupCheckSet( void); + + RCODE checkIfDup( + FLMUINT64 ui64NodeId, + FLMBOOL * pbDup); + + RCODE useNewDb( + F_Db * pDb); + + RCODE openBTree( + F_Db * pDb); + + // Does this index support native absolute positioning? + + FLMBOOL isAbsolutePositionable() + { + return (m_pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE; + } + + RCODE getKeyData( + F_Btree * pBTree, + FLMUINT uiDataLen); + + RCODE setKeyPosition( + F_Db * pDb, + FLMBOOL bGoingForward, + FLMBOOL bExcludeKey, + F_DataVector * pExtSrchKey, + KEYPOS * pSearchKey, + KEYPOS * pFoundKey, + FLMBOOL bGetKeyData, + FLMUINT * puiDataLen, + F_Btree * pBTree, + FLMUINT * puiAbsolutePos); + + FINLINE void closeBTree( void) + { + if (m_bTreeOpen) + { + m_pbTree->btClose(); + m_bTreeOpen = FALSE; + m_pDb = NULL; + m_eTransType = XFLM_NO_TRANS; + } + } + + FINLINE RCODE checkTransaction( + F_Db * pDb) + { + RCODE rc = NE_XFLM_OK; + if (RC_OK( rc = pDb->flushKeys())) + { + rc = (RCODE)((m_ui64CurrTransId != pDb->m_ui64CurrTransID || + m_uiBlkChangeCnt != pDb->m_uiBlkChangeCnt) + ? resetTransaction( pDb) + : NE_XFLM_OK); + } + return( rc); + } + + RCODE populateKey( + F_DataVector * pKey); + + RCODE checkIfKeyInRange( + FLMBOOL bPositionForward); + + FINLINE void getCurrKey( + KEYPOS * pKey + ) + { + f_memcpy( pKey->ucKey, m_curKey.ucKey, m_curKey.uiKeyLen); + pKey->uiKeyLen = m_curKey.uiKeyLen; + } + + // Database information + + FLMUINT64 m_ui64CurrTransId; + FLMUINT m_uiBlkChangeCnt; + FLMUINT m_uiIndexNum; + LFILE * m_pLFile; + IXD * m_pIxd; + F_Db * m_pDb; + eDbTransType m_eTransType; + + // Key range information + + FLMBOOL m_bSetup; + KEYPOS m_fromKey; + KEYPOS m_untilKey; + + // State information. + + FLMBOOL m_bAtBOF; // Before the first key. + FLMBOOL m_bAtEOF; // After the last key. + KEYPOS m_curKey; // Current key + FLMBYTE * m_pucCurKeyDataBuf; + FLMUINT m_uiCurKeyDataBufSize; + FLMUINT m_uiCurKeyDataLen; + F_Btree * m_pbTree; + FLMBOOL m_bTreeOpen; + FDynSearchSet * m_pNodeIdSet; + FLMBOOL m_bElimDups; + FLMBOOL m_bMovingForward; + IXKeyCompare m_ixCompare; + F_DataVector m_fromExtKey; + F_DataVector m_untilExtKey; + + friend class F_Query; +}; + +/*============================================================================ +Desc: File system implementation of a cursor for a collection. +============================================================================*/ +class FSCollectionCursor : public XF_RefCount, public XF_Base +{ +public: + + // Constructors & Destructor + + FSCollectionCursor(); + ~FSCollectionCursor(); + + void resetCursor(); + + RCODE resetTransaction( + F_Db * pDb); + + RCODE setupRange( + F_Db * pDb, + FLMUINT uiCollection, + FLMBOOL bDocumentIds, + FLMUINT64 ui64LowNodeId, + FLMUINT64 ui64HighNodeId, + FLMUINT * puiLeafBlocksBetween, + FLMUINT * puiTotalNodes, + FLMBOOL * pbTotalsEstimated); + + RCODE currentNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId); + + RCODE firstNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId); + + RCODE lastNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId); + + RCODE nextNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId); + + RCODE prevNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId); + +private: + + RCODE setNodePosition( + F_Db * pDb, + FLMBOOL bGoingForward, + FLMUINT64 ui64NodeId, + FLMUINT64 * pui64FoundNodeId, + F_Btree * pBTree); + + RCODE openBTree( + F_Db * pDb); + + FINLINE void closeBTree( void) + { + if (m_bTreeOpen) + { + m_pbTree->btClose(); + m_bTreeOpen = FALSE; + m_pDb = NULL; + m_eTransType = XFLM_NO_TRANS; + } + } + + FINLINE RCODE checkTransaction( + F_Db * pDb) + { + RCODE rc = NE_XFLM_OK; + + if (pDb->m_uiDirtyNodeCount) + { + if (RC_BAD( rc = pDb->flushDirtyNodes())) + { + goto Exit; + } + } + rc = (RCODE)((m_pDb != pDb || + m_ui64CurrTransId != pDb->m_ui64CurrTransID || + m_uiBlkChangeCnt != pDb->m_uiBlkChangeCnt) + ? resetTransaction( pDb) + : NE_XFLM_OK); + Exit: + return( rc); + } + + FINLINE RCODE populateNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) + { + if (pui64NodeId) + { + *pui64NodeId = m_ui64CurNodeId; + } + if (ppNode) + { + return( pDb->getNode( m_uiCollection, m_ui64CurNodeId, ppNode)); + } + return( NE_XFLM_OK); + } + + RCODE checkIfNodeInRange( + FLMBOOL bPositionForward); + + // Database Information + + FLMUINT64 m_ui64CurrTransId; + FLMUINT m_uiBlkChangeCnt; + FLMUINT m_uiCollection; + F_COLLECTION * m_pCollection; + FLMBOOL m_bDocumentIds; + LFILE * m_pLFile; + F_Db * m_pDb; + eDbTransType m_eTransType; + + // Key range information + + FLMBOOL m_bSetup; + FLMUINT64 m_ui64FromNodeId; + FLMUINT64 m_ui64UntilNodeId; + + // State information. + + FLMBOOL m_bAtBOF; // Before the first key. + FLMBOOL m_bAtEOF; // After the last key. + FLMUINT64 m_ui64CurNodeId; // Current node + F_Btree * m_pbTree; + FLMBOOL m_bTreeOpen; +}; + + +#endif diff --git a/version5/src/fsdatacu.cpp b/version5/src/fsdatacu.cpp new file mode 100644 index 0000000..f8e39af --- /dev/null +++ b/version5/src/fsdatacu.cpp @@ -0,0 +1,905 @@ +//------------------------------------------------------------------------------ +// Desc: Cursor routines to get the complexity of the file system out +// of the search code. +// +// 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: fsdatacu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "fscursor.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +FSCollectionCursor::FSCollectionCursor() +{ + m_pbTree = NULL; + m_bTreeOpen = FALSE; + m_pCollection = NULL; + m_bDocumentIds = FALSE; + m_pLFile = NULL; + m_pDb = NULL; + m_eTransType = XFLM_NO_TRANS; + resetCursor(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSCollectionCursor::~FSCollectionCursor() +{ + closeBTree(); + if (m_pbTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &m_pbTree); + } +} + +/**************************************************************************** +Desc: Resets any allocations, keys, state, etc. +****************************************************************************/ +void FSCollectionCursor::resetCursor( void) +{ + closeBTree(); + m_uiCollection = 0; + m_bDocumentIds = FALSE; + m_uiBlkChangeCnt = 0; + m_ui64CurrTransId = 0; + m_ui64CurNodeId = 0; + m_bAtBOF = TRUE; + m_bAtEOF = FALSE; + m_bSetup = FALSE; +} + +/**************************************************************************** +Desc: Resets to a new transaction that may change the read consistency of + the query. +****************************************************************************/ +RCODE FSCollectionCursor::resetTransaction( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + + if (RC_BAD( rc = pDb->m_pDict->getCollection( m_uiCollection, + &pCollection))) + { + goto Exit; + } + if (pCollection != m_pCollection) + { + m_pCollection = pCollection; + m_pLFile = &pCollection->lfInfo; + if (m_bTreeOpen) + { + closeBTree(); + } + m_pDb = pDb; + m_eTransType = pDb->m_eTransType; + } + m_ui64CurrTransId = pDb->m_ui64CurrTransID; + m_uiBlkChangeCnt = pDb->m_uiBlkChangeCnt; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Open the F_Btree object if not already open. +****************************************************************************/ +RCODE FSCollectionCursor::openBTree( + F_Db * pDb + ) +{ + RCODE rc = NE_XFLM_OK; + + if (!m_bTreeOpen) + { + if (!m_pbTree) + { + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &m_pbTree))) + { + goto Exit; + } + } +Open_Btree: + if (RC_BAD( rc = m_pbTree->btOpen( pDb, m_pLFile, FALSE, FALSE))) + { + goto Exit; + } + m_bTreeOpen = TRUE; + m_pDb = pDb; + m_eTransType = pDb->m_eTransType; + } + else + { + if (pDb != m_pDb || pDb->m_eTransType != m_eTransType) + { + closeBTree(); + goto Open_Btree; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set the node position. +****************************************************************************/ +RCODE FSCollectionCursor::setNodePosition( + F_Db * pDb, + FLMBOOL bGoingForward, + FLMUINT64 ui64NodeId, + FLMUINT64 * pui64FoundNodeId, + F_Btree * pBTree // BTree to use. NULL means use our + // internal one. + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucKey [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + FLMUINT64 ui64TmpNodeId; + IF_DOMNode * pNode = NULL; + + // if pBTree is NULL, we are to use m_pbTree. Otherwise, we + // need to open the pBTree and use it. + + if (!pBTree) + { + if (RC_BAD( rc = openBTree( pDb))) + { + goto Exit; + } + pBTree = m_pbTree; + } + + uiKeyLen = sizeof( ucKey); + if (RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = pBTree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + } + + if (bGoingForward) + { + if (rc == NE_XFLM_EOF_HIT) + { + goto Exit; + } + } + else + { + + // Going backwards or to last. See if we positioned too far. + + if (rc == NE_XFLM_BOF_HIT || rc == NE_XFLM_EOF_HIT) + { + + // Position to last key in tree. + + if (RC_BAD( rc = pBTree->btLastEntry( ucKey, sizeof( ucKey), + &uiKeyLen, + NULL, NULL, NULL))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64TmpNodeId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + if (ui64TmpNodeId > ui64NodeId) + { + + // Position to the previous key. + + if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, sizeof( ucKey), + &uiKeyLen, NULL, NULL, NULL))) + { + goto Exit; + } + } + } + } + + if (!m_bDocumentIds) + { + if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + pui64FoundNodeId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + } + else + { + + // Need to position to a document ID if we are looking only for document + // root nodes. + + for (;;) + { + if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64TmpNodeId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + // The following code tests to see if we have gone past the + // from or until node id. It is an optimization that isn't + // actually necessary for the code to work properly, because + // the outside code also makes this check. This just allows + // us to quit earlier than we otherwise might while looking + // for a document root node. + + if (bGoingForward) + { + + // If we have gone past the until node id, we are done. + + if (ui64TmpNodeId > m_ui64UntilNodeId) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + else + { + + // If we have gone past the from node id, we are done. + + if (ui64TmpNodeId < m_ui64FromNodeId) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + } + + if (RC_BAD( rc = pDb->getNode( m_uiCollection, ui64TmpNodeId, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + // If the node is a root node, we have a document we can + // process. + + if (((F_DOMNode *)pNode)->isRootNode()) + { + *pui64FoundNodeId = ui64TmpNodeId; + break; + } + + // Need to go to the next or previous node. + + if (bGoingForward) + { + if (RC_BAD( rc = pBTree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pBTree->btPrevEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + goto Exit; + } + } + } + } + +Exit: + + if (RC_BAD( rc)) + { + if (pBTree == m_pbTree) + { + closeBTree(); + } + } + if (pNode) + { + pNode->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Setup the from and until keys in the cursor. Return counts + after positioning to the from and until key in the index. + This code does not work with multiple key sets of FROM/UNTIL keys. +****************************************************************************/ +RCODE FSCollectionCursor::setupRange( + F_Db * pDb, + FLMUINT uiCollection, + FLMBOOL bDocumentIds, + FLMUINT64 ui64LowNodeId, + FLMUINT64 ui64HighNodeId, + FLMUINT * puiLeafBlocksBetween,// [out] blocks between the stacks + FLMUINT * puiTotalNodes, // [out] + FLMBOOL * pbTotalsEstimated) // [out] set to TRUE when estimating. +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pUntilBTree = NULL; + + m_bAtBOF = TRUE; + m_bAtEOF = FALSE; + m_uiCollection = uiCollection; + m_bDocumentIds = bDocumentIds; + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + + m_ui64FromNodeId = ui64LowNodeId; + m_ui64UntilNodeId = ui64HighNodeId; + m_bSetup = TRUE; + m_ui64CurNodeId = 0; + + // Want any of the counts back? + + if (puiLeafBlocksBetween || puiTotalNodes) + { + if (puiLeafBlocksBetween) + { + *puiLeafBlocksBetween = 0; + } + if (puiTotalNodes) + { + *puiTotalNodes = 0; + } + if (pbTotalsEstimated) + { + *pbTotalsEstimated = FALSE; + } + + // Position to the FROM and UNTIL key so we can get the stats. + + if (RC_BAD( rc = setNodePosition( pDb, TRUE, + m_ui64FromNodeId, &m_ui64CurNodeId, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // All nodes between FROM and UNTIL may be gone. + + if (m_ui64CurNodeId < m_ui64UntilNodeId) + { + FLMUINT64 ui64TmpNodeId; + + // Get a btree object + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( + &pUntilBTree))) + { + goto Exit; + } + if (RC_BAD( rc = pUntilBTree->btOpen( pDb, m_pLFile, + FALSE, FALSE))) + { + goto Exit; + } + + // We better be able to at least find m_ui64CurNodeId going + // backward from m_ui64UntilNodeId. + + if (RC_BAD( rc = setNodePosition( pDb, FALSE, + m_ui64UntilNodeId, &ui64TmpNodeId, pUntilBTree))) + { + goto Exit; + } + if (RC_BAD( rc = m_pbTree->btComputeCounts( pUntilBTree, + puiLeafBlocksBetween, puiTotalNodes, + pbTotalsEstimated, + (pDb->m_pDatabase->m_uiBlockSize * 3) / 4))) + { + goto Exit; + } + } + } + +Exit: + + if (pUntilBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pUntilBTree); + } + + return( rc); +} + +/**************************************************************************** +Desc: Return the current node. +****************************************************************************/ +RCODE FSCollectionCursor::currentNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + if (m_bAtBOF) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + if (m_bAtEOF) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + flmAssert( m_ui64CurNodeId); + + if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Make sure the current node is positioned in the range for the cursor. +****************************************************************************/ +RCODE FSCollectionCursor::checkIfNodeInRange( + FLMBOOL bPositionForward) +{ + RCODE rc = NE_XFLM_OK; + + if (bPositionForward) + { + if (m_ui64CurNodeId > m_ui64UntilNodeId) + { + m_bAtEOF = TRUE; + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + } + else + { + if (m_ui64CurNodeId < m_ui64FromNodeId) + { + m_bAtBOF = TRUE; + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Position to and return the first node. +****************************************************************************/ +RCODE FSCollectionCursor::firstNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + + // If at BOF and we have a node, then we are positioned on the first + // node already - this would have happened if we had positioned to + // calculate a cost. Rather than do the positioning again, we simply + // set m_bAtBOF to FALSE. + + if (m_bAtBOF && m_ui64CurNodeId) + { + m_bAtBOF = FALSE; + } + else + { + m_bAtBOF = m_bAtEOF = FALSE; + if (RC_BAD( rc = setNodePosition( pDb, TRUE, m_ui64FromNodeId, + &m_ui64CurNodeId, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + } + goto Exit; + } + } + + // Make sure the current node ID is within the FROM/UNTIL range. + + if (RC_BAD( rc = checkIfNodeInRange( TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + m_ui64CurNodeId = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to the next node. +****************************************************************************/ +RCODE FSCollectionCursor::nextNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + if (m_bAtEOF) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + if (m_bAtBOF || !m_ui64CurNodeId) + { + rc = firstNode( pDb, ppNode, pui64NodeId); + goto Exit; + } + + if (m_bDocumentIds) + { + if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId, + &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + + // Allow to just fall through to below and call setNodePosition. + } + else + { + goto Exit; + } + } + else if (((F_DOMNode *)pNode)->isRootNode()) + { + if (RC_BAD( rc = pNode->getNextDocument( pDb, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_EOF_HIT; + m_bAtEOF = TRUE; + } + } + else + { + if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId))) + { + goto Exit; + } + + if (RC_OK( rc = checkIfNodeInRange( TRUE))) + { + if (pui64NodeId) + { + *pui64NodeId = m_ui64CurNodeId; + } + if (ppNode) + { + if (*ppNode) + { + (*ppNode)->Release(); + } + *ppNode = pNode; + pNode = NULL; + } + } + } + goto Exit; + } + // Fall through to below to call setNodePosition. + } + + // Get the next node, if any + + if (m_ui64CurNodeId == ~((FLMUINT64)0)) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + m_bAtEOF = TRUE; + goto Exit; + } + + // See if we need to reset the b-tree object we are using + + if (m_bTreeOpen && + (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) + { + closeBTree(); + } + + if (RC_BAD( rc = setNodePosition( pDb, TRUE, + m_ui64CurNodeId + 1, &m_ui64CurNodeId, NULL))) + { + if (rc == NE_XFLM_EOF_HIT) + { + m_bAtEOF = TRUE; + } + goto Exit; + } + + // Make sure the current node ID is within the FROM/UNTIL range. + + if (RC_BAD( rc = checkIfNodeInRange( TRUE))) + { + goto Exit; + } + if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) + { + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (RC_BAD( rc)) + { + m_ui64CurNodeId = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to and return the last node. +****************************************************************************/ +RCODE FSCollectionCursor::lastNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + + // Position to the until node + + m_bAtBOF = m_bAtEOF = FALSE; + if (RC_BAD( rc = setNodePosition( pDb, FALSE, m_ui64UntilNodeId, + &m_ui64CurNodeId, NULL))) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_bAtBOF = TRUE; + } + goto Exit; + } + + // Make sure the current node ID is within the FROM/UNTIL range. + + if (RC_BAD( rc = checkIfNodeInRange( FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + m_ui64CurNodeId = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: Position to the previous node. +****************************************************************************/ +RCODE FSCollectionCursor::prevNode( + F_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT64 * pui64NodeId + ) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + + if (RC_BAD( rc = checkTransaction( pDb))) + { + goto Exit; + } + flmAssert( m_bSetup); + if (m_bAtBOF) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + goto Exit; + } + if (m_bAtEOF || !m_ui64CurNodeId) + { + rc = lastNode( pDb, ppNode, pui64NodeId); + goto Exit; + } + + if (m_bDocumentIds) + { + if (RC_BAD( rc = pDb->getNode( m_uiCollection, m_ui64CurNodeId, + &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + + // Allow to just fall through to below and call setNodePosition. + } + else + { + goto Exit; + } + } + else if (((F_DOMNode *)pNode)->isRootNode()) + { + if (RC_BAD( rc = pNode->getPreviousDocument( pDb, &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_BOF_HIT; + m_bAtBOF = TRUE; + } + } + else + { + if( RC_BAD( rc = pNode->getNodeId( m_pDb, &m_ui64CurNodeId))) + { + goto Exit; + } + + if (RC_OK( rc = checkIfNodeInRange( FALSE))) + { + if (pui64NodeId) + { + *pui64NodeId = m_ui64CurNodeId; + } + if (ppNode) + { + if (*ppNode) + { + (*ppNode)->Release(); + } + *ppNode = pNode; + pNode = NULL; + } + } + } + goto Exit; + } + // Fall through to below to call setNodePosition. + } + + // Get the previous node, if any + + if (m_ui64CurNodeId == 1) + { + rc = RC_SET( NE_XFLM_BOF_HIT); + m_bAtBOF = TRUE; + goto Exit; + } + + // See if we need to reset the b-tree object we are using + + if (m_bTreeOpen && + (pDb != m_pDb || pDb->m_eTransType != m_eTransType)) + { + closeBTree(); + } + + if (RC_BAD( rc = setNodePosition( pDb, FALSE, + m_ui64CurNodeId - 1, &m_ui64CurNodeId, NULL))) + { + if (rc == NE_XFLM_BOF_HIT) + { + m_bAtBOF = TRUE; + } + goto Exit; + } + + // Make sure the current node ID is within the FROM/UNTIL range. + + if (RC_BAD( rc = checkIfNodeInRange( FALSE))) + { + goto Exit; + } + if (RC_BAD( rc = populateNode( pDb, ppNode, pui64NodeId))) + { + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (RC_BAD( rc)) + { + m_ui64CurNodeId = 0; + } + return( rc); +} diff --git a/version5/src/fslfile.cpp b/version5/src/fslfile.cpp new file mode 100644 index 0000000..0a22085 --- /dev/null +++ b/version5/src/fslfile.cpp @@ -0,0 +1,648 @@ +//------------------------------------------------------------------------------ +// Desc: Routines for reading and writing logical file headers. +// +// 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: fslfile.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC FLMUINT FSLFileFindEmpty( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr); + +/*************************************************************************** +Desc: Searches a block for an empty LFH slot. This is called whenever + a new logical file is create so we re-use the slots. +Ret: 0-Empty slot not found + non-zero - offset in the block of the empty slot +***************************************************************************/ +FSTATIC FLMUINT FSLFileFindEmpty( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr) +{ + FLMUINT uiPos = SIZEOF_STD_BLK_HDR; + FLMUINT uiEndPos = blkGetEnd( uiBlockSize, SIZEOF_STD_BLK_HDR, + pBlkHdr); + F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + SIZEOF_STD_BLK_HDR); + + while (uiPos < uiEndPos) + { + if (pLfHdr->ui32LfType == XFLM_LF_INVALID) + { + break; + } + + uiPos += sizeof( F_LF_HDR); + pLfHdr++; + } + return( (uiPos < uiEndPos) ? uiPos : 0); +} + +/*************************************************************************** +Desc: Retrieves the logical file header record from disk & updates LFILE. + Called when it is discovered that the LFH for a + particular logical file is out of date. +*****************************************************************************/ +RCODE F_Database::lFileRead( + F_Db * pDb, + LFILE * pLFile, + F_COLLECTION * pCollection + ) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache; + FLMBOOL bReleaseCache = FALSE; + + // Read in the block containing the logical file header + + if (RC_BAD( rc = getBlock( pDb, NULL, + pLFile->uiBlkAddress, NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + // Copy the LFH from the block to the LFILE + + FSLFileIn( (FLMBYTE *)(pSCache->m_pBlkHdr) + pLFile->uiOffsetInBlk, + pLFile, pCollection, pLFile->uiBlkAddress, pLFile->uiOffsetInBlk); + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + } + + return( rc); +} + +/*************************************************************************** +Desc: Update the LFH data on disk. +*****************************************************************************/ +RCODE F_Database::lFileWrite( + F_Db * pDb, + F_COLLECTION * pCollection, + LFILE * pLFile) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache; + FLMBOOL bReleaseCache = FALSE; + F_LF_HDR * pLfHdr; +#ifdef DEBUG + F_CachedBlock * pTmpSCache = NULL; +#endif + + flmAssert( !pDb->m_pDatabase->m_pRfl || + !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + // Read in the block containing the logical file header + + if (RC_BAD( rc = getBlock( pDb, NULL, + pLFile->uiBlkAddress, NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + // Log the block before modifying it + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + + // Now modify the block and set its status to dirty + + pLfHdr = (F_LF_HDR *)((FLMBYTE *)(pSCache->m_pBlkHdr) + + pLFile->uiOffsetInBlk); + + // If deleted, fill with 0, except for type - it is set below + + if (pLFile->eLfType == XFLM_LF_INVALID) + { + f_memset( pLfHdr, 0, sizeof( F_LF_HDR)); + pLfHdr->ui32LfType = XFLM_LF_INVALID; + } + else + { + + pLfHdr->ui32LfNumber = (FLMUINT32)pLFile->uiLfNum; + pLfHdr->ui32LfType = (FLMUINT32)pLFile->eLfType; + pLfHdr->ui32EncId = (FLMUINT32)pLFile->uiEncId; + +#ifdef DEBUG + if (RC_BAD( rc = getBlock( pDb, NULL, + pLFile->uiRootBlk, NULL, &pTmpSCache))) + { + goto Exit; + } + + if (!isRootBlk( (F_BTREE_BLK_HDR *)pTmpSCache->m_pBlkHdr)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } +#endif + + pLfHdr->ui32RootBlkAddr = (FLMUINT32)pLFile->uiRootBlk; + + if (pCollection) + { + flmAssert( pLFile == &pCollection->lfInfo); + flmAssert( pLFile->eLfType == XFLM_LF_COLLECTION); + pLfHdr->ui64NextNodeId = pCollection->ui64NextNodeId; + pLfHdr->ui64FirstDocId = pCollection->ui64FirstDocId; + pLfHdr->ui64LastDocId = pCollection->ui64LastDocId; + pCollection->bNeedToUpdateNodes = FALSE; + } + else + { + flmAssert( pLFile->eLfType == XFLM_LF_INDEX); + pLfHdr->ui64NextNodeId = 0; + pLfHdr->ui64FirstDocId = 0; + pLfHdr->ui64LastDocId = 0; + } + } + + // If the LFILE was deleted, we need to set pLFile->uiLfNum to zero. + // This should happen only AFTER the LFILE update is logged, because + // logLFileUpdate relies on pLFile->uiLfNum being non-zero. + + if (pLFile->eLfType == XFLM_LF_INVALID) + { + pLFile->uiLfNum = 0; + } + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + } + +#ifdef DEBUG + if (pTmpSCache) + { + ScaReleaseCache( pTmpSCache, FALSE); + } +#endif + + return( rc); +} + +/*************************************************************************** +Desc: Creates and initializes a LFILE structure on disk and in memory. +*****************************************************************************/ +RCODE F_Database::lFileCreate( + F_Db * pDb, + LFILE * pLFile, + F_COLLECTION * pCollection, + FLMUINT uiLfNum, + eLFileType eLfType, + FLMBOOL bCounts, + FLMBOOL bHaveData, + FLMUINT uiEncId) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pNewSCache; + F_CachedBlock * pSCache = NULL; + F_BLK_HDR * pBlkHdr; + FLMUINT uiBlkAddress; + FLMUINT uiNextBlkAddress; + FLMUINT uiEndPos; + FLMUINT uiPos; + FLMBOOL bReleaseCache2 = FALSE; + FLMBOOL bReleaseCache = FALSE; + F_Btree * pbTree = NULL; + + flmAssert( !pDb->m_pDatabase->m_pRfl || + !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + + if (eLfType == XFLM_LF_COLLECTION) + { + if( bCounts) + { + // Force bCounts to be FALSE in this case + flmAssert( 0); + bCounts = FALSE; + } + + if( !bHaveData) + { + // Force bHaveData to be TRUE in this case + flmAssert( 0); + bHaveData = TRUE; + } + } + + // Find an available slot to create the LFH -- follow the linked list + // of LFH blocks to find one. + + uiNextBlkAddress = (FLMUINT)m_uncommittedDbHdr.ui32FirstLFBlkAddr; + + // Better be at least one LFH block. + + if (uiNextBlkAddress == 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + while (uiNextBlkAddress != 0) + { + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + bReleaseCache = FALSE; + } + uiBlkAddress = uiNextBlkAddress; + + if (RC_BAD( rc = getBlock( pDb, NULL, + uiBlkAddress, NULL, &pSCache))) + { + goto Exit; + } + bReleaseCache = TRUE; + + pBlkHdr = pSCache->m_pBlkHdr; + uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32NextBlkInChain; + uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); + if ((uiPos = FSLFileFindEmpty( m_uiBlockSize, pBlkHdr)) != 0) + { + break; + } + } + + // If we did not find a deleted slot we can use, see if there + // is room for a new logical file in the last block + // in the chain. If not, allocate a new block. + + if (uiPos == 0) + { + uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, pBlkHdr); + + // Allocate new block? + + if (uiEndPos + sizeof( F_LF_HDR) >= m_uiBlockSize) + { + if (RC_BAD( rc = createBlock( pDb, &pNewSCache))) + { + goto Exit; + } + bReleaseCache2 = TRUE; + + pBlkHdr = pNewSCache->m_pBlkHdr; + uiNextBlkAddress = (FLMUINT)pBlkHdr->ui32BlkAddr; + + // Modify the new block's next pointer and other fields. + + pBlkHdr->ui32NextBlkInChain = 0; + pBlkHdr->ui32PrevBlkInChain = (FLMUINT32)uiBlkAddress; + pBlkHdr->ui8BlkType = BT_LFH_BLK; + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - + SIZEOF_STD_BLK_HDR); + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + pSCache->m_pBlkHdr->ui32NextBlkInChain = (FLMUINT32)uiNextBlkAddress; + + // Set everything up so we are pointing to the new block. + + ScaReleaseCache( pSCache, FALSE); + pSCache = pNewSCache; + bReleaseCache2 = FALSE; + uiEndPos = blkGetEnd( m_uiBlockSize, SIZEOF_STD_BLK_HDR, + pSCache->m_pBlkHdr); + uiBlkAddress = uiNextBlkAddress; + } + + // Modify the end of block pointer -- log block before modifying. + + uiPos = uiEndPos; + uiEndPos += sizeof( F_LF_HDR); + } + + // Call memset to ensure unused bytes are zero. + // pBlkHdr, uiPos and uiEndPos should ALL be set. + + if (RC_BAD( rc = logPhysBlk( pDb, &pSCache))) + { + goto Exit; + } + pBlkHdr = pSCache->m_pBlkHdr; + f_memset( (FLMBYTE *)(pBlkHdr) + uiPos, 0, sizeof( F_LF_HDR)); + flmAssert( uiEndPos >= SIZEOF_STD_BLK_HDR && + uiEndPos <= m_uiBlockSize); + pBlkHdr->ui16BlkBytesAvail = (FLMUINT16)(m_uiBlockSize - uiEndPos); + + // Done with block in this routine + + ScaReleaseCache( pSCache, FALSE); + bReleaseCache = FALSE; + + // Set the variables in the LFILE structure to later save to disk + + pLFile->uiLfNum = uiLfNum; + pLFile->eLfType = eLfType; + pLFile->uiBlkAddress = uiBlkAddress; + pLFile->uiOffsetInBlk = uiPos; + pLFile->uiEncId = uiEncId; + if (pCollection) + { + pCollection->ui64NextNodeId = 1; + pCollection->ui64FirstDocId = 0; + pCollection->ui64LastDocId = 0; + pCollection->bNeedToUpdateNodes = TRUE; + } + + // Get the btree... + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) + { + goto Exit; + } + + if (RC_BAD( rc = pbTree->btCreate( pDb, pLFile, bCounts, bHaveData))) + { + goto Exit; + } + + if (RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile))) + { + goto Exit; + } + +Exit: + + if (bReleaseCache) + { + ScaReleaseCache( pSCache, FALSE); + } + if (bReleaseCache2) + { + ScaReleaseCache( pNewSCache, FALSE); + } + if (pbTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); + } + return( rc); +} + +/*************************************************************************** +Desc: Delete a logical file. +*****************************************************************************/ +RCODE F_Database::lFileDelete( + F_Db * pDb, + F_COLLECTION * pCollection, + LFILE * pLFile, + FLMBOOL bCounts, + FLMBOOL bHaveData) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pbTree = NULL; + F_DOMNode * pDoc = NULL; + F_DOMNode * pChainNode = NULL; + F_DOMNode * pAddrNode = NULL; + FLMUINT uiLoop; + + flmAssert( pDb->m_uiFlags & FDB_UPDATED_DICTIONARY); + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbTree))) + { + goto Exit; + } + + // Delete the logical file's B-Tree blocks. + // If there is no root block, no need to do anything. + + flmAssert( pLFile->uiRootBlk); + + if (pLFile->eLfType == XFLM_LF_COLLECTION) + { + flmAssert( pCollection); + flmAssert( !bCounts && bHaveData); + bCounts = FALSE; + bHaveData = TRUE; + + if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData))) + { + goto Exit; + } + + // Delete the B-Tree + + if (RC_BAD( rc = pbTree->btDeleteTree( pDb->m_pDeleteStatus))) + { + goto Exit; + } + } + else + { + FLMUINT puiBlkChains[ BH_MAX_LEVELS]; + FLMUINT uiChainCount; + + flmAssert( !pCollection); + flmAssert( pLFile->eLfType == XFLM_LF_INDEX); + + if( RC_BAD( rc = pbTree->btOpen( pDb, pLFile, bCounts, bHaveData))) + { + goto Exit; + } + + if( RC_BAD( rc = pbTree->btGetBlockChains( + puiBlkChains, &uiChainCount))) + { + goto Exit; + } + + // Indexes are always deleted in the background. + // Set up a maintenance document to free the blocks of + // the B-Tree. + + if( RC_BAD( rc = pDb->createRootNode( XFLM_MAINT_COLLECTION, + ELM_DELETE_TAG, ELEMENT_NODE, &pDoc))) + { + goto Exit; + } + + for( uiLoop = 0; uiLoop < uiChainCount; uiLoop++) + { + if( RC_BAD( rc = pDoc->createNode( pDb, + ELEMENT_NODE, ELM_BLOCK_CHAIN_TAG, XFLM_LAST_CHILD, + (IF_DOMNode **)&pChainNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pChainNode->createAttribute( + pDb, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pAddrNode->setUINT( + pDb, puiBlkChains[ uiLoop]))) + { + goto Exit; + } + + if( RC_BAD( rc = pAddrNode->addModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pChainNode->addModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDoc->addModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->documentDone( pDoc))) + { + goto Exit; + } + + // Signal the maintenance thread that it has work to do + + f_semSignal( m_hMaintSem); + } + + // Delete the LFILE entry. + + pLFile->uiRootBlk = 0; + pLFile->eLfType = XFLM_LF_INVALID; + + if( RC_BAD( rc = lFileWrite( pDb, pCollection, pLFile))) + { + goto Exit; + } + +Exit: + + if( pChainNode) + { + pChainNode->Release(); + } + + if( pAddrNode) + { + pAddrNode->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + if( pbTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbTree); + } + + return( rc); +} + +/*************************************************************************** +Desc: Set the next node ID for a collection. Must be inside an update + transaction. +*****************************************************************************/ +RCODE F_Db::setNextNodeId( + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId) +{ + RCODE rc = NE_XFLM_OK; + F_COLLECTION * pCollection; + F_Rfl * pRfl = m_pDatabase->m_pRfl; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiRflToken = 0; + + if( RC_BAD( rc = checkTransaction( XFLM_UPDATE_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Disable RFL logging + + pRfl->disableLogging( &uiRflToken); + + // Get a pointer to the collection + + if (RC_BAD( rc = m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + // Set the next NODE ID for the collection + + if (ui64NextNodeId > pCollection->ui64NextNodeId) + { + pCollection->ui64NextNodeId = ui64NextNodeId; + pCollection->bNeedToUpdateNodes = TRUE; + } + + // Log the operation + + pRfl->enableLogging( &uiRflToken); + + if( RC_BAD( rc = pRfl->logSetNextNodeId( this, uiCollection, ui64NextNodeId))) + { + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + if( uiRflToken) + { + pRfl->enableLogging( &uiRflToken); + } + + if( bStartedTrans) + { + if( RC_BAD( rc)) + { + transAbort(); + } + else + { + rc = transCommit(); + } + } + + return( rc); +} diff --git a/version5/src/fslfileu.cpp b/version5/src/fslfileu.cpp new file mode 100644 index 0000000..6bba570 --- /dev/null +++ b/version5/src/fslfileu.cpp @@ -0,0 +1,3036 @@ +//------------------------------------------------------------------------------ +// Desc: Routines to perform dictionary updates. +// +// 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: fslfileu.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +typedef struct FTravTag * F_TRAV_p; +typedef struct FTravTag +{ + F_DOMNode * pNode; + ICD * pRefIcd; + ICD * pIcd; + IX_CONTEXT * pIxContext; + F_TRAV_p pParent; + F_TRAV_p pChild; +} F_TRAV; + +FSTATIC void kyFreeIxContext( + IXD * pIxd, + IX_CONTEXT * pIxContext, + IX_CONTEXT ** ppIxContextList); + +/**************************************************************************** +Desc: Check a dictionary definition for duplicate names or numbers. + If no number was assigned, assign one. If deleting, verify that the + delete is allowed at this point. Freeze certain attributes so + they cannot be changed. +****************************************************************************/ +RCODE F_Db::checkDictDefInfo( + FLMUINT64 ui64DocumentID, + FLMBOOL bDeleting, + FLMUINT * puiDictType, + FLMUINT * puiDictNumber) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttr = NULL; + F_DOMNode * pTmpNode = NULL; + FLMBYTE szTmpBuf [80]; + FLMUINT uiNameId; + F_DataVector key1; + F_DataVector key2; + FLMUNICODE * puzName = NULL; + FLMUNICODE * puzNamespace = NULL; + FLMUINT uiState = 0; + FLMBOOL bFoundState = FALSE; + FLMUINT uiMaxTagNum; + FLMBOOL bAmbiguous; + FLMBOOL bHasAttrs; + FLMUINT uiInsertPos; + FLM_TAG_INFO * pTagInfo; + + *puiDictType = 0; + *puiDictNumber = 0; + + // Get the root node of the definition document + + if (RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, ui64DocumentID, &pNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pNode->getNameId( this, puiDictType))) + { + goto Exit; + } + + // Ignore anything that is one one of our predefined types. + + switch (*puiDictType) + { + case ELM_ELEMENT_TAG: + uiMaxTagNum = XFLM_MAX_ELEMENT_NUM; + break; + case ELM_ATTRIBUTE_TAG: + uiMaxTagNum = XFLM_MAX_ATTRIBUTE_NUM; + break; + case ELM_INDEX_TAG: + uiMaxTagNum = XFLM_MAX_INDEX_NUM; + break; + case ELM_PREFIX_TAG: + uiMaxTagNum = XFLM_MAX_PREFIX_NUM; + break; + case ELM_COLLECTION_TAG: + uiMaxTagNum = XFLM_MAX_COLLECTION_NUM; + break; + case ELM_ENCDEF_TAG: +#ifndef FLM_USE_NICI + rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); + goto Exit; +#else + uiMaxTagNum = XFLM_MAX_ENCDEF_NUM; + break; +#endif + default: + *puiDictType = 0; + goto Exit; + } + + if( RC_BAD( rc = pNode->hasAttributes( this, &bHasAttrs))) + { + goto Exit; + } + + if( !bHasAttrs) + { + goto Exit; + } + + if( pNode->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = pNode->getFirstAttribute( this, (IF_DOMNode **)&pAttr))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Cycle through the attributes, pulling out stuff we are interested + // in. + + for( ;;) + { + if( RC_BAD( rc = pAttr->getNameId( this, &uiNameId))) + { + goto Exit; + } + + switch (uiNameId) + { + case ATTR_NAME_TAG: + { + if (RC_BAD( rc = pAttr->getUnicode( this, &puzName))) + { + goto Exit; + } + + // Put a freeze on name if we are not deleting. + // Modify is allowed, but delete is not. + + if (!bDeleting) + { + if (RC_BAD( rc = pAttr->addModeFlags( + this, FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + break; + } + + case ATTR_TARGET_NAMESPACE_TAG: + { + if (RC_BAD( rc = pAttr->getUnicode( this, &puzNamespace))) + { + goto Exit; + } + break; + } + + case ATTR_TYPE_TAG: + { + // Put a freeze on data type if we are not deleting. + + if (!bDeleting) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + break; + } + + case ATTR_STATE_TAG: + { + if (RC_BAD( rc = pAttr->getUTF8( this, szTmpBuf, + sizeof( szTmpBuf), 0, ~((FLMUINT)0)))) + { + goto Exit; + } + + if (*puiDictType == ELM_INDEX_TAG) + { + if (RC_BAD( rc = fdictGetIndexState( + (char *)szTmpBuf, &uiState))) + { + goto Exit; + } + } + else if (*puiDictType == ELM_ELEMENT_TAG || + *puiDictType == ELM_ATTRIBUTE_TAG) + { + if (RC_BAD( rc = fdictGetState( + (char *)szTmpBuf, &uiState))) + { + goto Exit; + } + } + + // Put a freeze on state if we are not deleting. + + if (!bDeleting && + (*puiDictType == ELM_INDEX_TAG || + *puiDictType == ELM_ELEMENT_TAG || + *puiDictType == ELM_ATTRIBUTE_TAG)) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + bFoundState = TRUE; + break; + } + + case ATTR_STATE_CHANGE_COUNT_TAG: + { + // Put a freeze on state change count if we are not deleting. + + if (!bDeleting && + (*puiDictType == ELM_ELEMENT_TAG || + *puiDictType == ELM_ATTRIBUTE_TAG)) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + break; + } + + case ATTR_DICT_NUMBER_TAG: + { + if (RC_BAD( rc = pAttr->getUINT( this, puiDictNumber))) + { + goto Exit; + } + + // If we are deleting, no need to verify or alter + // dictionary number. + + if (bDeleting) + { + break; + } + + // If the set dictionary number was zero, allocate a new + // one, set it, and freeze it. + + if (!(*puiDictNumber)) + { + if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType, + puiDictNumber))) + { + goto Exit; + } + + // *puiDictNumber will be zero coming back if the + // dictionary type does not keep track of a next + // dictionary number (like for prefixes) + + if (*puiDictNumber) + { + if( RC_BAD( rc = pAttr->removeModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUINT( this, *puiDictNumber))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + } + } + else + { + // Set the next unused dictionary number for this type - but only + // if this number is >= the one already stored. + + if (RC_BAD( rc = m_pDict->setNextDictNum( this, *puiDictType, + *puiDictNumber))) + { + goto Exit; + } + } + break; + } + + default: + { + // Ignore all other attributes for now. + + break; + } + } + + if( RC_BAD( rc = pAttr->getNextSibling( this, (IF_DOMNode **)&pAttr))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + } + + // If dictionary number is missing, create one and add it in. + + if (!(*puiDictNumber) && !bDeleting) + { + + // Allocate the next unused dictionary number for this type. + + if (RC_BAD( rc = m_pDict->allocNextDictNum( this, *puiDictType, + puiDictNumber))) + { + goto Exit; + } + + // *puiDictNumber will be zero coming back if the + // dictionary type does not keep track of a next + // dictionary number for this type (like for prefixes) + + if (*puiDictNumber) + { + + // Create a dict number attribute, set it to the newly + // allocated value, and freeze it. + + if (RC_OK( rc = pNode->createAttribute( this, ATTR_DICT_NUMBER_TAG, + (IF_DOMNode **)&pAttr))) + { + if (RC_OK( rc = pAttr->setUINT( this, *puiDictNumber))) + { + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + } + } + } + + if (!bDeleting) + { + + // Must have a name specified, and it must be unique + + if (!puzName) + { + switch (*puiDictType) + { + case ELM_ELEMENT_TAG: + rc = RC_SET( NE_XFLM_MISSING_ELEMENT_NAME); + break; + case ELM_ATTRIBUTE_TAG: + rc = RC_SET( NE_XFLM_MISSING_ATTRIBUTE_NAME); + break; + case ELM_INDEX_TAG: + rc = RC_SET( NE_XFLM_MISSING_INDEX_NAME); + break; + case ELM_PREFIX_TAG: + rc = RC_SET( NE_XFLM_MISSING_PREFIX_NAME); + break; + case ELM_COLLECTION_TAG: + rc = RC_SET( NE_XFLM_MISSING_COLLECTION_NAME); + break; + case ELM_ENCDEF_TAG: + rc = RC_SET( NE_XFLM_MISSING_ENCDEF_NAME); + break; + default: + + // Should never hit this case! + + flmAssert( 0); + break; + } + goto Exit; // Will return NE_XFLM_OK + } + + // Verify name uniqueness + + key1.reset(); + if (RC_BAD( rc = key1.setUINT( 0, *puiDictType))) + { + goto Exit; + } + if (RC_BAD( rc = key1.setUnicode( 1, puzName))) + { + goto Exit; + } + if ((*puiDictType == ELM_ELEMENT_TAG || + *puiDictType == ELM_ATTRIBUTE_TAG) && + puzNamespace) + { + if (RC_BAD( rc = key1.setUnicode( 2, puzNamespace))) + { + goto Exit; + } + } + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, + &key1, XFLM_EXACT, &key2))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + // We should have found the thing! It should have + // already been indexed! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // See if there is another one after this for the same + // key. + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NAME_INDEX, + &key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS, + &key1))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + // See if it is defined in our name table and is a number in + // our reserved range. In that case, it should never be + // coming through here - it is a name conflict we should prevent. + + if ((pTagInfo = m_pDict->getNameTable()->findTagByTypeAndName( + *puiDictType, puzName, NULL, TRUE, + puzNamespace, &bAmbiguous, &uiInsertPos)) != NULL) + { + if (pTagInfo->uiTagNum > uiMaxTagNum) + { + goto Have_Name_Conflict; + } + } + } + else + { +Have_Name_Conflict: + + // We should NOT have found another one with this same name + + switch (*puiDictType) + { + case ELM_ELEMENT_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NAME); + goto Exit; + case ELM_ATTRIBUTE_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NAME); + goto Exit; + case ELM_INDEX_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NAME); + goto Exit; + case ELM_COLLECTION_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NAME); + goto Exit; + case ELM_PREFIX_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_PREFIX_NAME); + goto Exit; + default: + + // VISIT: Do we care on other dictionary types that + // we have a duplicate name? + + break; + } + } + + // If this is an attribute definition, and the name is "xmlns" or + // begins with "xmlns:", it cannot have a target namespace. + + if (*puiDictType == ELM_ATTRIBUTE_TAG && + puzNamespace && *puzNamespace && + isXMLNS( puzName) && + (puzName [5] == 0 || (puzName [5] == ':' && puzName [6]))) + { + rc = RC_SET( NE_XFLM_NAMESPACE_NOT_ALLOWED); + goto Exit; + } + } + + // Verify dictionary number uniqueness + + if (*puiDictNumber && !bDeleting) + { + key1.reset(); + key2.reset(); + if (RC_BAD( rc = key1.setUINT( 0, *puiDictType))) + { + goto Exit; + } + if (RC_BAD( rc = key1.setUINT( 1, *puiDictNumber))) + { + goto Exit; + } + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &key1, XFLM_EXACT, &key2))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + // We should have found the thing! It should have + // already been indexed! + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + + // See if there is another one after this for the same + // key. + + if (RC_BAD( rc = keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &key2, XFLM_EXCL | XFLM_KEY_EXACT | XFLM_MATCH_IDS, + &key1))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + + // We should NOT have found another one with this same number + + switch (*puiDictType) + { + case ELM_ELEMENT_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_ELEMENT_NUM); + goto Exit; + case ELM_ATTRIBUTE_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_ATTRIBUTE_NUM); + goto Exit; + case ELM_INDEX_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_INDEX_NUM); + goto Exit; + case ELM_COLLECTION_TAG: + rc = RC_SET( NE_XFLM_DUPLICATE_COLLECTION_NUM); + goto Exit; + default: + + // Dictionary number is not used for other types + // so we really don't care if there is a duplicate + // in these cases. + + break; + } + } + } + + // Is it a delete? + + if (bDeleting) + { + if (*puiDictType == ELM_ELEMENT_TAG) + { + if (*puiDictNumber) + { + // Make sure that elements are in the right state + // to be deleted. + + if( !m_bItemStateUpdOk) + { + rc = RC_SET( NE_XFLM_CANNOT_DEL_ELEMENT); + goto Exit; + } + } + } + else if (*puiDictType == ELM_ATTRIBUTE_TAG) + { + if (*puiDictNumber) + { + // Make sure that attributes are in the right state + // to be deleted. + + if( !m_bItemStateUpdOk) + { + rc = RC_SET( NE_XFLM_CANNOT_DEL_ATTRIBUTE); + goto Exit; + } + } + } + else if (*puiDictType == ELM_COLLECTION_TAG) + { + if (*puiDictNumber) + { + if (RC_BAD( rc = m_pDict->checkCollectionReferences( *puiDictNumber))) + { + goto Exit; + } + } + } + } + else + { + // Set a state attribute and freeze it if it was missing. + // This makes it so that the state can only be changed by a call + // to changeItemState. This routine ensures that the state + // change is legal. + + if( (*puiDictType == ELM_ATTRIBUTE_TAG || + *puiDictType == ELM_ELEMENT_TAG || + *puiDictType == ELM_ENCDEF_TAG) && !bFoundState) + { + if (RC_BAD( rc = pNode->createAttribute( + this, ATTR_STATE_TAG, (IF_DOMNode **)&pTmpNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pTmpNode->setUTF8( + this, (FLMBYTE *)XFLM_ACTIVE_OPTION_STR))) + { + goto Exit; + } + + if( RC_BAD( rc = pTmpNode->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pAttr) + { + pAttr->Release(); + } + + if (pTmpNode) + { + pTmpNode->Release(); + } + + if (puzName) + { + f_free( &puzName); + } + + if (puzNamespace) + { + f_free( &puzNamespace); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called when a dictionary document is done being + modified. It will check the definition and then generate any + dictionary updates that need to be done. +****************************************************************************/ +RCODE F_Db::dictDocumentDone( + FLMUINT64 ui64DocumentID, + FLMBOOL bDeleting, + FLMUINT * puiDictDefType) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDictType; + FLMUINT uiDictNumber; + + if (puiDictDefType) + { + *puiDictDefType = 0; + } + + // Document ID 1 is the one that is reserved for next element, next + // attribute, next index, and next collection. + + if (ui64DocumentID == XFLM_DICTINFO_DOC_ID) + { + goto Exit; + } + + // Flush any index keys before making any changes to dictionary items. + // Dictionary changes may change index definitions, etc. + + if( RC_BAD( rc = keysCommit( FALSE))) + { + goto Exit; + } + + // Clear out the cdl table in case it changes. + + krefCntrlFree(); + + // Retrieve the root element of the definition + + if( RC_BAD( rc = checkDictDefInfo( ui64DocumentID, bDeleting, + &uiDictType, &uiDictNumber))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + // Was the document one of our known dictionary document types? + // Also, had a dictionary number been defined. If bDeleting + // is TRUE and no dictionary number had been defined, there + // is nothing to be done on the internal dictionary. + + if (!uiDictType || !uiDictNumber) + { + goto Exit; // Will return NE_XFLM_OK + } + + // Create a separate dictionary object if one has not already + // been created. + + if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = dictClone())) + { + goto Exit; + } + } + + // Update the dictionary and create/drop indexes or containers + // if it is that type of definition + + if (RC_BAD( rc = m_pDict->updateDict( this, uiDictType, ui64DocumentID, + uiDictNumber, FALSE, bDeleting))) + { + goto Exit; + } + + // Return the type of definition + + if( puiDictDefType) + { + *puiDictDefType = uiDictType; + } + +Exit: + + if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + return( rc ); +} + +/**************************************************************************** +Desc: Copies an existing dictionary to a new dictionary. +****************************************************************************/ +RCODE F_Db::dictClone( void) +{ + RCODE rc = NE_XFLM_OK; + F_Dict * pNewDict = NULL; + + // Allocate a new FDICT structure + + if ((pNewDict = f_new F_Dict) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Nothing to do is not a legal state. + + if (!m_pDict) + { + flmAssert( 0); + m_pDict = pNewDict; + goto Exit; + } + + // Copy the dictionary. + + if (RC_BAD( rc = pNewDict->cloneDict( m_pDict))) + { + goto Exit; + } + + m_pDatabase->lockMutex(); + unlinkFromDict(); + m_pDatabase->unlockMutex(); + m_pDict = pNewDict; + pNewDict = NULL; + m_uiFlags |= FDB_UPDATED_DICTIONARY; + +Exit: + + if (RC_BAD( rc) && pNewDict) + { + pNewDict->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Build an index. +****************************************************************************/ +RCODE F_Db::buildIndex( + FLMUINT uiIndexNum, + FLMUINT uiState) +{ + RCODE rc = NE_XFLM_OK; + LFILE * pIxLFile; + IXD * pIxd; + + // Flush any KY keys and free the tables because they may grow! + + if (RC_BAD( rc = keysCommit( TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = krefCntrlCheck())) + { + goto Exit; + } + + if (RC_BAD(rc = m_pDict->getIndex( uiIndexNum, &pIxLFile, &pIxd, TRUE))) + { + goto Exit; + } + + // NON-BLOCKING INDEX BUILD - NOTE: The IXD_SUSPENDED flag may + // also be set, which indicates that we should NOT start the + // background maintenance thread right now. + + if (uiState & IXD_OFFLINE) + { + if (RC_BAD( rc = setIxStateInfo( pIxd->uiIndexNum, 0, uiState))) + { + goto Exit; + } + + // setIxStateInfo may have changed to a new dictionary, so pIxd is no + // good after this point + + pIxd = NULL; + + // Don't schedule a maintenance thread if index is to start + // out life in a suspended state, or if we are replaying + // the roll-forward log. + + if (!(uiState & IXD_SUSPENDED) && !(m_uiFlags & FDB_REPLAYING_RFL)) + { + if (RC_BAD( rc = addToStartList( uiIndexNum))) + { + goto Exit; + } + } + + // Done + + goto Exit; + } + + // There may be "new" nodes in the node cache. + // Need to flush them to the database so that + // the B-Tree lookups done by the indexing code will + // work correctly + + if( RC_BAD( rc = flushDirtyNodes())) + { + goto Exit; + } + + // NORMAL INDEX BUILD - BLOCKING. uiIndexToBeUpdated better be + // zero at this point since we are not working in the background. + + if (RC_BAD( rc = indexSetOfDocuments( uiIndexNum, 1, + ~((FLMUINT64)0), m_pIxStatus, m_pIxClient, NULL, NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Logs information about an index being built +****************************************************************************/ +void flmLogIndexingProgress( + FLMUINT uiIndexNum, + FLMUINT64 ui64LastDocumentId) +{ + IF_LogMessageClient * pLogMsg = NULL; + char szMsg[ 128]; + + if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) + { + pLogMsg->changeColor( XFLM_YELLOW, XFLM_BLACK); + if (ui64LastDocumentId) + { + f_sprintf( (char *)szMsg, + "Indexing progress: Index %u is offline. Last document processed = %I64u.", + (unsigned)uiIndexNum, ui64LastDocumentId); + } + else + { + f_sprintf( (char *)szMsg, + "Indexing progress: Index %u is online.", + (unsigned)uiIndexNum); + } + pLogMsg->appendString( szMsg); + } + flmEndLogMessage( &pLogMsg); +} + +/**************************************************************************** +Desc: Unlink and free an IX_CONTEXT structure. +****************************************************************************/ +FSTATIC void kyFreeIxContext( + IXD * pIxd, + IX_CONTEXT * pIxContext, + IX_CONTEXT ** ppIxContextList + ) +{ + if (pIxContext->pPrev) + { + pIxContext->pPrev->pNext = pIxContext->pNext; + } + else + { + *ppIxContextList = pIxContext->pNext; + } + if (pIxContext->pNext) + { + pIxContext->pNext->pPrev = pIxContext->pPrev; + } + kyReleaseCdls( pIxd, pIxContext->pCdlTbl); + if (pIxContext->pPool) + { + pIxContext->pPool->poolFree(); + pIxContext->pPool->Release(); + } + f_free( &pIxContext); +} + +/**************************************************************************** +Desc: Output the keys for a particular IX_CONTEXT structure. Also, free + the IX_CONTEXT structure and its associated CDLs, etc. +****************************************************************************/ +RCODE F_Db::outputContextKeys( + FLMUINT64 ui64DocumentId, + IXD * pIxd, + IX_CONTEXT * pIxContext, + IX_CONTEXT ** ppIxContextList) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd, + pIxContext->pCdlTbl, TRUE, TRUE))) + { + goto Exit; + } + + // Free the IX_CONTEXT structure - unlinks from list too. + + kyFreeIxContext( pIxd, pIxContext, ppIxContextList); + + // Flush keys if over threshhold - get key count before flushing. + + if( pIxd->uiIndexNum && isKrefOverThreshold()) + { + processDupKeys( pIxd); + if (RC_BAD( rc = keysCommit( FALSE, FALSE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Output the keys for a particular IX_CONTEXT structure and remove CDLs + for the specified ICD, if any. +****************************************************************************/ +RCODE F_Db::removeCdls( + FLMUINT64 ui64DocumentId, + IXD * pIxd, + IX_CONTEXT * pIxContext, + ICD * pRefIcd) +{ + RCODE rc = NE_XFLM_OK; + CDL * pCdl; + CDL * pOldCdlList; + ICD * pIcd; + + if (RC_BAD( rc = buildKeys( ui64DocumentId, pIxd, + pIxContext->pCdlTbl, TRUE, TRUE))) + { + goto Exit; + } + + pIcd = pRefIcd; + while (pIcd) + { + + // Free the CDLs for the specified ICD, if any. + // Put the CDLs into a table for reuse. + + pCdl = pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList; + pIxContext->pCdlTbl [pIcd->uiCdl].pCdlList = NULL; + if (pCdl) + { + pOldCdlList = pIxContext->pCdlList; + pIxContext->pCdlList = pCdl; + for (;;) + { + if (pCdl->pNode) + { + pCdl->pNode->Release(); + pCdl->pNode = NULL; + } + if (!pCdl->pNext) + { + pCdl->pNext = pOldCdlList; + break; + } + pCdl = pCdl->pNext; + } + } + if (pIcd == pRefIcd->pParent) + { + break; + } + if ((pIcd = pIcd->pNextSibling) == NULL) + { + + // Also do reference ICD's parent ICD. This is not absolutely + // necessary, since this CDLs will be ignored when we verify + // context, but we might as well do it to save just a little more + // memory space. + + if ((pIcd = pRefIcd->pParent) == NULL) + { + break; + } + } + } + + // Flush keys if over threshhold - get key count before flushing. + + if( pIxd->uiIndexNum && isKrefOverThreshold()) + { + processDupKeys( pIxd); + if (RC_BAD( rc = keysCommit( FALSE, FALSE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Index a particular document for a particular index. +****************************************************************************/ +RCODE F_Db::indexDocument( + IXD * pIxd, + F_DOMNode * pDocNode) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd; + ICD * pChildIcd; + CDL * pCdl; + IX_CONTEXT * pIxContextList = NULL; + IX_CONTEXT * pIxContext; + F_DOMNode * pTmpNode = NULL; + F_TRAV * pTrav = NULL; + void * pvMark = m_TempPool.poolMark(); + CDL_HDR * pCdlHdr; + FLMUINT64 ui64DocId; + + // Root of a document cannot be an attribute node + + flmAssert( pDocNode->getNodeType() != ATTRIBUTE_NODE); + + // Get the document ID + + if( RC_BAD( rc = pDocNode->getNodeId( this, &ui64DocId))) + { + goto Exit; + } + + // If the index number is zero, we are generating keys for a query + // but not to store in the database. We want the kref table cleared + // out. + + if (!pIxd->uiIndexNum) + { + if (!m_bKrefSetup) + { + if (RC_BAD( rc = krefCntrlCheck())) + { + goto Exit; + } + } + else if (m_eTransType == XFLM_UPDATE_TRANS) + { + if (RC_BAD( rc = keysCommit( FALSE))) + { + goto Exit; + } + } + else + { + // Empty the table out so that the only keys added in this + // call are for this index, this document. + + m_pKrefPool->poolReset( NULL, TRUE); + m_uiKrefCount = 0; + m_uiTotalKrefBytes = 0; + } + flmAssert( !m_uiKrefCount && !m_uiTotalKrefBytes); + } + else + { + if (RC_BAD( rc = krefCntrlCheck())) + { + goto Exit; + } + } + + // Do an in-order traversal of the document. + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( F_TRAV), (void **)&pTrav))) + { + goto Exit; + } + pTrav->pNode = pDocNode; + pTrav->pNode->AddRef(); + // pTrav->pRefIcd = NULL; // Set by poolCalloc + // pTrav->pIcd = NULL; // Set by poolCalloc + // pTrav->pIxContext = NULL; // Set by poolCalloc + // pTrav->pParent = NULL; // Set by poolCalloc + // pTrav->pChild = NULL; // Set by poolCalloc + + for (;;) + { + FLMUINT uiNameId; + eDomNodeType eNodeType = pTrav->pNode->getNodeType(); + + if( RC_BAD( rc = pTrav->pNode->getNameId( this, &uiNameId))) + { + goto Exit; + } + + if (uiNameId && + (eNodeType == ELEMENT_NODE || eNodeType == ATTRIBUTE_NODE)) + { + FLMBOOL bCheckedIcdTreeRoot = FALSE; + + // See if the node has an ICD in the current context. + + if ((pIcd = pTrav->pRefIcd) == NULL) + { + pIcd = pIxd->pIcdTree; + bCheckedIcdTreeRoot = TRUE; + } + if (pIcd->uiDictNum == ELM_ROOT_TAG) + { + if (pTrav->pNode->getParentId()) + { + pIcd = NULL; + } + } + else if (eNodeType == ELEMENT_NODE) + { + while (pIcd && (pIcd->uiDictNum != uiNameId || + (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + + // If we did not start at the root of the ICD tree, + // need to go back there to see if we need to start + // a new context. However, if the root if the ICD tree + // is ELM_ROOT_TAG, it is pointless, because the current + // pTrev->pNode should be a child of some node if + // bCheckedIcdTreeRoot is FALSE. + + if (!pIcd && !bCheckedIcdTreeRoot && + pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG) + { + pIcd = pIxd->pIcdTree; + while (pIcd && (pIcd->uiDictNum != uiNameId || + (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + if (pIcd) + { + + // Reset these so that a new context will be + // created below. + + pTrav->pRefIcd = NULL; + pTrav->pIxContext = NULL; + } + } + } + else + { + while (pIcd && (pIcd->uiDictNum != uiNameId || + !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + + // If we did not start at the root of the ICD tree, + // need to go back there to see if we need to start + // a new context. However, if the root if the ICD tree + // is ELM_ROOT_TAG, it is pointless, because the current + // pTrev->pNode should be a child of some node if + // bCheckedIcdTreeRoot is FALSE. + + if (!pIcd && !bCheckedIcdTreeRoot && + pIxd->pIcdTree->uiDictNum != ELM_ROOT_TAG) + { + pIcd = pIxd->pIcdTree; + while (pIcd && (pIcd->uiDictNum != uiNameId || + !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + if (pIcd) + { + + // Reset these so that a new context will be + // created below. + + pTrav->pRefIcd = NULL; + pTrav->pIxContext = NULL; + } + } + } + + // If we found a matching ICD, see if we want to save the node + // in a CDL table. + + if ((pTrav->pIcd = pIcd) != NULL) + { + + // If there is no indexing context, start one. + + if (!pTrav->pIxContext) + { + flmAssert( !pTrav->pRefIcd); + if (RC_BAD( rc = f_calloc( sizeof( IX_CONTEXT), &pIxContext))) + { + goto Exit; + } + if ((pIxContext->pNext = pIxContextList) != NULL) + { + pIxContextList->pPrev = pIxContext; + } + pIxContextList = pIxContext; + + if ((pIxContext->pPool = f_new F_Pool) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + pIxContext->pPool->poolInit( 512); + + if (RC_BAD( rc = pIxContext->pPool->poolCalloc( + sizeof( CDL_HDR) * pIxd->uiNumIcds, + (void **)&pIxContext->pCdlTbl))) + { + goto Exit; + } + pTrav->pIxContext = pIxContext; + pTrav->pRefIcd = pIxd->pIcdTree; + } + else + { + pIxContext = pTrav->pIxContext; + } + + // If this node has a parent, and the front of the + // list is a "missing" place holder for the node, + // replace that CDL. + + pCdlHdr = &pIxContext->pCdlTbl [pIcd->uiCdl]; + pCdl = pCdlHdr->pCdlList; + if (pCdl && !pCdl->pNode && + pTrav->pNode->getParentId() && + pCdl->ui64ParentId == pTrav->pNode->getParentId()) + { + pCdl->pNode = pTrav->pNode; + pCdl->bInNodeSubtree = TRUE; + pCdl->pNode->AddRef(); + } + else + { + + // Reuse a CDL if one is available. + + if (pIxContext->pCdlList) + { + pCdl = pIxContext->pCdlList; + pIxContext->pCdlList = pCdl->pNext; + + // pCdl->pNode should have been released when it was + // put into the CDL list! + + flmAssert( !pCdl->pNode); + } + else + { + if (RC_BAD( rc = pIxContext->pPool->poolAlloc( + sizeof( CDL), (void **)&pCdl))) + { + goto Exit; + } + } + pCdl->pNode = pTrav->pNode; + pCdl->ui64ParentId = pTrav->pNode->getParentId(); + pCdl->bInNodeSubtree = TRUE; + pCdl->pNode->AddRef(); + pCdl->pNext = pCdlHdr->pCdlList; + pCdlHdr->pCdlList = pCdl; + } + + // Add "missing" place-holders for any child ICDs + + pChildIcd = pIcd->pFirstChild; + while (pChildIcd) + { + + // Reuse a CDL if one is available. + + if (pIxContext->pCdlList) + { + pCdl = pIxContext->pCdlList; + pIxContext->pCdlList = pCdl->pNext; + + // pCdl->pNode should have been released when it was + // put into the CDL list! + + flmAssert( !pCdl->pNode); + } + else + { + if (RC_BAD( rc = pIxContext->pPool->poolAlloc( + sizeof( CDL), (void **)&pCdl))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pTrav->pNode->getNodeId( + this, &pCdl->ui64ParentId))) + { + goto Exit; + } + + pCdl->pNode = NULL; + pCdl->bInNodeSubtree = TRUE; + pCdlHdr = &pIxContext->pCdlTbl [pChildIcd->uiCdl]; + pCdl->pNext = pCdlHdr->pCdlList; + pCdlHdr->pCdlList = pCdl; + pChildIcd = pChildIcd->pNextSibling; + } + } + } + + // Go to the next node. + + if( eNodeType == ATTRIBUTE_NODE) + { + if (RC_OK( rc = pTrav->pNode->getNextSibling( this, + (IF_DOMNode **)&pTrav->pNode))) + { + pTrav->pIcd = NULL; + continue; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + + // No siblings, go back to parent node and see if it has any + // siblings. + + if (pTrav->pNode) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + } + + // If the parent has a different IX_CONTEXT (including NULL) + // free this one before going back to the parent. + + if (pTrav->pIxContext && pTrav->pParent && + pTrav->pParent->pIxContext != pTrav->pIxContext) + { + if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, + pTrav->pIxContext, &pIxContextList))) + { + goto Exit; + } + pTrav->pIxContext = NULL; + pTrav->pRefIcd = NULL; + } + else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH) + { + if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext, + pTrav->pRefIcd))) + { + goto Exit; + } + } + + pTrav = pTrav->pParent; + + // Has to be a parent at this point! + + flmAssert( pTrav); + + // Fall through to do element's siblings. + } + else if (eNodeType == ELEMENT_NODE || eNodeType == DOCUMENT_NODE) + { + if (RC_OK( rc = pTrav->pNode->getFirstChild( this, (IF_DOMNode **)&pTmpNode))) + { +Setup_Child: + if (!pTrav->pChild) + { + F_TRAV * pNewTrav; + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( F_TRAV), + (void **)&pNewTrav))) + { + goto Exit; + } + pNewTrav->pParent = pTrav; + pTrav->pChild = pNewTrav; + } + pTrav = pTrav->pChild; + if (pTrav->pNode) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + } + pTrav->pNode = pTmpNode; + pTrav->pNode->AddRef(); + pTmpNode->Release(); + pTmpNode = NULL; + pTrav->pRefIcd = (pTrav->pParent->pIcd + ? pTrav->pParent->pIcd->pFirstChild + : NULL); + pTrav->pIcd = NULL; + pTrav->pIxContext = pTrav->pParent->pIxContext; + if (!pTrav->pRefIcd) + { + pTrav->pIxContext = NULL; + } + continue; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + + // See if the node has any attributes + + if( pTrav->pNode->getNodeType() == ELEMENT_NODE) + { + if (RC_OK( rc = pTrav->pNode->getFirstAttribute( this, + (IF_DOMNode **)&pTmpNode))) + { + goto Setup_Child; + } + else if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + } + + // Follow sibling chain. Go to parents until we find a + // parent that has a sibling. + + for (;;) + { + // If the parent has a different IX_CONTEXT + // free this one before going back to the parent. + + if (pTrav->pIxContext && pTrav->pParent && pTrav->pParent->pIxContext && + pTrav->pParent->pIxContext != pTrav->pIxContext) + { + if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, + pTrav->pIxContext, &pIxContextList))) + { + goto Exit; + } + pTrav->pRefIcd = (pTrav->pParent->pIcd + ? pTrav->pParent->pIcd->pFirstChild + : NULL); + pTrav->pIcd = NULL; + pTrav->pIxContext = pTrav->pParent->pIxContext; + if (!pTrav->pRefIcd) + { + pTrav->pIxContext = NULL; + } + } + if (RC_BAD( rc = pTrav->pNode->getNextSibling( this, (IF_DOMNode **)&pTrav->pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // If there is no parent node, we are done. + + if (!pTrav->pParent) + { + goto Done_With_Document; + } + + // See if the parent has a a first attribute. + + if( pTrav->pParent->pNode->getNodeType() == ELEMENT_NODE) + { + if (RC_BAD( rc = pTrav->pParent->pNode->getFirstAttribute( this, + (IF_DOMNode **)&pTrav->pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + + // Will continue processing attributes as siblings. + + break; + } + } + + // At this point we will be going back to the parent node. + + if (pTrav->pNode) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + } + + // If the parent has no context, we need to output the keys we may + // have collected so far. NOTE: If parent's context is non-NULL and + // just different than our context, we will already have output the + // keys above. pTrav->pParent must be non-NULL at this point. + + if (pTrav->pIxContext && !pTrav->pParent->pIxContext) + { + if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, + pTrav->pIxContext, &pIxContextList))) + { + goto Exit; + } + } + else if (pTrav->pIxContext && pIxd->uiFlags & IXD_SINGLE_PATH) + { + if (RC_BAD( rc = removeCdls( ui64DocId, pIxd, pTrav->pIxContext, + pTrav->pRefIcd))) + { + goto Exit; + } + } + + pTrav = pTrav->pParent; + } + else if (pTrav->pNode->getNodeType() == ELEMENT_NODE) + { + pTrav->pIcd = NULL; + break; + } + else + { + + // better not be in a list of attribute nodes at this point! + + flmAssert( pTrav->pNode->getNodeType() != ATTRIBUTE_NODE); + } + } + } + +Done_With_Document: + + // Need to build keys for each index context that we have. + + while (pIxContextList) + { + if (RC_BAD( rc = outputContextKeys( ui64DocId, pIxd, + pIxContextList, &pIxContextList))) + { + goto Exit; + } + } + + // Flush keys - get key count before flushing. + + if (!pIxd->uiIndexNum) + { + processDupKeys( pIxd); + } + else + { + if( isKrefOverThreshold()) + { + processDupKeys( pIxd); + if (RC_BAD( rc = keysCommit( FALSE, FALSE))) + { + goto Exit; + } + } + } + +Exit: + + if (pTmpNode) + { + pTmpNode->Release(); + } + + while (pTrav && pTrav->pParent) + { + pTrav = pTrav->pParent; + } + while (pTrav) + { + if (pTrav->pNode) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + } + pTrav = pTrav->pChild; + } + + while (pIxContextList) + { + kyFreeIxContext( pIxd, pIxContextList, &pIxContextList); + } + + m_TempPool.poolReset( pvMark); + return( rc); +} + +/**************************************************************************** +Desc: Index a set of documents or until time runs out. +****************************************************************************/ +RCODE F_Db::indexSetOfDocuments( + FLMUINT uiIxNum, + FLMUINT64 ui64StartDocumentId, + FLMUINT64 ui64EndDocumentId, + IF_IxStatus * ifpIxStatus, + IF_IxClient * ifpIxClient, + XFLM_INDEX_STATUS * pIndexStatus, + FLMBOOL * pbHitEnd, + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64DocumentId; + FLMUINT64 ui64LastDocumentId = 0; + IXD * pIxd = NULL; + F_COLLECTION * pCollection; + ServerLockObject * + pDatabaseLockObj = m_pDatabase->m_pDatabaseLockObj; + FLMBOOL bHitEnd = FALSE; + FLMUINT uiCurrTime; + FLMUINT uiLastStatusTime = 0; + FLMUINT uiStartTime; + FLMUINT uiMinTU; + FLMUINT uiStatusIntervalTU; + FLMUINT64 ui64DocumentsProcessed = 0; + FLMBOOL bUpdateTracker = FALSE; + FLMBOOL bRelinquish = FALSE; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + void * pvTmpPoolMark = m_TempPool.poolMark(); + F_Btree * pbtree = NULL; + FLMBOOL bNeg; + FLMUINT uiBytesProcessed; + F_DOMNode * pNode = NULL; + + FLM_MILLI_TO_TIMER_UNITS( 500, uiMinTU); // 1/2 second + FLM_SECS_TO_TIMER_UNITS( 10, uiStatusIntervalTU); // 10 seconds + uiStartTime = FLM_GET_TIMER(); + + if (RC_BAD( rc = krefCntrlCheck())) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDict->getIndex( uiIxNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + flmAssert( !(pIxd->uiFlags & IXD_SUSPENDED)); + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDict->getCollection( + pIxd->uiCollectionNum, &pCollection))) + { + goto Exit; + } + + if (RC_BAD( rc = pbtree->btOpen( this, &pCollection->lfInfo, + FALSE, TRUE))) + { + goto Exit; + } + + uiKeyLen = sizeof( ucKey); + if (RC_BAD( rc = flmNumber64ToStorage( ui64StartDocumentId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + if( RC_BAD( rc = pbtree->btLocateEntry( + ucKey, sizeof( ucKey), &uiKeyLen, XFLM_INCL))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + bHitEnd = TRUE; + goto Commit_Keys; + } + + goto Exit; + } + + // Make sure we hit a root node. If not, continue reading until we do + // or until we hit the end. Root nodes are always linked together in + // ascending order, so if there is another document, we will find it + // simply by searching forward from where we are. Then we can follow + // document links. + + for (;;) + { + if (RC_BAD( rc = flmCollation2Number( uiKeyLen, ucKey, + &ui64DocumentId, &bNeg, &uiBytesProcessed))) + { + goto Exit; + } + + if (RC_BAD( rc = getNode( pIxd->uiCollectionNum, ui64DocumentId, + &pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + + // Better be able to find the node at this point! + + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + // If the node is a root node, we have a document we can + // process. + + if (pNode->isRootNode()) + { + + // This is a root node - has no parent and is not linked + // into orphan list. + + break; + } + + // Need to go to the next node. + + if (RC_BAD( rc = pbtree->btNextEntry( ucKey, uiKeyLen, &uiKeyLen))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + bHitEnd = TRUE; + goto Commit_Keys; + } + goto Exit; + } + } + + for (;;) + { + if( RC_BAD( rc = pNode->getNodeId( this, &ui64DocumentId))) + { + goto Exit; + } + + if (ui64DocumentId > ui64EndDocumentId) + { + break; + } + + if (RC_BAD( rc = indexDocument( pIxd, pNode))) + { + goto Exit; + } + + // See if there is an indexing callback + + if (ifpIxClient) + { + if (RC_BAD( rc = ifpIxClient->doIndexing( this, uiIxNum, + pIxd->uiCollectionNum, pNode))) + { + goto Exit; + } + } + + ui64LastDocumentId = ui64DocumentId; + ui64DocumentsProcessed++; + + if (pIndexStatus) + { + pIndexStatus->ui64DocumentsProcessed++; + pIndexStatus->ui64LastDocumentIndexed = ui64LastDocumentId; + } + + // Get the current time + + uiCurrTime = FLM_GET_TIMER(); + + // Break out if someone is waiting for an update transaction. + + if (pThread) + { + if (pThread->getShutdownFlag()) + { + bRelinquish = TRUE; + break; + } + + if (pDatabaseLockObj->ThreadWaitingLock()) + { + // See if our minimum run time has elapsed + + if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) >= uiMinTU) + { + if (ui64DocumentsProcessed < 50) + { + // If there are higher priority waiters in the lock queue, + // we want to relinquish. + + if (pDatabaseLockObj->haveHigherPriorityWaiter( + FLM_BACKGROUND_LOCK_PRIORITY)) + { + bRelinquish = TRUE; + break; + } + } + else + { + bRelinquish = TRUE; + break; + } + } + } + else + { + + // Even if no one has requested a lock for a long time, we + // still want to periodically commit our transaction so + // we won't lose more than uiMaxCPInterval timer units worth + // of work if we crash. We will run until we exceed the checkpoint + // interval and we see that someone (the checkpoint thread) is + // waiting for the write lock. + + if (FLM_ELAPSED_TIME( uiCurrTime, uiStartTime) > + gv_XFlmSysData.uiMaxCPInterval && + m_pDatabase->m_pWriteLockObj->ThreadWaitingLock()) + { + bRelinquish = TRUE; + break; + } + } + } + + if (FLM_ELAPSED_TIME( uiCurrTime, uiLastStatusTime) >= + uiStatusIntervalTU) + { + uiLastStatusTime = uiCurrTime; + if( ifpIxStatus) + { + if( RC_BAD( rc = ifpIxStatus->reportIndex( ui64LastDocumentId))) + { + goto Exit; + } + } + + // Send indexing completed event notification + + if( gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) + { + flmDoEventCallback( XFLM_EVENT_UPDATES, + XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), + 0, uiIxNum, ui64LastDocumentId, + NE_XFLM_OK); + } + + // Log a progress message + + flmLogIndexingProgress( uiIxNum, ui64LastDocumentId); + } + + // Need to go to the next document. + + if (RC_BAD( rc = pNode->getNextDocument( this, (IF_DOMNode **)&pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + bHitEnd = TRUE; + break; + } + } + } + +Commit_Keys: + + if (RC_BAD( rc = keysCommit( TRUE))) + { + goto Exit; + } + + // If at the end, change trans ID to the current transaction. + + if (bHitEnd) + { + if (RC_BAD( rc = setIxStateInfo( uiIxNum, ~((FLMUINT64)0), 0))) + { + goto Exit; + } + + // setIxStateInfo may have changed to a new dictionary, so pIxd is no + // good after this point + + pIxd = NULL; + } + else if (ui64DocumentsProcessed || bUpdateTracker) + { + if (RC_BAD( rc = setIxStateInfo( uiIxNum, ui64LastDocumentId, + IXD_OFFLINE))) + { + goto Exit; + } + + // setIxStateInfo may have changed to a new dictionary, so pIxd is no + // good after this point + + pIxd = NULL; + } + +Exit: + + // We want to make one last call if we are in the foreground or if + // we actually did some indexing. + + if (gv_XFlmSysData.EventHdrs[ XFLM_EVENT_UPDATES].pEventCBList) + { + flmDoEventCallback( XFLM_EVENT_UPDATES, + XFLM_EVENT_INDEXING_PROGRESS, this, f_threadId(), + 0, uiIxNum, + (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId), + NE_XFLM_OK); + } + + flmLogIndexingProgress( uiIxNum, + (FLMUINT64)(bHitEnd ? (FLMUINT64)0 : ui64LastDocumentId)); + + if (ifpIxStatus) + { + (void) ifpIxStatus->reportIndex( ui64LastDocumentId); + } + + if (pbHitEnd) + { + *pbHitEnd = bHitEnd; + } + + krefCntrlFree(); + m_TempPool.poolReset( pvTmpPoolMark); + + if (pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Set information in the tracker record for the index. +****************************************************************************/ +RCODE F_Db::setIxStateInfo( + FLMUINT uiIndexNum, + FLMUINT64 ui64LastDocIndexed, + FLMUINT uiState) +{ + RCODE rc = NE_XFLM_OK; + IXD_FIXUP * pIxdFixup; + IXD * pIxd; + F_DOMNode * pAttr = NULL; + F_DOMNode * pElement = NULL; + FLMBOOL bMustAbortOnError = FALSE; + + // Get the IXD - even if the index is offline. + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + // See if this index is in our fixup list. + + pIxdFixup = m_pIxdFixups; + while (pIxdFixup && pIxdFixup->uiIndexNum != uiIndexNum) + { + pIxdFixup = pIxdFixup->pNext; + } + + if (!pIxdFixup) + { + if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( IXD_FIXUP), &pIxdFixup))) + { + goto Exit; + } + pIxdFixup->pNext = m_pIxdFixups; + m_pIxdFixups = pIxdFixup; + pIxdFixup->uiIndexNum = uiIndexNum; + pIxdFixup->ui64LastDocIndexed = pIxd->ui64LastDocIndexed; + } + + bMustAbortOnError = TRUE; + + // Update the last node indexed, if it changed. + + if (pIxdFixup->ui64LastDocIndexed != ui64LastDocIndexed) + { + pIxdFixup->ui64LastDocIndexed = ui64LastDocIndexed; + + // First, retrieve the root element of the index definition. + + if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId, + (F_DOMNode **)&pElement))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // No need to create a new node if we are just setting it + // to the default value - see fdict.cpp, getIndexDef for where + // the default value gets set. + + if (ui64LastDocIndexed != ~((FLMUINT64)0)) + { + + // Create a new dictionary - so that we can set the + // ui64LastDocIndexedNodeId on the IXD. If the transaction + // aborts, the whole dictionary will go away. + + if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = dictClone())) + { + goto Exit; + } + + // Get a pointer to the new IXD + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + } + + // Create a new attribute node on the index definition to hold + // the last node indexed value. + + if (RC_BAD( rc = pElement->createAttribute( this, + ATTR_LAST_DOC_INDEXED_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pElement->getAttribute( this, + ATTR_LAST_DOC_INDEXED_TAG, (IF_DOMNode **)&pAttr))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + } + + if( pAttr) + { + if (RC_BAD( rc = pAttr->setUINT64( this, ui64LastDocIndexed))) + { + goto Exit; + } + } + } + + // If IXD_SUSPENDED is set, then IXD_OFFLINE must also be set. + // There are places in the code that only check for IXD_OFFLINE + // that don't care if the index is also suspended. + + if (uiState & IXD_SUSPENDED) + { + uiState = IXD_SUSPENDED | IXD_OFFLINE; + } + else if (uiState & IXD_OFFLINE) + { + uiState = IXD_OFFLINE; + } + else + { + uiState = 0; + } + + // See if we need to change state. + + if ((pIxd->uiFlags & (IXD_SUSPENDED | IXD_OFFLINE)) != uiState) + { + const char * pszStateStr; + + if (uiState & IXD_SUSPENDED) + { + pszStateStr = XFLM_INDEX_SUSPENDED_STR; + } + else if (uiState & IXD_OFFLINE) + { + pszStateStr = XFLM_INDEX_OFFLINE_STR; + } + else + { + pszStateStr = XFLM_INDEX_ONLINE_STR; + } + + // At this point we know we need to change the state. That means we need + // to create a new dictionary, if we have not already done so. + + if (!(m_uiFlags & FDB_UPDATED_DICTIONARY)) + { + if (RC_BAD( rc = dictClone())) + { + goto Exit; + } + + // Get a pointer to the new IXD + + if (RC_BAD( rc = m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + } + + // pElement may have been fetched above. Don't need to get it + // here if that is the case. + + if (!pElement) + { + + // First, retrieve the root element of the index definition. + + if( RC_BAD( rc = getNode( XFLM_DICT_COLLECTION, pIxd->ui64IxDefNodeId, + (F_DOMNode **)&pElement))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + } + + // Create a new attribute node on the index definition to hold + // the last node indexed value. + + if (RC_BAD( rc = pElement->createAttribute( this, + ATTR_STATE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + // May need to unfreeze the state to change it. + + if( RC_BAD( rc = pAttr->removeModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setUTF8( this, (FLMBYTE *)pszStateStr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( this, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Put the state into the IXD. + + pIxd->uiFlags = (pIxd->uiFlags & (~(IXD_SUSPENDED | IXD_OFFLINE))) | + uiState; + } + +Exit: + + if (pAttr) + { + pAttr->Release(); + } + + if (pElement) + { + pElement->Release(); + } + + if( RC_BAD( rc) && bMustAbortOnError) + { + setMustAbortTrans( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: See if any IXD structures need indexing in the background. +****************************************************************************/ +RCODE F_Db::startBackgroundIndexing( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiIndexNum; + IXD * pIxd; + + if (RC_BAD( rc = checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + if (m_eTransType != XFLM_NO_TRANS) + { + if (!okToCommitTrans()) + { + rc = RC_SET( NE_XFLM_ABORT_TRANS); + goto Exit; + } + } + else + { + + // Need to have at least a read transaction going. + + if (RC_BAD( rc = beginTrans( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + if (m_pDict->getIndexCount( FALSE)) + { + uiIndexNum = 0; + for (;;) + { + if ((pIxd = m_pDict->getNextIndex( uiIndexNum, FALSE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + + // Restart any indexes that are off-line but not suspended + + if ((pIxd->uiFlags & (IXD_OFFLINE | IXD_SUSPENDED)) == IXD_OFFLINE) + { + flmAssert( flmBackgroundIndexGet( m_pDatabase, + uiIndexNum, FALSE) == NULL); + + if (RC_BAD( rc = startIndexBuild( uiIndexNum))) + { + goto Exit; + } + } + } + } + +Exit: + + if (bStartedTrans) + { + (void)abortTrans(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Check and set the next dictionary number for a specific dictionary type. +****************************************************************************/ +RCODE F_Db::setNextDictNum( + FLMUINT uiDictType, + FLMUINT uiDictNumber + ) +{ + RCODE rc = NE_XFLM_OK; + + // Make sure an update transaction is active + + if (m_eTransType == XFLM_NO_TRANS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NO_TRANS_ACTIVE); + goto Exit; + } + + if (m_eTransType == XFLM_READ_TRANS) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_TRANS_OP); + goto Exit; + } + + // See if the transaction needs to be aborted + + if (RC_BAD( rc = m_AbortRc)) + { + goto Exit; + } + + // The number must be greater than 1 + + if (uiDictNumber < 2) + { + goto Exit; + } + + // Set the next dictionary number + + if (RC_BAD( rc = m_pDict->setNextDictNum( this, uiDictType, uiDictNumber))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_Database::startMaintThread( void) +{ + RCODE rc = NE_XFLM_OK; + char szThreadName[ F_PATH_MAX_SIZE]; + char szBaseName[ 32]; + + flmAssert( !m_pMaintThrd); + flmAssert( m_hMaintSem == F_SEM_NULL); + + // Generate the thread name + + if( RC_BAD( rc = gv_pFileSystem->pathReduce( + m_pszDbPath, szThreadName, szBaseName))) + { + goto Exit; + } + + f_sprintf( (char *)szThreadName, "Maintenance (%s)", (char *)szBaseName); + + // Create the maintenance semaphore + + if( RC_BAD( rc = f_semCreate( &m_hMaintSem))) + { + goto Exit; + } + + // Start the thread. + + if( RC_BAD( rc = f_threadCreate( &m_pMaintThrd, + F_Database::maintenanceThread, szThreadName, + FLM_DEFAULT_THREAD_GROUP, 0, this, NULL, 32000))) + { + goto Exit; + } + + // Signal the thread to check for any queued work + + f_semSignal( m_hMaintSem); + +Exit: + + if( RC_BAD( rc)) + { + if( m_hMaintSem != F_SEM_NULL) + { + f_semDestroy( &m_hMaintSem); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Db::beginBackgroundTrans( + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + +RetryLock: + + // Obtain the file lock + + flmAssert( !(m_uiFlags & FDB_HAS_FILE_LOCK)); + + if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Lock( this, m_hWaitSem, + TRUE, FALSE, TRUE, XFLM_NO_TIMEOUT, FLM_BACKGROUND_LOCK_PRIORITY, + m_pDbStats))) + { + if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) + { + // This would only happen if we were signaled to shut down. + // So, it's ok to exit + + flmAssert( pThread->getShutdownFlag()); + } + goto Exit; + } + + // The lock needs to be marked as implicit so that commitTrans + // will unlock the database and allow the next update transaction to + // begin before all writes are complete. + + m_uiFlags |= (FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); + + // If there are higher priority waiters in the lock queue, + // we want to relinquish. + + if( m_pDatabase->m_pDatabaseLockObj->haveHigherPriorityWaiter( + FLM_BACKGROUND_LOCK_PRIORITY)) + { + if( pThread->getShutdownFlag()) + { + goto Exit; + } + if( RC_BAD( rc = m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this))) + { + goto Exit; + } + + m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); + goto RetryLock; + } + + // If we are shutting down, relinquish and exit. + + if( pThread->getShutdownFlag()) + { + rc = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); + goto Exit; + } + + // Start an update transaction + + if( RC_BAD( rc = beginTrans( + XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT, XFLM_DONT_POISON_CACHE))) + { + if( rc == NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT) + { + // This would only happen if we were signaled to shut down. + // So, it's ok to exit + + flmAssert( pThread->getShutdownFlag()); + } + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + if( m_uiFlags & FDB_HAS_FILE_LOCK) + { + (void)m_pDatabase->m_pDatabaseLockObj->Unlock( TRUE, this); + m_uiFlags &= ~(FDB_HAS_FILE_LOCK | FDB_FILE_LOCK_IMPLICIT); + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Thread that will build an index in the background. + Caller will create a pDb to use. This pDb must be + freed at the conclusion of the routine. +****************************************************************************/ +RCODE F_Database::maintenanceThread( + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = (F_Database *)pThread->getParm1(); + F_Db * pDb; + F_DOMNode * pDoc; + F_DOMNode * pNextDoc; + FLMUINT64 ui64DocId; + FLMUINT64 ui64TmpTransId; + FLMUINT64 ui64SweepTransId; + FLMUINT uiNameId; + FLMBOOL bStartedTrans; + FLMBOOL bShutdown; + F_DbSystem * pDbSystem = NULL; + +Retry: + + rc = NE_XFLM_OK; + pDb = NULL; + pDoc = NULL; + pNextDoc = NULL; + bStartedTrans = FALSE; + bShutdown = FALSE; + + if( (pDbSystem = f_new F_DbSystem) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_INITIALIZING); + + if( RC_BAD( rc = pDbSystem->internalDbOpen( pDatabase, &pDb))) + { + // If the file is being closed, this is not an error. + + if( pDatabase->getFlags() & DBF_BEING_CLOSED) + { + rc = NE_XFLM_OK; + bShutdown = TRUE; + } + goto Exit; + } + pDbSystem->Release(); + pDbSystem = NULL; + + for( ;;) + { + pThread->setThreadStatus( FLM_THREAD_STATUS_RUNNING); + ui64DocId = 0; + + for( ;;) + { + if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread))) + { + goto Exit; + } + bStartedTrans = TRUE; + + if( RC_BAD( rc = pDb->getDocument( + XFLM_MAINT_COLLECTION, XFLM_INCL, ui64DocId, (IF_DOMNode **)&pDoc))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + ui64DocId = pDoc->getDocumentId(); + + if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId))) + { + goto Exit; + } + + if( uiNameId == ELM_DELETE_TAG) + { + if( RC_BAD( rc = pDb->maintBlockChainFree( + ui64DocId, 25, 0, NULL))) + { + goto Exit; + } + + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->commitTrans( 0, FALSE))) + { + goto Exit; + } + } + else if( uiNameId == ELM_SWEEP_TAG) + { + ui64SweepTransId = pDb->getTransID(); + pDb->abortTrans(); + bStartedTrans = FALSE; + + if( RC_BAD( rc = pDb->sweep( pThread))) + { + goto Exit; + } + + // Delete the sweep documents from the tracker + + if( RC_BAD( rc = pDb->beginBackgroundTrans( pThread))) + { + goto Exit; + } + bStartedTrans = TRUE; + + for( ;;) + { + if( RC_BAD( rc = pDoc->getNextDocument( pDb, + (IF_DOMNode **)&pNextDoc))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if( RC_BAD( rc = pDoc->getNameId( pDb, &uiNameId))) + { + goto Exit; + } + + if( uiNameId == ELM_SWEEP_TAG) + { + if( RC_BAD( rc = pDoc->getAttributeValueUINT64( + pDb, ATTR_TRANSACTION_TAG, &ui64TmpTransId, 0))) + { + goto Exit; + } + + if( ui64TmpTransId > ui64SweepTransId) + { + break; + } + + if( RC_BAD( rc = pDoc->removeModeFlags( + pDb, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pDoc->deleteNode( pDb))) + { + goto Exit; + } + } + + pDoc->Release(); + + // Use the reference on pNextDoc and set it to NULL so that + // it doesn't get released. + + pDoc = pNextDoc; + pNextDoc = NULL; + } + + bStartedTrans = FALSE; + if( RC_BAD( rc = pDb->commitTrans( 0, FALSE))) + { + goto Exit; + } + } + else + { + flmAssert( bStartedTrans); + pDb->abortTrans(); + bStartedTrans = FALSE; + } + + ui64DocId++; + } + + if( bStartedTrans) + { + pDb->abortTrans(); + bStartedTrans = FALSE; + } + + pThread->setThreadStatus( FLM_THREAD_STATUS_SLEEPING); + f_semWait( pDatabase->m_hMaintSem, F_SEM_WAITFOREVER); + + if( pThread->getShutdownFlag()) + { + bShutdown = TRUE; + goto Exit; + } + } + +Exit: + + pThread->setThreadStatus( FLM_THREAD_STATUS_TERMINATING); + + if( pDbSystem) + { + pDbSystem->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + if( pNextDoc) + { + pNextDoc->Release(); + } + + if( bStartedTrans) + { + pDb->abortTrans(); + } + + if( pDb) + { + pDb->Release(); + pDb = NULL; + } + + if( !bShutdown) + { + flmAssert( RC_BAD( rc)); + f_sleep( 250); + f_semSignal( pDatabase->m_hMaintSem); + goto Retry; + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Db::maintBlockChainFree( + FLMUINT64 ui64MaintDocID, + FLMUINT uiBlocksToFree, + FLMUINT uiExpectedEndAddr, + FLMUINT * puiBlocksFreed) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTmp; + FLMUINT uiBlocksFreed = 0; + FLMUINT uiStartAddr = 0; + FLMUINT uiEndAddr = 0; + F_DOMNode * pDoc = NULL; + F_DOMNode * pChainNode = NULL; + F_DOMNode * pAddrNode = NULL; + FLMUINT uiRflToken = 0; + + // Make sure an update transaction is going and that a + // non-zero number of blocks was specified + + if( getTransType() != XFLM_UPDATE_TRANS || !uiBlocksToFree) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Retrieve the maintenance document + + if( RC_BAD( rc = getNode( XFLM_MAINT_COLLECTION, + ui64MaintDocID, XFLM_EXACT, &pDoc))) + { + goto Exit; + } + + m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + + while( uiBlocksFreed < uiBlocksToFree) + { + if( RC_BAD( rc = pDoc->getChildElement( + this, ELM_BLOCK_CHAIN_TAG, (IF_DOMNode **)&pChainNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + if( RC_BAD( rc = pDoc->removeModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pDoc->deleteNode( this))) + { + goto Exit; + } + + break; + } + + if( RC_BAD( rc = pChainNode->getAttributeValueUINT( + this, ATTR_ADDRESS_TAG, &uiStartAddr, 0))) + { + goto Exit; + } + + if( !uiStartAddr) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = btFreeBlockChain( + this, NULL, uiStartAddr, uiBlocksToFree - uiBlocksFreed, + &uiTmp, &uiEndAddr, NULL))) + { + goto Exit; + } + + uiBlocksFreed += uiTmp; + flmAssert( uiBlocksFreed <= uiBlocksToFree); + + if( RC_BAD( rc = pChainNode->removeModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( !uiEndAddr) + { + if( RC_BAD( rc = pChainNode->deleteNode( this))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pChainNode->getAttribute( + this, ATTR_ADDRESS_TAG, (IF_DOMNode **)&pAddrNode))) + { + goto Exit; + } + + if( RC_BAD( rc = pAddrNode->removeModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pAddrNode->setUINT( this, uiEndAddr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAddrNode->addModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pChainNode->addModeFlags( + this, FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = documentDone( + XFLM_MAINT_COLLECTION, ui64MaintDocID))) + { + goto Exit; + } + } + + if( uiExpectedEndAddr) + { + if( uiBlocksToFree != uiBlocksFreed || + uiEndAddr != uiExpectedEndAddr) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + + if ( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if( RC_BAD( rc = m_pDatabase->m_pRfl->logBlockChainFree( + this, ui64MaintDocID, uiStartAddr, uiEndAddr, uiBlocksFreed))) + { + goto Exit; + } + + if( puiBlocksFreed) + { + *puiBlocksFreed = uiBlocksFreed; + } + +Exit: + + if ( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + if( pChainNode) + { + pChainNode->Release(); + } + + if( pAddrNode) + { + pAddrNode->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + return( rc); +} diff --git a/version5/src/fsrefupd.cpp b/version5/src/fsrefupd.cpp new file mode 100644 index 0000000..4ff1968 --- /dev/null +++ b/version5/src/fsrefupd.cpp @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// Desc: Insert and delete keys in an index B-Tree. +// +// 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: fsrefupd.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/*************************************************************************** +Desc: Update (add or delete) a single reference +*****************************************************************************/ +RCODE F_Db::refUpdate( + LFILE * pLFile, + IXD * pIxd, + KREF_ENTRY * pKrefEntry, + FLMBOOL bNormalUpdate) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pbtree = NULL; + IXKeyCompare compareObject; + + // Get a btree + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pbtree))) + { + goto Exit; + } + + flmAssert( pLFile->uiRootBlk); + + compareObject.setIxInfo( this, pIxd); + if (bNormalUpdate && pKrefEntry->bDelete) + { + compareObject.setOldNodeList( m_pOldNodeList); + } + if( RC_BAD( rc = pbtree->btOpen( this, pLFile, + (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE, + (pIxd->pFirstData) ? TRUE : FALSE, &compareObject))) + { + goto Exit; + } + + if (!pKrefEntry->bDelete) + { + pbtree->btResetBtree(); + if( RC_BAD( rc = pbtree->btInsertEntry( + (FLMBYTE *)&pKrefEntry [1], pKrefEntry->ui16KeyLen, + pKrefEntry->ui16KeyLen, + pKrefEntry->uiDataLen + ? ((FLMBYTE *)(&pKrefEntry [1])) + 1 + pKrefEntry->ui16KeyLen + : NULL, + pKrefEntry->uiDataLen, TRUE, TRUE))) + { + goto Exit; + } + } + else + { + pbtree->btResetBtree(); + if (RC_BAD( rc = pbtree->btRemoveEntry( + (FLMBYTE *)&pKrefEntry [1], pKrefEntry->ui16KeyLen, + pKrefEntry->ui16KeyLen))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + // Already been deleted, ignore the error condition and go on. + + RC_UNEXPECTED_ASSERT( rc); + rc = NE_XFLM_OK; + } + + goto Exit; + } + } + +Exit: + + if (pbtree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pbtree); + } + + return( rc); +} diff --git a/version5/src/fsrvlock.cpp b/version5/src/fsrvlock.cpp new file mode 100644 index 0000000..d814a12 --- /dev/null +++ b/version5/src/fsrvlock.cpp @@ -0,0 +1,1212 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the methods for the ServerLockManager and +// ServerLockObject classes. +// +// 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: fsrvlock.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define LOCK_HASH_ENTRIES 256 + +/**************************************************************************** +Desc: +****************************************************************************/ +ServerLockManager::~ServerLockManager() +{ + // Free everything in the avail lock list. This is where all + // of the lock objects should be at this point. + + if (m_hMutex != F_MUTEX_NULL) + { + lockMutex( FALSE); + + // Signal all pending lock waiters. + + CheckLockTimeouts( TRUE, TRUE); + + while (m_pAvailLockList) + { + ServerLockObject_p pLockObject = m_pAvailLockList; + + UnlinkLockObject( pLockObject, FALSE); + + pLockObject->Release(); + } + unlockMutex( FALSE); + f_mutexDestroy( &m_hMutex); + } + + // Free the hash table. + + f_free( &m_pHashTbl); +} + +/**************************************************************************** +Desc: Initializes the lock manager's hash table. +****************************************************************************/ +RCODE ServerLockManager::SetupHashTbl() +{ + return( flmAllocHashTbl( LOCK_HASH_ENTRIES, &m_pHashTbl)); +} + +/**************************************************************************** +Desc: Finds the lock object for the passed in item identifier. + If one is not found, one will be created. +****************************************************************************/ +ServerLockObject_p ServerLockManager::GetLockObject( + F_ItemId_p pItemId) +{ + FLMUINT uiBucket; + ServerLockObject * pLockObject; + ServerLockObject * pTmpLockObject; + + // Get the hash bucket. + + uiBucket = pItemId->GetHashBucket( m_pHashTbl, LOCK_HASH_ENTRIES); + + // See if the desired file is already in the hash bucket. + + lockMutex( FALSE); + + pLockObject = (ServerLockObject_p)m_pHashTbl [uiBucket].pFirstInBucket; + + // See if any of the objects match. + + while (pLockObject) + { + if (pItemId->IsEqual( pLockObject->m_pItemId)) + { + goto Exit; + } + pLockObject = pLockObject->m_pNext; + } + + // If we didn't find a matching object, allocate an object and link it into + // the hash bucket. Check to see if we have any in the avail list + // first so that we don't have to allocate memory if we can avoid it. + + if ((pLockObject = m_pAvailLockList) != NULL) + { + UnlinkLockObject( pLockObject, FALSE); + } + else + { + if ((pLockObject = f_new ServerLockObject) == NULL) + { + goto Exit; + } + } + + // Setup the new object and put it into the hash bucket. + + pLockObject->Setup( this, pItemId, uiBucket); + pTmpLockObject = + (ServerLockObject_p)m_pHashTbl [uiBucket].pFirstInBucket; + pLockObject->m_pPrev = NULL; + pLockObject->m_pNext = pTmpLockObject; + if (pTmpLockObject) + { + pTmpLockObject->m_pPrev = pLockObject; + } + m_pHashTbl [uiBucket].pFirstInBucket = pLockObject; + +Exit: + + unlockMutex( FALSE); + return( pLockObject); +} + +/**************************************************************************** +Desc: Unlinks a lock object from whatever list it is in. This routine + assumes that the server lock manager's mutex is already locked. +****************************************************************************/ +void ServerLockManager::UnlinkLockObject( + ServerLockObject * pLockObject, + FLMBOOL bPutInAvailList) +{ + ServerLockObject * pTmpLockObject; + FLMUINT uiBucket; + + // If hash bucket 0xFFFF, unlink from the avail list. Otherwise, + // unlink from the hash bucket it is in. + + if ((uiBucket = pLockObject->m_uiBucket) == 0xFFFF) + { + if ((pTmpLockObject = pLockObject->m_pPrev) == NULL) + { + m_pAvailLockList = pLockObject->m_pNext; + } + else + { + pTmpLockObject->m_pNext = pLockObject->m_pNext; + } + m_uiNumAvail--; + } + else + { + if ((pTmpLockObject = pLockObject->m_pPrev) == NULL) + { + m_pHashTbl [uiBucket].pFirstInBucket = pLockObject->m_pNext; + } + else + { + pTmpLockObject->m_pNext = pLockObject->m_pNext; + } + } + if ((pTmpLockObject = pLockObject->m_pNext) != NULL) + { + pTmpLockObject->m_pPrev = pLockObject->m_pPrev; + } + + if (bPutInAvailList) + { + if (m_uiNumAvail >= 50) + { + flmAssert( getRefCount() == 1); + pLockObject->Release(); + } + else + { + pLockObject->Setup( this, NULL, 0xFFFF); + if (m_pAvailLockList) + { + m_pAvailLockList->m_pPrev = pLockObject; + } + pLockObject->m_pPrev = NULL; + pLockObject->m_pNext = m_pAvailLockList; + m_pAvailLockList = pLockObject; + m_uiNumAvail++; + } + } +} + +/**************************************************************************** +Desc: Checks for any pending lock requests that have timed out. +****************************************************************************/ +void ServerLockManager::CheckLockTimeouts( + FLMBOOL bMutexAlreadyLocked, + FLMBOOL bTimeoutAll) +{ + FLMUINT uiCurrTime; + LOCK_WAITER_p pLockWaiter; + + lockMutex( bMutexAlreadyLocked); + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + while ((m_pFirstLockWaiter) && + ((bTimeoutAll) || + ((m_pFirstLockWaiter->uiWaitTime) && + (FLM_ELAPSED_TIME( uiCurrTime, m_pFirstLockWaiter->uiWaitStartTime) >= + m_pFirstLockWaiter->uiWaitTime)))) + { + // Sanity check + + flmAssert( m_pFirstLockWaiter->pPrevGlobal == NULL); + + // Lock waiter has timed out. + + pLockWaiter = m_pFirstLockWaiter; + + // Remove from global list and lock object's list + + RemoveWaiter( pLockWaiter); + pLockWaiter->pLockObject->RemoveWaiter( pLockWaiter); + + // Tell the waiter that the lock request timed out. + + *(pLockWaiter->pRc) = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); + f_semSignal( pLockWaiter->hESem); + } + + unlockMutex( bMutexAlreadyLocked); +} + + +/**************************************************************************** +Desc: Signal a lock waiter that has a matching thread id. +****************************************************************************/ +void ServerLockManager::SignalLockWaiter( + FLMUINT uiThreadId) +{ + FLMUINT uiCurrTime; + LOCK_WAITER_p pLockWaiter; + LOCK_WAITER_p pNextWaiter; + + lockMutex( FALSE); + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + for( pLockWaiter = m_pFirstLockWaiter; + pLockWaiter; + pLockWaiter = pNextWaiter) + { + pNextWaiter = pLockWaiter->pNextGlobal; + + if( pLockWaiter->uiThreadId == uiThreadId) + { + // Remove from global list and lock object's list + + RemoveWaiter( pLockWaiter); + pLockWaiter->pLockObject->RemoveWaiter( pLockWaiter); + + // Tell the waiter that the lock request timed out. + + *(pLockWaiter->pRc) = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); + f_semSignal( pLockWaiter->hESem); + break; + } + } + + unlockMutex( FALSE); + return; +} + +/**************************************************************************** +Desc: Inserts a waiter into the global list of waiters, sorted by + its end wait time. + + NOTE: This routine assumes that the lock manager's semaphore + is already locked. +****************************************************************************/ +void ServerLockManager::InsertWaiter( + LOCK_WAITER_p pLockWaiter) +{ + LOCK_WAITER_p pPrevLockWaiter; + + // Determine where in the list this lock waiter should go. + + if ((pPrevLockWaiter = m_pFirstLockWaiter) != NULL) + { + FLMUINT uiCurrTime = FLM_GET_TIMER(); + FLMUINT uiElapTime; + FLMUINT uiTimeLeft; + + while (pPrevLockWaiter) + { + // Waiters with zero wait time go to end of list. + // They never time out. + + if (!pPrevLockWaiter->uiWaitTime) + { + + // Should go BEFORE the first zero waiter. + + pPrevLockWaiter = pPrevLockWaiter->pPrevGlobal; + break; + } + else if (!pLockWaiter->uiWaitTime) + { + if (!pPrevLockWaiter->pNextGlobal) + { + break; + } + pPrevLockWaiter = pPrevLockWaiter->pNextGlobal; + } + else + { + // Determine how much time is left on the previous + // lock waiter's timer. If it is less than the + // new lock waiter's wait time, the new lock waiter + // should be inserted AFTER it. Otherwise, the + // new lock waiter should be inserted BEFORE it. + + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, + pPrevLockWaiter->uiWaitStartTime); + if (uiElapTime >= pPrevLockWaiter->uiWaitTime) + { + uiTimeLeft = 0; + } + else + { + uiTimeLeft = pPrevLockWaiter->uiWaitTime - uiElapTime; + } + + // New lock waiter will time out before previous lock + // waiter - insert it BEFORE the previous lock waiter. + + if (pLockWaiter->uiWaitTime < uiTimeLeft) + { + pPrevLockWaiter = pPrevLockWaiter->pPrevGlobal; + break; + } + else + { + if (!pPrevLockWaiter->pNextGlobal) + break; + pPrevLockWaiter = pPrevLockWaiter->pNextGlobal; + } + } + } + } + + // Insert into list AFTER pPrevLockWaiter. + + if ((pLockWaiter->pPrevGlobal = pPrevLockWaiter) != NULL) + { + if ((pLockWaiter->pNextGlobal = pPrevLockWaiter->pNextGlobal) != NULL) + { + pLockWaiter->pNextGlobal->pPrevGlobal = pLockWaiter; + } + pPrevLockWaiter->pNextGlobal = pLockWaiter; + } + else + { + if( (pLockWaiter->pNextGlobal = m_pFirstLockWaiter) != NULL) + { + m_pFirstLockWaiter->pPrevGlobal = pLockWaiter; + } + m_pFirstLockWaiter = pLockWaiter; + } +} + +/**************************************************************************** +Desc: See if this item ID is equal to another F_ItemId. +****************************************************************************/ +FLMBOOL FFileItemId::IsEqual( + F_ItemId_p pItemId) +{ + FFileItemId_p pFFileItemId; + RFileItemId_p pRFileItemId; + char szName1 [F_FILENAME_SIZE]; + char szName2 [F_FILENAME_SIZE]; + FLMUINT uiItemType = pItemId->GetItemType(); + + switch (uiItemType) + { + case FFILE_ITEM: + case FFILE_TRANS_ITEM: + if (m_uiItemType != uiItemType) + return( FALSE); + pFFileItemId = (FFileItemId_p)pItemId; + + /* First see if the FFILE pointers are the same. */ + + if (pFFileItemId->getDatabase() == + this->getDatabase()) + { + return( TRUE); + } + + /* Next see if the file names are the same. */ + + this->GetFileName( szName1); + pFFileItemId->GetFileName( szName2); +#if !defined( FLM_UNIX) + if (f_stricmp( szName1, szName2) == 0) + return( TRUE); +#else + if (f_strcmp( szName1, szName2) == 0) + return( TRUE); +#endif + break; + case RFILE_ITEM: + case RFILE_TRANS_ITEM: + if ((uiItemType == RFILE_ITEM && + m_uiItemType != FFILE_ITEM) || + (uiItemType == RFILE_TRANS_ITEM && + m_uiItemType != FFILE_TRANS_ITEM)) + { + return( FALSE); + } + pRFileItemId = (RFileItemId_p)pItemId; + + /* See if the file names are the same. */ + + this->GetFileName( szName1); + pRFileItemId->GetFileName( szName2); +#if !defined( FLM_UNIX) + if (f_stricmp( szName1, szName2) == 0) + return( TRUE); +#else + if (f_strcmp( szName1, szName2) == 0) + return( TRUE); +#endif + break; + default: + break; + } + return( FALSE); +} + +/**************************************************************************** +Desc: Get file name for this file item. +****************************************************************************/ +void FFileItemId::GetFileName( + char * pszFileNameRV) +{ + // _ExtractFileName( m_pFile, pszFileNameRV); + char szTmpPath[ F_PATH_MAX_SIZE]; + + (void)gv_pFileSystem->pathReduce( m_pDatabase->m_pszDbPath, szTmpPath, pszFileNameRV); + + // Convert to upper case for consistency when hashing. + +#if !defined( FLM_UNIX) + while (*pszFileNameRV) + { + *pszFileNameRV = (char)f_toupper( *pszFileNameRV); + pszFileNameRV++; + } +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RFileItemId::RFileItemId( + char * pszFileName, + FLMBOOL bTrans) +{ + char * pszTmp = &m_szFileName [0]; + + while (*pszFileName) + { + // Convert to uppercase for consistency when hashing. + +#if !defined( FLM_UNIX) + *pszFileName = (char)f_toupper( *pszFileName); +#else + *pszTmp++ = *pszFileName; +#endif + pszFileName++; + } + *pszTmp = 0; + m_uiItemType = (FLMUINT)((bTrans) + ? (FLMUINT)RFILE_TRANS_ITEM + : (FLMUINT)RFILE_ITEM); +} + +/**************************************************************************** +Desc: See if this item ID is equal to another F_ItemId. +****************************************************************************/ +FLMBOOL RFileItemId::IsEqual( + F_ItemId * pItemId) +{ + FFileItemId * pFFileItemId; + RFileItemId * pRFileItemId; + char szName1 [F_FILENAME_SIZE]; + char szName2 [F_FILENAME_SIZE]; + FLMUINT uiItemType = pItemId->GetItemType(); + + switch (uiItemType) + { + case FFILE_ITEM: + case FFILE_TRANS_ITEM: + if ((uiItemType == FFILE_ITEM && + m_uiItemType != RFILE_ITEM) || + (uiItemType == FFILE_TRANS_ITEM && + m_uiItemType != RFILE_TRANS_ITEM)) + { + return( FALSE); + } + pFFileItemId = (FFileItemId_p)pItemId; + + /* See if the file names are the same. */ + + this->GetFileName( szName1); + pFFileItemId->GetFileName( szName2); +#if !defined( FLM_UNIX) + if (f_stricmp( szName1, szName2) == 0) + return( TRUE); +#else + if (f_strcmp( szName1, szName2) == 0) + return( TRUE); +#endif + break; + case RFILE_ITEM: + case RFILE_TRANS_ITEM: + if (m_uiItemType != uiItemType) + return( FALSE); + pRFileItemId = (RFileItemId_p)pItemId; + + /* See if the file names are the same. */ + + this->GetFileName( szName1); + pRFileItemId->GetFileName( szName2); +#if !defined( FLM_UNIX) + if (f_stricmp( szName1, szName2) == 0) + return( TRUE); +#else + if (f_strcmp( szName1, szName2) == 0) + return( TRUE); +#endif + break; + default: + break; + } + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ServerLockObject::ServerLockObject() +{ + m_pServerLockMgr = NULL; + m_pItemId = NULL; + m_uiLockThreadId = 0; + m_uiLockTime = 0; + m_uiLockCnt = 0; + m_pFirstLockWaiter = + m_pLastLockWaiter = NULL; + m_uiNumWaiters = 0; + m_pNext = m_pPrev = NULL; + m_uiSharedLockCnt = 0; + m_bExclLock = FALSE; + m_uiBucket = 0xFFFF; + m_bStartTimeSet = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT ServerLockObject::Release( + FLMBOOL bMutexAlreadyLocked) +{ + FLMUINT uiRefCnt = --m_ui32RefCnt; + + if( !uiRefCnt) + { + delete this; + goto Exit; + } + + // When it is no longer pointed to from anything but the server lock + // manager, put it into the avail list. + + if (uiRefCnt == 1) + { + LOCK_WAITER * pLockWaiter; + + // Signal all waiters that they cannot get the lock. + + m_pServerLockMgr->lockMutex( bMutexAlreadyLocked); + while (m_pFirstLockWaiter) + { + pLockWaiter = m_pFirstLockWaiter; + + // Remove from global list and lock object's list + + RemoveWaiter( pLockWaiter); + m_pServerLockMgr->RemoveWaiter( pLockWaiter); + + // Tell the waiter that the lock request timed out and signal + // the thread to wake it up. + + *(pLockWaiter->pRc) = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); + f_semSignal( pLockWaiter->hESem); + } + + m_pServerLockMgr->UnlinkLockObject( this, TRUE); + m_pServerLockMgr->unlockMutex( bMutexAlreadyLocked); + } + +Exit: + + return( uiRefCnt); +} + +/**************************************************************************** +Desc: Initialize some data for the lock object. +****************************************************************************/ +void ServerLockObject::Setup( + ServerLockManager_p pServerLockMgr, + F_ItemId_p pItemId, + FLMUINT uiBucket) +{ + m_pServerLockMgr = pServerLockMgr; + if (m_pItemId) + { + m_pItemId->Release(); + m_pItemId = NULL; + } + if ((m_pItemId = pItemId) != NULL) + { + m_pItemId->AddRef(); + } + m_uiBucket = uiBucket; +} + +/**************************************************************************** +Desc: Removes a waiter from the list of waiters on this object. + NOTE: This routine assumes that the lock manager's semaphore + is already locked. +****************************************************************************/ +void ServerLockObject::RemoveWaiter( + LOCK_WAITER_p pLockWaiter) +{ + if (pLockWaiter->pNext) + pLockWaiter->pNext->pPrev = pLockWaiter->pPrev; + else + m_pLastLockWaiter = pLockWaiter->pPrev; + + if (pLockWaiter->pPrev) + pLockWaiter->pPrev->pNext = pLockWaiter->pNext; + else + m_pFirstLockWaiter = pLockWaiter->pNext; + flmAssert( m_uiNumWaiters > 0); + m_uiNumWaiters--; +} + +/**************************************************************************** +Desc: Lock this object. If object is locked, wait the specified + number of seconds. +****************************************************************************/ +RCODE ServerLockObject::Lock( + F_Db * pDb, + F_SEM hWaitSem, + FLMBOOL bLogEvent, + FLMBOOL bSendSuspendEvent, // Send suspend event, as opposed to + // waiting event, when waiting. + FLMBOOL bExclReq, // Exclusive or shared lock? + FLMUINT uiMaxWaitSecs, // Maximum wait time in seconds. + FLMINT iPriority, // Lock priority + XFLM_DB_STATS * pDbStats) // Place to collect stats. +{ + RCODE rc = NE_XFLM_OK; + RCODE TempRc; + LOCK_WAITER LockWait; + FLMBOOL bMutexLocked = FALSE; + + flmAssert( hWaitSem != F_SEM_NULL); + + m_pServerLockMgr->lockMutex( FALSE); + bMutexLocked = TRUE; + + if ((m_pFirstLockWaiter) || + (m_bExclLock) || + ((bExclReq) && (m_uiSharedLockCnt))) + { + + // Object is locked, wait to get lock. + + if (!uiMaxWaitSecs) + { + rc = RC_SET( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT); + goto Exit; + } + + // Set up to wait for the lock. + + f_memset( &LockWait, 0, sizeof( LockWait)); + LockWait.pLockObject = this; + LockWait.hESem = hWaitSem; + + // Link into list of waiters on this object. + + if ((LockWait.pPrev = m_pLastLockWaiter) != NULL) + { + LockWait.pPrev->pNext = &LockWait; + } + else + { + m_pFirstLockWaiter = &LockWait; + } + m_pLastLockWaiter = &LockWait; + m_uiNumWaiters++; + + LockWait.uiThreadId = f_threadId(); + LockWait.pRc = &rc; + + rc = RC_SET( NE_XFLM_FAILURE); + + LockWait.bExclReq = bExclReq; + LockWait.iPriority = iPriority; + LockWait.uiWaitStartTime = (FLMUINT)FLM_GET_TIMER(); + + if (bExclReq && pDbStats) + { + f_timeGetTimeStamp( &LockWait.StartTime); + LockWait.pDbStats = pDbStats; + } + + if (uiMaxWaitSecs == XFLM_NO_TIMEOUT) + { + LockWait.uiWaitTime = 0; + } + else + { + FLM_SECS_TO_TIMER_UNITS( uiMaxWaitSecs, + LockWait.uiWaitTime); + } + + // Link to list of global waiters - ordered by end time. + + m_pServerLockMgr->InsertWaiter( &LockWait); + + m_pServerLockMgr->unlockMutex( FALSE); + bMutexLocked = FALSE; + + // Do the event callback, if any registered. + + if (bLogEvent && + gv_XFlmSysData.EventHdrs [XFLM_EVENT_LOCKS].pEventCBList) + { + flmDoEventCallback( XFLM_EVENT_LOCKS, + (eEventType)((bSendSuspendEvent) + ? XFLM_EVENT_LOCK_SUSPENDED + : XFLM_EVENT_LOCK_WAITING), + pDb, LockWait.uiThreadId, + 0, 0, 0, NE_XFLM_OK); + } + + // Now just wait to be signaled. + + if (RC_BAD( TempRc = f_semWait( LockWait.hESem, F_SEM_WAITFOREVER))) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + RC_UNEXPECTED_ASSERT( TempRc); +#endif + rc = TempRc; + } + else + { + // Process that signaled us better set the rc to something + // besides NE_XFLM_FAILURE. + + if (rc == NE_XFLM_FAILURE) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + RC_UNEXPECTED_ASSERT( rc); +#endif + } + } + + // Do the event callback, if any registered. + + if (bLogEvent && + gv_XFlmSysData.EventHdrs [XFLM_EVENT_LOCKS].pEventCBList) + { + if (RC_BAD( rc)) + { + flmDoEventCallback( XFLM_EVENT_LOCKS, + XFLM_EVENT_LOCK_TIMEOUT, + pDb, LockWait.uiThreadId, + 0, 0, 0, NE_XFLM_OK); + } + else + { + flmDoEventCallback( XFLM_EVENT_LOCKS, + (eEventType)((bSendSuspendEvent) + ? XFLM_EVENT_LOCK_RESUMED + : XFLM_EVENT_LOCK_GRANTED), + pDb, LockWait.uiThreadId, + 0, 0, 0, NE_XFLM_OK); + } + } + } + else + { + + // Object is NOT locked in a conflicting mode. Grant the + // lock immediately. + + m_uiLockThreadId = f_threadId(); + m_bExclLock = bExclReq; + if (!bExclReq) + { + m_uiSharedLockCnt++; + } + else + { + m_uiLockTime = (FLMUINT)FLM_GET_TIMER(); + flmAssert( m_uiSharedLockCnt == 0); + + // Take care of statistics gathering. + + if (pDbStats) + { + + // If m_bStartTimeSet is TRUE, we started the + // clock the last time nobody had the exclusive + // lock, so we need to sum up idle time now. + + if (m_bStartTimeSet) + { + flmAddElapTime( &m_StartTime, &pDbStats->NoLocks.ui64ElapMilli); + pDbStats->NoLocks.ui64Count++; + } + + // Restart the clock for this locker. + + f_timeGetTimeStamp( &m_StartTime); + m_bStartTimeSet = TRUE; + } + else + { + m_bStartTimeSet = FALSE; + } + } + + // Do the event callback, if any registered. + + if (bLogEvent && + !bSendSuspendEvent && + gv_XFlmSysData.EventHdrs [XFLM_EVENT_LOCKS].pEventCBList) + { + m_pServerLockMgr->unlockMutex( FALSE); + bMutexLocked = FALSE; + flmDoEventCallback( XFLM_EVENT_LOCKS, + XFLM_EVENT_LOCK_GRANTED, + pDb, m_uiLockThreadId, + 0, 0, 0, NE_XFLM_OK); + } + } + +Exit: + + if (RC_OK( rc)) + { + m_uiLockCnt++; + } + + if (bMutexLocked) + { + m_pServerLockMgr->unlockMutex( FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Unlock this object. If there is a pending lock request, give + the lock to the next waiter. +****************************************************************************/ +RCODE ServerLockObject::Unlock( + FLMBOOL bLogEvent, + F_Db * pDb, + FLMBOOL bRelease, + XFLM_DB_STATS * pDbStats) +{ + RCODE rc = NE_XFLM_OK; + F_SEM hESem; + LOCK_WAITER_p pLockWaiter; + + m_pServerLockMgr->lockMutex( FALSE); + + if (m_bExclLock) + { + flmAssert( m_uiSharedLockCnt == 0); + m_bExclLock = FALSE; + + // Record how long the lock was held, if we were tracking + // it. + + if (pDbStats && m_bStartTimeSet) + { + flmAddElapTime( &m_StartTime, &pDbStats->HeldLock.ui64ElapMilli); + pDbStats->HeldLock.ui64Count++; + } + m_bStartTimeSet = FALSE; + } + else + { + flmAssert( m_uiSharedLockCnt > 0); + m_uiSharedLockCnt--; + } + + // Do the event callback, if any registered. + // NOTE: flmDoEventCallback locks the event mutex. + // Since we are inside the lock manager's mutex lock, + // the callback should not do ANYTHING that would cause + // us to end up in here again! + + if (bLogEvent && + gv_XFlmSysData.EventHdrs [XFLM_EVENT_LOCKS].pEventCBList) + { + flmDoEventCallback( XFLM_EVENT_LOCKS, + XFLM_EVENT_LOCK_RELEASED, + pDb, m_uiLockThreadId, + 0, 0, 0, NE_XFLM_OK); + } + + m_uiLockThreadId = 0; + + /* See if we need to signal the next set of waiters. */ + + if (m_pFirstLockWaiter && !m_uiSharedLockCnt) + { + m_bExclLock = m_pFirstLockWaiter->bExclReq; + while (m_pFirstLockWaiter) + { + if (!m_bExclLock) + { + m_uiSharedLockCnt++; + } + + + pLockWaiter = m_pFirstLockWaiter; + hESem = pLockWaiter->hESem; + + // Unlink the waiter from the list of waiters on this lock + // object and then from the global list of waiters. + // IMPORTANT NOTE: Do NOT signal the semaphore until AFTER + // doing this unlinking. This is because LOCK_WAITER + // structures exist only on the stack of the thread + // being signaled. If we tried to assign m_pFirstLockWaiter after + // signaling the semaphore, the LOCK_WAITER structure could + // disappear and m_pFirstLockWaiter would get garbage. + + RemoveWaiter( pLockWaiter); + m_pServerLockMgr->RemoveWaiter( pLockWaiter); + + // Update statistics for the waiter. + + if (pLockWaiter->pDbStats) + { + flmAddElapTime( &pLockWaiter->StartTime, + &pLockWaiter->pDbStats->WaitingForLock.ui64ElapMilli); + pLockWaiter->pDbStats->WaitingForLock.ui64Count++; + } + + // Grant the lock to this waiter and signal the thread + // to wake it up. + + m_uiLockThreadId = pLockWaiter->uiThreadId; + if (m_bExclLock) + { + m_uiLockTime = (FLMUINT)FLM_GET_TIMER(); + + // Restart the stats timer + + if (pDbStats) + { + m_bStartTimeSet = TRUE; + f_timeGetTimeStamp( &m_StartTime); + } + } + + *(pLockWaiter->pRc) = NE_XFLM_OK; + + f_semSignal( hESem); + + // If the next waiter is not a shared lock request or + // the lock that was granted was exclusive, we stop + // here. + + if (m_bExclLock || + (m_pFirstLockWaiter && m_pFirstLockWaiter->bExclReq)) + { + break; + } + } + } + else if (bRelease && + !m_pFirstLockWaiter && // No one is wating for the object + !m_uiSharedLockCnt) // No one has the object locked + { + // Release the object. If the reference count drops to 1, + // the object will be put in the avail list. The caller + // should have performed an AddRef() on the object at + // some point prior to calling this method. Once this routine + // returns the caller should not attempt further access of the object. + Release( TRUE); + bRelease = FALSE; + } + + // Start timer, if not already running. If the timer is not set at + // this point, it will be because nobody has been granted the exclusive + // lock. If someone was granted the exclusive lock, the timer would + // have been started above. We start it here so we can track idle + // time. + + if (pDbStats && !m_bStartTimeSet && !bRelease) + { + flmAssert( !m_bExclLock); + m_bStartTimeSet = TRUE; + f_timeGetTimeStamp( &m_StartTime); + } + + // If we get to this point and bRelease is still TRUE, someone was + // waiting to acquire the lock or there is still a shared lock + // count. + + if( bRelease) + { + // All lock waiters should have done an AddRef on the lock object. + // At this point we should still have our reference to the object, + // the lock manager's reference, and a reference from at least one + // waiter (that may have been granted above). + flmAssert( m_ui32RefCnt >= 3); + m_ui32RefCnt--; + } + + m_pServerLockMgr->unlockMutex( FALSE); + return( rc); +} + +/**************************************************************************** +Desc: Returns information about the pending lock requests. +****************************************************************************/ +void ServerLockObject::GetLockInfo( + FLMINT iPriority, + eDbLockType * peCurrLockType, + FLMUINT * puiThreadId, + FLMUINT * puiNumExclQueued, + FLMUINT * puiNumSharedQueued, + FLMUINT * puiPriorityCount) +{ + LOCK_WAITER_p pLockWaiter; + + *puiNumExclQueued = 0; + *puiNumSharedQueued = 0; + *puiPriorityCount = 0; + + m_pServerLockMgr->lockMutex( FALSE); + + // Get the type of lock, if any. + + if (m_bExclLock) + { + *peCurrLockType = XFLM_LOCK_EXCLUSIVE; + *puiThreadId = m_uiLockThreadId; + } + else if (m_uiSharedLockCnt) + { + *peCurrLockType = XFLM_LOCK_SHARED; + *puiThreadId = 0; + } + else + { + *peCurrLockType = XFLM_LOCK_NONE; + *puiThreadId = 0; + } + + // Get information on pending lock requests. + + pLockWaiter = m_pFirstLockWaiter; + for ( ; pLockWaiter; pLockWaiter = pLockWaiter->pNext ) + { + + // Count the number of exclusive and shared waiters. + + if (pLockWaiter->bExclReq) + { + (*puiNumExclQueued)++; + } + else + { + (*puiNumSharedQueued)++; + } + + // Count the number of waiters at or above input priority. + + if (pLockWaiter->iPriority >= iPriority) + { + (*puiPriorityCount)++; + } + } + + m_pServerLockMgr->unlockMutex( FALSE); +} + +/**************************************************************************** +Desc: Return the lock waiters for this object. +****************************************************************************/ +RCODE ServerLockObject::GetLockInfo( + IF_LockInfoClient * pLockInfo) +{ + RCODE rc = NE_XFLM_OK; + LOCK_WAITER * pLockWaiter; + FLMUINT uiCnt; + FLMUINT uiElapTime; + FLMUINT uiCurrTime; + FLMUINT uiMilli; + + m_pServerLockMgr->lockMutex( FALSE); + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + if (!m_uiNumWaiters && !m_uiLockThreadId) + { + pLockInfo->setLockCount( 0); + goto Exit; + } + uiCnt = m_uiNumWaiters + 1; // Add one for lock holder. + if( pLockInfo->setLockCount( uiCnt) == FALSE) + { + goto Exit; + } + + // Output the lock holder first. + + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, m_uiLockTime); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, uiMilli); + if( pLockInfo->addLockInfo( 0, m_uiLockThreadId, uiMilli) == FALSE) + { + goto Exit; + } + uiCnt--; + + // Output the lock waiters. + + pLockWaiter = m_pFirstLockWaiter; + while( pLockWaiter && uiCnt) + { + uiElapTime = FLM_ELAPSED_TIME( uiCurrTime, pLockWaiter->uiWaitStartTime); + FLM_TIMER_UNITS_TO_MILLI( uiElapTime, uiMilli); + if( pLockInfo->addLockInfo( (m_uiNumWaiters - uiCnt) + 1, + pLockWaiter->uiThreadId, uiMilli) == FALSE) + { + goto Exit; + } + pLockWaiter = pLockWaiter->pNext; + uiCnt--; + } + flmAssert( pLockWaiter == NULL && uiCnt == 0); + +Exit: + + m_pServerLockMgr->unlockMutex( FALSE); + return( rc); +} + +/**************************************************************************** +Desc: Returns TRUE if there are lock waiters with a priority > iPriority +****************************************************************************/ +FLMBOOL ServerLockObject::haveHigherPriorityWaiter( + FLMINT iPriority) +{ + LOCK_WAITER_p pLockWaiter; + FLMBOOL bWaiters = FALSE; + + m_pServerLockMgr->lockMutex( FALSE); + + pLockWaiter = m_pFirstLockWaiter; + for ( ; pLockWaiter; pLockWaiter = pLockWaiter->pNext ) + { + // If we find a waiter with a priority > the specified + // priority, we're done. + + if (pLockWaiter->iPriority > iPriority) + { + bWaiters = TRUE; + break; + } + } + + m_pServerLockMgr->unlockMutex( FALSE); + return( bWaiters); +} diff --git a/version5/src/fsrvlock.h b/version5/src/fsrvlock.h new file mode 100644 index 0000000..3fbae49 --- /dev/null +++ b/version5/src/fsrvlock.h @@ -0,0 +1,424 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's +// ServerLockManager and ServerLockObject classes. +// +// 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: fsrvlock.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FSRVLOCK_H +#define FSRVLOCK_H + +FLMUINT flmStrHashBucket( + char * pszStr, + FBUCKET * pHashTbl, + FLMUINT uiNumBuckets); + + /* + *** Define the 'C++' classes for FLAIM's File Handle cache code. + */ + +class ServerLockManager; // Forward Reference +class ServerLockObject; // Forward Reference +class F_ItemId; // Forward Reference +class FFileItemId; // Forward Reference +class RFileItemId; // Forward Reference + +typedef ServerLockManager * ServerLockManager_p; +typedef ServerLockObject * ServerLockObject_p; +typedef F_ItemId * F_ItemId_p; +typedef FFileItemId * FFileItemId_p; +typedef RFileItemId * RFileItemId_p; + +/************************************************************************** +Struct: LOCK_WAITER (Lock Waiter) +Desc: This structure is used to keep track of threads waiting for a + lock. +**************************************************************************/ +typedef struct Lock_Waiter * LOCK_WAITER_p; + +typedef struct Lock_Waiter +{ + ServerLockObject_p pLockObject; // Pointer to lock object. + FLMUINT uiThreadId; // Thread of waiter + F_SEM hESem; // Semaphore to signal when lock is + // granted (or denied). + RCODE * pRc; // Pointer to return code that is to + // be set when lock is granted or + // denied. + FLMUINT uiWaitStartTime; + // Time we started waiting. + FLMUINT uiWaitTime; // Time pending lock request should + // wait before being timed out. + // Zero means should not be timed out. + FLMBOOL bExclReq; // TRUE if exclusive lock request. + FLMINT iPriority; // Priority of waiter. + F_TMSTAMP StartTime; // Time we started waiting (for stats) + XFLM_DB_STATS * pDbStats; // Statistics to update. + LOCK_WAITER_p pNext; // Next lock waiter in list. + LOCK_WAITER_p pPrev; // Previous lock waiter in list. + LOCK_WAITER_p pNextGlobal; // Next lock waiter in global list + // that is ordered according to + // udWaitEndTime. + LOCK_WAITER_p pPrevGlobal; // Previous lock waiter in global list +} LOCK_WAITER; + +/*=========================================================================== +Class: ServerLockManager +Desc: The ServerLockManager class manages ServerLockObject objects. +===========================================================================*/ + +class ServerLockManager : public XF_RefCount, public XF_Base +{ +public: + + ServerLockManager() + { + m_hMutex = F_MUTEX_NULL; + m_pFirstLockWaiter = NULL; + m_pHashTbl = NULL; + m_uiNumAvail = 0; + m_pAvailLockList = NULL; + } + + virtual ~ServerLockManager(); // ServerLockManager Destructor - free + // ServerLockObjects owned by this + // ServerLockManager. + + FINLINE RCODE setupLockMgr( void) + { + return( f_mutexCreate( &m_hMutex)); + } + + RCODE SetupHashTbl(); // Setup hash table for lock manager. + + void CheckLockTimeouts( // See if any pending lock requests have + FLMBOOL bMutexAlreadyLocked, + FLMBOOL bTimeoutAll); // timed out. + + void InsertWaiter( // Insert waiter into global list. + LOCK_WAITER * pLockWaiter); + + FINLINE void RemoveWaiter( + LOCK_WAITER * pLockWaiter) + { + if (pLockWaiter->pNextGlobal) + pLockWaiter->pNextGlobal->pPrevGlobal = pLockWaiter->pPrevGlobal; + + if (pLockWaiter->pPrevGlobal) + { + pLockWaiter->pPrevGlobal->pNextGlobal = pLockWaiter->pNextGlobal; + } + else + { + m_pFirstLockWaiter = pLockWaiter->pNextGlobal; + } + } + + ServerLockObject_p GetLockObject(// Return a lock object for the file. + F_ItemId * pItemId); + + void SignalLockWaiter( // Unlink a lock object from lists + FLMUINT uiThreadId); + + void UnlinkLockObject( // Unlink a lock object from lists + ServerLockObject * pLockObject, + FLMBOOL bPutInAvailList); + + 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); + } + } + +private: + + // Private variables + + F_MUTEX m_hMutex; + FBUCKET * m_pHashTbl; // Hash table. + LOCK_WAITER * m_pFirstLockWaiter; // Pointer to first in list of global + // lock waiters. + FLMUINT m_uiNumAvail; // Number of lock objects in avail + // list. + ServerLockObject * + m_pAvailLockList; // List of available lock objects. + +friend class F_ServerLockMgrPage; +friend class ServerLockObject; + +}; + +/*=========================================================================== +Class: F_ItemId +Desc: The item id that identifies a particular object. +===========================================================================*/ +class F_ItemId : public XF_RefCount, public XF_Base +{ +public: + F_ItemId(); // F_ItemId Constructor + + virtual ~F_ItemId(); // F_ItemId Destructor + + virtual FLMBOOL IsEqual( // Compare to another F_ItemId. + F_ItemId * pItemId) = 0; + + virtual FLMUINT GetHashBucket( // Get hash bucket for lock item id + FBUCKET * pHashTbl, + FLMUINT uiHashTblSize) = 0; + + FLMUINT GetItemType() // Returns the type of item. + { return m_uiItemType; } + +protected: + FLMUINT m_uiItemType; // Item type. +#define FFILE_ITEM 1 +#define RFILE_ITEM 2 +#define FFILE_TRANS_ITEM 3 +#define RFILE_TRANS_ITEM 4 +}; + + + /* + Public: constructor, destructor + */ + FINLINE F_ItemId::F_ItemId() + { + m_uiItemType = 0; + } + + FINLINE F_ItemId::~F_ItemId() + { + } + +/*=========================================================================== +Class: FFileItemId +Desc: The item id that identifies an FFILE object. +===========================================================================*/ +class FFileItemId : public F_ItemId +{ +public: + + // Constructor + + FFileItemId( + F_Database * pDatabase, + FLMBOOL bTrans) + { + m_pDatabase = pDatabase; + m_uiItemType = (FLMUINT)(bTrans + ? (FLMUINT)FFILE_TRANS_ITEM + : (FLMUINT)FFILE_ITEM); + } + + virtual ~FFileItemId() + { + } + + FLMBOOL IsEqual( // Compare to another F_ItemId. + F_ItemId * pItemId); + + FINLINE FLMUINT GetHashBucket( + FBUCKET * pHashTbl, + FLMUINT uiHashTblSize) + { + char szFileName[ F_PATH_MAX_SIZE]; + + // Extract the file name + + this->GetFileName( szFileName); + + // Determine what hash bucket the file should be in - based on file name. + + return( flmStrHashBucket( szFileName, pHashTbl, uiHashTblSize)); + } + + FINLINE F_Database * getDatabase( void) // was GetFilePtr + { + return m_pDatabase; + } + + void GetFileName( // Returns file name + char * pszFileNameRV); + +private: + F_Database * m_pDatabase; +}; + +/*=========================================================================== +Class: RFileItemId +Desc: The item id that identifies a file being used by rebuild. +===========================================================================*/ +class RFileItemId : public F_ItemId +{ +public: + RFileItemId( // RFileItemId Constructor + char * pszFileName, + FLMBOOL bTrans = FALSE); + + virtual ~RFileItemId() // RFileItemId Destructor + { + } + + FLMBOOL IsEqual( // Compare to another F_ItemId. + F_ItemId * pItemId); + + FINLINE FLMUINT GetHashBucket( // Get hash bucket for lock item id + FBUCKET * pHashTbl, + FLMUINT uiHashTblSize) + { + // Determine what hash bucket the file should be in - based on file name. + + return( flmStrHashBucket( m_szFileName, pHashTbl, uiHashTblSize)); + } + + FINLINE void GetFileName( // Returns file name + char * pszFileNameRV) + { + f_strcpy( pszFileNameRV, m_szFileName); + } + +private: + char m_szFileName [F_PATH_MAX_SIZE]; // File's name. +}; + +/*=========================================================================== +Desc: The ServerLockObject is used to lock and unlock a particular + object. +===========================================================================*/ +class ServerLockObject : public XF_RefCount, public XF_Base +{ +public: + ServerLockObject(); // ServerLockObject Constructor + + virtual ~ServerLockObject() + { + if( m_pItemId) + { + m_pItemId->Release(); + } + } + + void Setup( + ServerLockManager * pServerLockMgr, + F_ItemId * pItemId, + FLMUINT uiBucket); + + RCODE Lock( + F_Db * pDb, + F_SEM hWaitSem, + FLMBOOL bLogEvent, + FLMBOOL bSendSuspendEvent, // Send suspend event when waiting + // for lock. + FLMBOOL bExclLock, // Exclusive or shared lock? + FLMUINT uiMaxWaitSecs, // Maximum wait time in seconds. + FLMINT iPriority, // Lock priority + XFLM_DB_STATS * pDbStats = NULL); // Place to gather DB stats. + + RCODE Unlock( + FLMBOOL bLogEvent, + F_Db * pDb, // used for event callbacks. + FLMBOOL bRelease = FALSE, // Release object if no one is waiting + XFLM_DB_STATS * pDbStats = NULL); // Place to gather DB stats. + + FLMUINT Release( + FLMBOOL bMutexAlreadyLocked); + // Decrement ref count, when it gets + // down to 1, put it in the avail list. + + FLMUINT32 XFLMAPI Release( void) + { + return( (FLMUINT32)Release( FALSE)); + } + + void RemoveWaiter( + LOCK_WAITER * pLockWaiter); + + FLMBOOL ThreadWaitingLock( void) + { + return( ((m_pFirstLockWaiter) ? (FLMBOOL)TRUE : (FLMBOOL)FALSE) ); + } + + FLMUINT LockCnt( void) + { + return( m_uiLockCnt); + } + + void GetLockInfo( // Returns lock information + FLMINT iPriority, // A count of all lock requests with + // a priority >= to this will be returned + // in pLockInfo. + eDbLockType * peCurrLockType, + FLMUINT * puiThreadId, + FLMUINT * puiNumExclQueued, + FLMUINT * puiNumSharedQueued, + FLMUINT * puiPriorityCount); + + RCODE GetLockInfo( + IF_LockInfoClient * pLockInfo); + + FLMBOOL haveHigherPriorityWaiter( + FLMINT iPriority); + +private: + + ServerLockManager * m_pServerLockMgr; // Server Lock Manager pointer. + F_ItemId * m_pItemId; // ID for object this lock + // object represents + FLMUINT m_uiLockThreadId; // Thread of thread that has this + // object locked. Zero if none. + FLMUINT m_uiLockTime; // Time lock was granted, if + // exclusive lock. + FLMUINT m_uiLockCnt; // Number of locks that have been + // granted thus far. + LOCK_WAITER * m_pFirstLockWaiter; + // Pointer to first in list of + // lock waiters. + LOCK_WAITER * m_pLastLockWaiter;// Pointer to last in list of + // lock waiters. + FLMUINT m_uiNumWaiters; // Number of threads waiting. + ServerLockObject * m_pNext; // Next in hash bucket or avail list + ServerLockObject * m_pPrev; // Prev in hash bucket or avail list + FLMUINT m_uiSharedLockCnt;// Number of shared locks that have + // been granted. + FLMBOOL m_bExclLock; // Is the granted lock exclusive? + FLMUINT m_uiBucket; // Hash bucket this object is in. + // 0xFFFF means it is in avail list. + F_TMSTAMP m_StartTime; // Time exclusive lock was grabbed. + FLMBOOL m_bStartTimeSet; // Was m_StartTime set? + +friend class ServerLockManager; + +}; + +#endif // FSRVLOCK_H diff --git a/version5/src/fstream.cpp b/version5/src/fstream.cpp new file mode 100644 index 0000000..4f67c36 --- /dev/null +++ b/version5/src/fstream.cpp @@ -0,0 +1,2685 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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() +{ + m_bLockedModule = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IStream::~F_IStream() +{ + if( m_bLockedModule) + { + UnlockModule(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_IStream::lockModule( void) +{ + if( !m_bLockedModule) + { + LockModule(); + m_bLockedModule = TRUE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_PosIStream::F_PosIStream() +{ + m_bLockedModule = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_PosIStream::~F_PosIStream() +{ + if( m_bLockedModule) + { + UnlockModule(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_PosIStream::lockModule( void) +{ + if( !m_bLockedModule) + { + LockModule(); + m_bLockedModule = TRUE; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_OStream::F_OStream() +{ + m_bLockedModule = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_OStream::~F_OStream() +{ + if( m_bLockedModule) + { + UnlockModule(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_OStream::lockModule( void) +{ + if( !m_bLockedModule) + { + LockModule(); + m_bLockedModule = TRUE; + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_FileIStream::open( + const char * pszFilePath) +{ + RCODE rc = NE_XFLM_OK; + + close(); + + if( RC_BAD( rc = gv_pFileSystem->Open( (char *)pszFilePath, + XFLM_IO_RDONLY | XFLM_IO_SH_DENYNONE, &m_pFileHdl))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Closes the input stream and frees any resources +*****************************************************************************/ +void F_FileIStream::close( void) +{ + if( m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + m_ui64FileOffset = 0; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 XFLMAPI F_FileIStream::totalSize( void) +{ + FLMUINT64 ui64FileSize = 0; + + (void)m_pFileHdl->Size( &ui64FileSize); + return( ui64FileSize); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 XFLMAPI F_FileIStream::remainingSize( void) +{ + FLMUINT64 ui64TotalSize = totalSize(); + FLMUINT64 ui64Offset = getCurrPosition(); + + if( ui64TotalSize >= ui64Offset) + { + return( ui64TotalSize - ui64Offset); + } + + return( 0); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMUINT64 XFLMAPI F_FileIStream::getCurrPosition( void) +{ + return( m_ui64FileOffset); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_FileIStream::positionTo( + FLMUINT64 ui64Offset) +{ + m_ui64FileOffset = ui64Offset; + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Reads the requested amount of data from the stream. +*****************************************************************************/ +RCODE F_FileIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead = 0; + + if( !m_pFileHdl) + { + rc = RC_SET( NE_XFLM_READING_FILE); + goto Exit; + } + + rc = m_pFileHdl->Read( m_ui64FileOffset, uiBytesToRead, + pvBuffer, &uiBytesRead); + m_ui64FileOffset += uiBytesRead; + + if( RC_BAD( rc)) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + +Exit: + + if( puiBytesRead) + { + *puiBytesRead = uiBytesRead; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_BufferedIStream::open( + IF_IStream * pIStream, + FLMUINT uiBufferSize) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_BufferedIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesRead = 0; + FLMUINT uiMaxSize; + + if (!m_pIStream) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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: +*****************************************************************************/ +void XFLMAPI F_BufferedIStream::close( void) +{ + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + 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; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_FileOStream::open( + const char * pszFilePath, + FLMBOOL bTruncateIfExists) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pFileHdl) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if( bTruncateIfExists) + { + if( RC_BAD( rc = gv_pFileSystem->Delete( (char *)pszFilePath))) + { + if( rc != NE_XFLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + } + + if( RC_BAD( rc = gv_pFileSystem->Create( + (char *)pszFilePath, XFLM_IO_RDWR, &m_pFileHdl))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = gv_pFileSystem->Open( + (char *)pszFilePath, XFLM_IO_RDWR, &m_pFileHdl))) + { + if( rc != NE_XFLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + if( RC_BAD( rc = gv_pFileSystem->Create( + (char *)pszFilePath, XFLM_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 XFLMAPI F_FileOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesWritten = 0; + + if (!m_pFileHdl) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_FileOStream::close( void) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pFileHdl) + { + m_pFileHdl->Release(); + m_pFileHdl = NULL; + } + + m_ui64FileOffset = 0; + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_MultiFileIStream::open( + const char * pszDirectory, + const char * pszBaseName) +{ + RCODE rc = NE_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_OK; + F_FileIStream * pFileIStream = NULL; + F_BufferedIStream * pBufferedIStream = NULL; + FLMUINT uiNewFileNum = 0; + FLMBYTE szFilePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE 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_XFLM_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_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pFileIStream->open( (const char *)szFilePath))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + m_bEndOfStream = TRUE; + rc = RC_SET( NE_XFLM_EOF_HIT); + } + goto Exit; + } + + if( (pBufferedIStream = f_new F_BufferedIStream) == NULL) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_MultiFileIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTmpRead; + FLMUINT uiTotalRead = 0; + FLMBOOL bRollToNextFile = FALSE; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if( !m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + if (m_bEndOfStream) + { + rc = RC_SET( NE_XFLM_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_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + bRollToNextFile = TRUE; + if (!uiTmpRead) + { + continue; + } + } + + pucBuffer += uiTmpRead; + uiBytesToRead -= uiTmpRead; + uiTotalRead += uiTmpRead; + m_ui64FileOffset += uiTmpRead; + } + +Exit: + + if (puiBytesRead) + { + *puiBytesRead = uiTotalRead; + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +void XFLMAPI 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; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileOStream::create( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOkToOverwrite) +{ + RCODE rc = NE_XFLM_OK; + + if( m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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_XFLM_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_XFLM_IO_NO_MORE_FILES) + { + goto Exit; + } + + rc = NE_XFLM_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_XFLM_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->Delete( szFilePath))) + { + if (rc != NE_XFLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + } + } + } + +Exit: + + if( pDirHandle) + { + pDirHandle->Release(); + pDirHandle = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DbSystem::removeMultiFileStream( + const char * pszDirectory, + const char * pszBaseName) +{ + RCODE rc = NE_XFLM_OK; + F_MultiFileOStream * pMultiStream = NULL; + + if( (pMultiStream = f_new F_MultiFileOStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pMultiStream->processDirectory( + pszDirectory, pszBaseName, TRUE))) + { + goto Exit; + } + +Exit: + + if( pMultiStream) + { + pMultiStream->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_MultiFileOStream::rollToNextFile( void) +{ + RCODE rc = NE_XFLM_OK; + F_FileOStream * pFileOStream = NULL; + F_BufferedOStream * pBufferedOStream = NULL; + FLMUINT uiNewFileNum = 0; + FLMBYTE szFilePath[ F_PATH_MAX_SIZE + 1]; + FLMBYTE 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_XFLM_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_XFLM_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_XFLM_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 XFLMAPI F_MultiFileOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiMaxToWrite; + FLMUINT uiBytesWritten = 0; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + if (!m_bOpen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_MultiFileOStream::close( void) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_BufferedOStream::open( + IF_OStream * pOStream, + FLMUINT uiBufferSize) +{ + RCODE rc = NE_XFLM_OK; + + if( !pOStream || m_pOStream || !uiBufferSize) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_BufferedOStream::flush( void) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_BufferedOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_BufferedOStream::close( void) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_BufferIStream::open( + const FLMBYTE * pucBuffer, + FLMUINT uiLength, + FLMBYTE ** ppucAllocatedBuffer) +{ + RCODE rc = NE_XFLM_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: +******************************************************************************/ +void XFLMAPI 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; + } +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_BufferIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_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_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_Base64DecoderIStream::open( + IF_IStream * pIStream) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_Base64DecoderIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMBYTE ucQuadBuffer[ 4]; + FLMUINT uiOffset; + FLMUINT uiBytesToCopy; + + if( puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_pIStream) + { + rc = RC_SET( NE_XFLM_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_XFLM_EOF_HIT) + { + goto Exit; + } + + if( uiOffset) + { + rc = RC_SET( NE_XFLM_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_XFLM_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 XFLMAPI F_Base64EncoderIStream::open( + IF_IStream * pIStream, + FLMBOOL bLineBreaks) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pIStream || !pIStream) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_Base64EncoderIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucOutBuf = (FLMBYTE *)pvBuffer; + FLMUINT uiBytesToCopy; + FLMUINT uiBytesToEncode; + FLMBYTE ucTriBuffer[ 3]; + + if( *puiBytesRead) + { + *puiBytesRead = 0; + } + + if( !m_pIStream) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + while( uiBytesToRead) + { + if( !m_uiAvailBytes) + { + m_uiBufOffset = 0; + + if( m_bInputExhausted) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + if( RC_BAD( rc = m_pIStream->read( ucTriBuffer, + 3, &uiBytesToEncode))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_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_XFLM_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 XFLMAPI F_CompressingOStream::open( + IF_OStream * pOStream) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_CompressingOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_CompressingOStream::close( void) +{ + RCODE rc = NE_XFLM_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 XFLMAPI F_UncompressingIStream::open( + IF_IStream * pIStream) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_OK; + FLMBYTE ucInBuf[ 2]; + + if( m_bEndOfStream) + { + rc = RC_SET( NE_XFLM_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_XFLM_EOF_HIT); + goto Exit; + } + +Exit: + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_UncompressingIStream::decodeToBuffer( + FLMUINT16 ui16Code) +{ + RCODE rc = NE_XFLM_OK; + + if( ui16Code >= m_ui16FreeCode || m_ui16LastCode == LZW_END_OF_DATA) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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 XFLMAPI F_UncompressingIStream::read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_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_XFLM_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: +******************************************************************************/ +void XFLMAPI 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); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DbSystem::openBufferIStream( + const char * pucBuffer, + FLMUINT uiLength, + IF_PosIStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_BufferIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( (FLMBYTE *)pucBuffer, uiLength))) + { + goto Exit; + } + + if( *ppIStream) + { + (*ppIStream)->Release(); + } + + pNewIStream->lockModule(); + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return an input stream that reads from multiple files. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openMultiFileIStream( + const char * pszDirectory, + const char * pszBaseName, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_MultiFileIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_MultiFileIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pszDirectory, pszBaseName))) + { + goto Exit; + } + + pNewIStream->lockModule(); + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return a buffered input stream from pIStream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openBufferedIStream( + IF_IStream * pIStream, + FLMUINT uiBufferSize, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_BufferedIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_BufferedIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pIStream, uiBufferSize))) + { + goto Exit; + } + + pNewIStream->lockModule(); + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openUncompressingIStream( + IF_IStream * pIStream, + IF_IStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_UncompressingIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_UncompressingIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pIStream))) + { + goto Exit; + } + + pNewIStream->lockModule(); + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return a compressing output stream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openCompressingOStream( + IF_OStream * pOStream, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_XFLM_OK; + F_CompressingOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_CompressingOStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pOStream))) + { + goto Exit; + } + + pNewOStream->lockModule(); + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return a file output stream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openFileOStream( + const char * pszFileName, + FLMBOOL bTruncateIfExists, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_XFLM_OK; + F_FileOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_FileOStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pszFileName, bTruncateIfExists))) + { + goto Exit; + } + + pNewOStream->lockModule(); + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return a multi-file output stream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openMultiFileOStream( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOverwrite, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_XFLM_OK; + F_MultiFileOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_MultiFileOStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->create( pszDirectory, pszBaseName, + uiMaxFileSize, bOverwrite))) + { + goto Exit; + } + + pNewOStream->lockModule(); + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Return a buffered output stream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openBufferedOStream( + IF_OStream * pOStream, + FLMUINT uiBufferSize, + IF_OStream ** ppOStream) +{ + RCODE rc = NE_XFLM_OK; + F_BufferedOStream * pNewOStream = NULL; + + if( (pNewOStream = f_new F_BufferedOStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewOStream->open( pOStream, uiBufferSize))) + { + goto Exit; + } + + pNewOStream->lockModule(); + *ppOStream = pNewOStream; + pNewOStream = NULL; + +Exit: + + if( pNewOStream) + { + pNewOStream->Release(); + } + + return( rc); +} + +/****************************************************************************** +Desc: Read all data from input stream and write to the output stream. +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::writeToOStream( + IF_IStream * pIStream, + IF_OStream * pOStream) +{ + RCODE rc = NE_XFLM_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_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + if (!uiBytesRead) + { + goto Exit; + } + } + + uiBytesToWrite = uiBytesRead; + if( RC_BAD( rc = pOStream->write( ucBuffer, uiBytesToWrite))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE XFLMAPI F_DbSystem::openFileIStream( + const char * pszPath, + IF_PosIStream ** ppIStream) +{ + RCODE rc = NE_XFLM_OK; + F_FileIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_FileIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pszPath))) + { + goto Exit; + } + + if( *ppIStream) + { + (*ppIStream)->Release(); + } + + pNewIStream->lockModule(); + *ppIStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openBase64Encoder( + IF_IStream * pInputStream, + FLMBOOL bInsertLineBreaks, + IF_IStream ** ppEncodedStream) +{ + RCODE rc = NE_XFLM_OK; + F_Base64EncoderIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_Base64EncoderIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pInputStream, bInsertLineBreaks))) + { + goto Exit; + } + + if( *ppEncodedStream) + { + (*ppEncodedStream)->Release(); + } + + pNewIStream->lockModule(); + *ppEncodedStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE XFLMAPI F_DbSystem::openBase64Decoder( + IF_IStream * pInputStream, + IF_IStream ** ppDecodedStream) +{ + RCODE rc = NE_XFLM_OK; + F_Base64DecoderIStream * pNewIStream = NULL; + + if( (pNewIStream = f_new F_Base64DecoderIStream) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNewIStream->open( pInputStream))) + { + goto Exit; + } + + if( *ppDecodedStream) + { + (*ppDecodedStream)->Release(); + } + + pNewIStream->lockModule(); + *ppDecodedStream = pNewIStream; + pNewIStream = NULL; + +Exit: + + if( pNewIStream) + { + pNewIStream->Release(); + } + + return( rc); +} diff --git a/version5/src/fstructs.h b/version5/src/fstructs.h new file mode 100644 index 0000000..c6ae058 --- /dev/null +++ b/version5/src/fstructs.h @@ -0,0 +1,1802 @@ +//------------------------------------------------------------------------------ +// Desc: Various structures and classes used internally by FLAIM. +// +// 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: fstructs.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FSTRUCTS_H +#define FSTRUCTS_H + +#if defined( FLM_WIN) || defined( FLM_NLM) || defined( FLM_LINUX) + #pragma pack( push, 1) +#else + #pragma pack( 1) +#endif + +class HRequest; +class F_BtPool; +class F_XMLImport; +class F_XMLExport; +class F_FileIdList; +class F_Rfl; +class F_SuperFileHdl; +class F_Btree; +class F_DbRebuild; +class F_DbCheck; +class F_XML; +class ServerLockManager; +class ServerLockObject; +class F_FileHdlMgr; +class F_ThreadMgr; +class F_Cursor; +class F_MultiAlloc; +class F_CachedNode; +class F_CachedBlock; +class F_BlockCacheMgr; +class F_NodeCacheMgr; +class F_GlobalCacheMgr; +class F_NodePool; +class F_BTreeInfo; +class F_NodeRelocator; +class F_NodeDataRelocator; +class F_NodeListRelocator; +class F_AttrItemRelocator; +class F_AttrBufferRelocator; + +#if defined( FLM_NLM) + + extern "C" + { + void ConvertTicksToSeconds( + LONG ticks, + LONG * seconds, + LONG * tenthsOfSeconds); + + 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)) +#else /* 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: Tests to see if database is NOT in native platform format. +****************************************************************************/ +FINLINE FLMBOOL hdrIsNonNativeFormat( + XFLM_DB_HDR * pDbHdr) +{ + return( (FLMBOOL)(pDbHdr->ui8IsLittleEndian == + XFLM_NATIVE_IS_LITTLE_ENDIAN + ? (FLMBOOL)FALSE + : (FLMBOOL)TRUE)); +} + +/**************************************************************************** +Desc: Block header - on-disk format. +****************************************************************************/ +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; + +#define SIZEOF_STD_BLK_HDR sizeof( F_BLK_HDR) + +/**************************************************************************** +Desc: Test to see if block is in non-native format. +****************************************************************************/ +FINLINE FLMBOOL blkIsNonNativeFormat( + F_BLK_HDR * pBlkHdr) +{ +#if XFLM_NATIVE_IS_LITTLE_ENDIAN + return( (pBlkHdr->ui8BlkFlags & BLK_FORMAT_IS_LITTLE_ENDIAN) + ? FALSE + : TRUE); +#else + return( (pBlkHdr->ui8BlkFlags & BLK_FORMAT_IS_LITTLE_ENDIAN) + ? TRUE + : FALSE); +#endif +} + +/**************************************************************************** +Desc: Set block flags to indicate it is in native format. +****************************************************************************/ +FINLINE void blkSetNativeFormat( + F_BLK_HDR * pBlkHdr) +{ +#if XFLM_NATIVE_IS_LITTLE_ENDIAN + pBlkHdr->ui8BlkFlags |= BLK_FORMAT_IS_LITTLE_ENDIAN; +#else + pBlkHdr->ui8BlkFlags &= ~(BLK_FORMAT_IS_LITTLE_ENDIAN); +#endif +} + +/**************************************************************************** +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: Test to see if a block type is a NEW B-Tree block type. +****************************************************************************/ +FINLINE FLMBOOL blkIsNewBTree( + F_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BlkType >= BT_LEAF) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: Determine where the block ends. +****************************************************************************/ +FINLINE FLMUINT blkGetEnd( + FLMUINT uiBlockSize, + FLMUINT uiBlkHdrSize, + F_BLK_HDR * pBlkHdr + ) +{ + return( (FLMUINT)(blkIsNewBTree( pBlkHdr) + ? uiBlockSize + : (FLMUINT)(pBlkHdr->ui16BlkBytesAvail > + uiBlockSize - uiBlkHdrSize + ? uiBlkHdrSize + : uiBlockSize - + (FLMUINT)pBlkHdr->ui16BlkBytesAvail))); +} + +FINLINE void setBlockEncrypted( + F_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BlkFlags |= BLK_IS_ENCRYPTED; +} + +FINLINE void unsetBlockEncrypted( + F_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BlkFlags &= (~(BLK_IS_ENCRYPTED)); +} + +FINLINE FLMBOOL isEncryptedBlk( + F_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BlkFlags & BLK_IS_ENCRYPTED) ? TRUE : FALSE); +} + +/**************************************************************************** +Desc: B-Tree block header - on-disk format. +****************************************************************************/ +typedef struct FlmBTreeBlkHdr +{ + F_BLK_HDR stdBlkHdr; // Standard block header + FLMUINT16 ui16LogicalFile; // 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_ui16LogicalFile_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; + +/**************************************************************************** +Desc: Encrypted B-Tree block header - on-disk format. +****************************************************************************/ +typedef struct +{ + F_BTREE_BLK_HDR btree; + FLMUINT64 ui64Reserved; // Reserving 8 bytes to ensure the data segment + // of the block is sized to an even 16 byte boundary + // as needed by encryption. +} F_ENC_BTREE_BLK_HDR; + +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: Encrypted Data-only block header - on-disk format. +****************************************************************************/ +typedef struct +{ + F_BLK_HDR blk; + FLMUINT32 ui32EncId; + FLMBYTE ucReserved[12]; // Reserving 12 bytes to ensure the data segment + // of the block is sized to an even 16 byte boundary + // as needed by encryption. +} F_ENC_DO_BLK_HDR; + +FINLINE FLMUINT sizeofDOBlkHdr( + F_BLK_HDR * pBlkHdr + ) +{ + if (!isEncryptedBlk( pBlkHdr)) + { + return sizeof( F_BLK_HDR); + } + else + { + return sizeof( F_ENC_DO_BLK_HDR); + } +} + +FINLINE FLMBOOL isRootBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BTreeFlags & BLK_IS_ROOT) ? TRUE : FALSE); +} + +FINLINE void setRootBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BTreeFlags |= BLK_IS_ROOT; +} + +FINLINE void unsetRootBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_ROOT)); +} + +FINLINE FLMBOOL isIndexBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) ? TRUE : FALSE); +} + +FINLINE eLFileType getBlkLfType( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + return( (eLFileType)((pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) + ? (eLFileType)XFLM_LF_INDEX + : (eLFileType)XFLM_LF_COLLECTION)); +} + +FINLINE void setBlkLfType( + F_BTREE_BLK_HDR * pBlkHdr, + FLMUINT uiLfType + ) +{ + if (uiLfType == XFLM_LF_COLLECTION) + { + pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_INDEX)); + } + else + { + pBlkHdr->ui8BTreeFlags |= BLK_IS_INDEX; + } +} + +FINLINE FLMBOOL isContainerBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BTreeFlags & BLK_IS_INDEX) ? FALSE : TRUE); +} + +FINLINE void setIndexBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BTreeFlags |= BLK_IS_INDEX; +} + +FINLINE void setContainerBlk( + F_BTREE_BLK_HDR * pBlkHdr + ) +{ + pBlkHdr->ui8BTreeFlags &= (~(BLK_IS_INDEX)); +} + +/**************************************************************************** +Desc: Get block header size. +****************************************************************************/ +FINLINE FLMUINT blkHdrSize( + F_BLK_HDR * pBlkHdr + ) +{ + return( (pBlkHdr->ui8BlkType != BT_FREE && + pBlkHdr->ui8BlkType != BT_LFH_BLK) + ? (pBlkHdr->ui8BlkType == BT_DATA_ONLY + ? sizeofDOBlkHdr( pBlkHdr) + : sizeofBTreeBlkHdr((F_BTREE_BLK_HDR *)pBlkHdr)) + : SIZEOF_STD_BLK_HDR); +} + +/**************************************************************************** +Desc: This is a union of all block header types - so that we can have + something that gives us the largest block header type in one + structure. Reduce uses this. +****************************************************************************/ +typedef struct FlmLargestBlkHdr +{ + union + { + F_BLK_HDR stdBlkHdr; + F_BTREE_BLK_HDR BTreeBlkHdr; + } all; +} F_LARGEST_BLK_HDR; + +#define SIZEOF_LARGEST_BLK_HDR sizeof( F_LARGEST_BLK_HDR) + +/**************************************************************************** +Desc: Logical File (b-tree) header - on-disk format. +****************************************************************************/ +typedef struct FlmLfHdrTag +{ + FLMUINT32 ui32LfNumber; + FLMUINT32 ui32LfType; + FLMUINT32 ui32RootBlkAddr; + FLMUINT32 ui32EncId; + FLMUINT64 ui64NextNodeId; + FLMUINT64 ui64FirstDocId; + FLMUINT64 ui64LastDocId; + FLMBYTE ucZeroes[ 24]; // Reserve 24 bytes of zero. + + // IMPORTANT NOTE: If anything is changed in here, need to make + // corresponding changes to convertLfHdr routine and + // flmVerifyDiskStructOffsets routine. + +#define F_LF_HDR_ui32LfNumber_OFFSET 0 +#define F_LF_HDR_ui32LfType_OFFSET 4 +#define F_LF_HDR_ui32RootBlkAddr_OFFSET 8 +#define F_LF_HDR_ui32EncId_OFFSET 12 +#define F_LF_HDR_ui64NextNodeId_OFFSET 16 +#define F_LF_HDR_ui64FirstDocId_OFFSET 24 +#define F_LF_HDR_ui64LastDocId_OFFSET 32 +#define F_LF_HDR_ucZeroes_OFFSET 40 +} F_LF_HDR; + +typedef struct +{ + FLMUINT uiCollection; + FLMUINT64 ui64Document; + FLMUINT64 ui64NodeId; +} NODE_LIST_ITEM; + +/**************************************************************************** +Desc: Node list +****************************************************************************/ +class F_NodeList : public XF_RefCount, public XF_Base +{ +public: + + F_NodeList() + { + m_pNodeTbl = NULL; + m_uiNodeTblSize = 0; + clearNodes(); + } + + ~F_NodeList() + { + if (m_pNodeTbl) + { + f_free( &m_pNodeTbl); + } + } + + FINLINE void clearNodes( void) + { + m_uiNumNodes = 0; + m_uiLastPosition = 0; + m_uiLastCollection = 0; + m_ui64LastDocument = 0; + m_ui64LastNodeId = 0; + } + + RCODE addNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId); + + FINLINE void removeNode( + FLMUINT uiPosition) + { + if( uiPosition < m_uiNumNodes) + { + if( uiPosition < m_uiNumNodes - 1) + { + f_memmove( &m_pNodeTbl[ uiPosition], + &m_pNodeTbl[ uiPosition + 1], + sizeof( NODE_LIST_ITEM) * (m_uiNumNodes - uiPosition)); + } + m_uiNumNodes--; + } + + m_uiLastPosition = 0; + m_uiLastCollection = 0; + m_ui64LastDocument = 0; + m_ui64LastNodeId = 0; + } + + void removeNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId); + + FINLINE FLMBOOL isNodeInList( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId) + { + FLMUINT uiPos; + + return( findNode( uiCollection, ui64Document, ui64NodeId, &uiPos)); + } + + FLMBOOL findNode( + FLMUINT uiCollection, + FLMUINT64 ui64Document, + FLMUINT64 ui64NodeId, + FLMUINT * puiPos); + + FINLINE void getNode( + FLMUINT uiPosition, + FLMUINT * puiCollection, + FLMUINT64 * pui64Document, + FLMUINT64 * pui64NodeId) + { + if( uiPosition < m_uiNumNodes) + { + *puiCollection = m_pNodeTbl[ uiPosition].uiCollection; + *pui64Document = m_pNodeTbl[ uiPosition].ui64Document; + *pui64NodeId = m_pNodeTbl[ uiPosition].ui64NodeId; + } + else + { + *puiCollection = 0; + *pui64Document = 0; + *pui64NodeId = 0; + } + } + +private: + + NODE_LIST_ITEM * m_pNodeTbl; + FLMUINT m_uiNodeTblSize; + FLMUINT m_uiNumNodes; + FLMUINT m_uiLastPosition; + FLMUINT m_uiLastCollection; + FLMUINT64 m_ui64LastDocument; + FLMUINT64 m_ui64LastNodeId; + +friend class F_Db; +friend class F_Database; +friend class F_Dict; +}; + +/*************************************************************************** +Desc: This is the notify request structure. Notify requests are linked + off of open requests for files or read requests for files so that + when an operation is complete that multiple threads are waiting + on, all of them will be notified. +***************************************************************************/ +typedef struct FNotify +{ + FNotify * pNext; // Pointer to next FNOTIFY structure in list. + FLMUINT uiThreadId; // ID of thread requesting the notify + RCODE * pRc; // Pointer to a return code variable that is to + // be filled in when the operation is completed. + // The thread requesting notification supplies + // the return code variable to be filled in. + void * pvUserData; // Other user data that the notifier might use + // to transfer other information to the waiter. + F_SEM hSem; // Semaphore that will be signaled when the + // operation is complete. +} FNOTIFY; + +// Flags for the uiKrAction parameter - used in sorting/indexing. + +#define KREF_DEL_KEYS 0x01 +#define KREF_ADD_KEYS 0x02 +#define KREF_INDEXING_ONLY 0x04 +#define KREF_IN_MODIFY 0x10 +#define KREF_MISSING_KEYS_OK 0x20 + +// Maximum length of a key. + +#define MAX_KEY_SIZ 1024 +#define MAX_ID_SIZE 256 // Cannot be more than 256 because + // we can only use one byte to + // represent the total ID size. + +#define DEFAULT_KREF_TBL_SIZE 4096 +#define DEFAULT_KREF_POOL_BLOCK_SIZE 8192 + +/**************************************************************************** +Desc: This structure is used to sort keys before the keys are actually + added to an index. +****************************************************************************/ +typedef struct Kref_Entry +{ + FLMBOOL bDelete; // Delete the key if TRUE + FLMUINT uiSequence; // Sequence of updates within trans. + FLMUINT uiDataLen; // Data length for this entry. The + // data, if any, comes after the key. + // Note that there will be a null + // terminating byte between the key and + // the data. + + // Note: used uint16 below to reduce memory allocations. + + FLMUINT16 ui16IxNum; // Index number + FLMUINT16 ui16KeyLen; // Key Length for this entry. The key + // comes immediately after this structure. +} KREF_ENTRY; + +/**************************************************************************** +Desc: This is a temporary structure that is used when building compound + keys. +****************************************************************************/ +typedef struct Cdl +{ + F_DOMNode * pNode; // Node to be included in a compound key. + // May be NULL. + FLMUINT64 ui64ParentId; // If pNode is NULL, this is a place + // holder for a "missing" child node. We + // just need to remember the parent node id. + FLMBOOL bInNodeSubtree; // Is this node subordinate to the original + // node we are indexing on? + Cdl * pNext; // Pointer to the next CDL entry. +} CDL; + +/**************************************************************************** +Desc: This is a temporary structure that is used when building compound + keys. +****************************************************************************/ +typedef struct CdlHdrTag +{ + CDL * pCdlList; + FLMUINT64 ui64NodeId; +} CDL_HDR; + +/**************************************************************************** +Desc: This is a temporary structure that is used when building compound + keys. +****************************************************************************/ +typedef struct IxContextTag +{ + F_Pool * pPool; + CDL_HDR * pCdlTbl; + CDL * pCdlList; + IxContextTag * pNext; + IxContextTag * pPrev; +} IX_CONTEXT; + +typedef struct IxdFixupTag +{ + FLMUINT uiIndexNum; + FLMUINT64 ui64LastDocIndexed; + IxdFixupTag * pNext; +} IXD_FIXUP; + +#define FTHREAD_ACTION_IDLE 0 +#define FTHREAD_ACTION_INDEX_OFFLINE 1 + +/*************************************************************************** +Desc: Contains elements for passing parms into the background thread. +***************************************************************************/ +typedef struct F_BkgndIx +{ + F_Database * pDatabase; + FLMUINT uiIndexingAction; + XFLM_INDEX_STATUS indexStatus; + F_BkgndIx * pPrev; + F_BkgndIx * pNext; +} F_BKGND_IX; + +/**************************************************************************** +Desc: Structure used to pass information to the checkpoint thread for 3.x + databases. +****************************************************************************/ +typedef struct +{ + F_Database * pDatabase; + F_SuperFileHdl * pSFileHdl; + F_SEM hWaitSem; + XFLM_STATS Stats; + FLMBOOL bStatsInitialized; + FLMBOOL bShuttingDown; + FLMBOOL bDoingCheckpoint; + FLMUINT uiStartTime; + FLMBOOL bForcingCheckpoint; + FLMUINT uiForceCheckpointStartTime; + FLMINT iForceCheckpointReason; + FLMUINT uiLogBlocksWritten; + FLMBOOL bWritingDataBlocks; + FLMUINT uiDataBlocksWritten; + FLMUINT uiStartWaitTruncateTime; +} CP_INFO; + +#define MAX_WRITE_BUFFER_BYTES (4 * 1024 * 1024) +#define MAX_PENDING_WRITES (MAX_WRITE_BUFFER_BYTES / 4096) +#define MAX_LOG_BUFFER_SIZE (256 * 1024) + +typedef struct Tmp_Read_Stats +{ + XFLM_DISKIO_STAT BlockReads; // Statistics on block reads. + XFLM_DISKIO_STAT OldViewBlockReads; // Statistics on old view block + // reads. + FLMUINT uiBlockChkErrs; // Number of times we had + // check errors reading blocks. + FLMUINT uiOldViewBlockChkErrs; // Number of times we had + // check errors reading an + // old view of a block. +} TMP_READ_STATS, * TMP_READ_STATS_p; + +// Flags for F_Database->m_uiFlags + +#define DBF_BEING_OPENED 0x01 // Flag indicating whether this database is + // in the process of being opened. +#define DBF_BEING_CLOSED 0x02 // Database is being closed - cannot open. + +/***************************************************************************** +Desc: Shared database object - only to be used internally +*****************************************************************************/ +class F_Database : public XF_RefCount, public XF_Base +{ +public: + + F_Database( + FLMBOOL bTempDb); + + ~F_Database(); + + void freeDatabase( void); + + RCODE setupDatabase( + const char * pszDbPath, + const char * pszDataDir); + + RCODE dbWriteLock( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats = NULL, + FLMUINT uiTimeout = XFLM_NO_TIMEOUT); + + void dbWriteUnlock( + XFLM_DB_STATS * pDbStats = NULL); + + void shutdownDatabaseThreads( void); + + RCODE linkToBucket( void); // was flmLinkFileToBucket + + void setMustCloseFlags( // was flmSetMustCloseFlags + RCODE rcMustClose, + FLMBOOL bMutexLocked); + + FINLINE F_NameTable * getNameTable( void) + { + if( m_pDictList) + { + return( m_pDictList->getNameTable()); + } + + return( NULL); + } + + FINLINE RCODE checkState( + const char * pszFileName, + FLMINT iLineNumber) + + { + RCODE rc = NE_XFLM_OK; + + if (m_bMustClose) + { + logMustCloseReason( pszFileName, iLineNumber); + rc = RC_SET( NE_XFLM_MUST_CLOSE_DATABASE); + } + return( rc); + } + + RCODE startCPThread( void); + + FLMBOOL tryCheckpoint( + F_Thread * pThread, + CP_INFO * pCPInfo); + + void newDatabaseFinish( + RCODE OpenRc); + + RCODE getExclAccess( + const char * pszFilePath); + + RCODE verifyOkToUse( + FLMBOOL * pbWaited); + + RCODE writeDbHdr( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + XFLM_DB_HDR * pDbHdr, + XFLM_DB_HDR * pCPDbHdr, + FLMBOOL bIsCheckpoint); + + FINLINE char * getDbNamePtr( void) + { + return m_pszDbPath; + } + + FINLINE XFLM_DB_HDR * getUncommittedDbHdr( void) + { + return &m_uncommittedDbHdr; + } + + FINLINE FLMUINT getBlockSize( void) + { + return( m_uiBlockSize); + } + + FINLINE FLMUINT getMaxFileSize( void) + { + return( m_uiMaxFileSize); + } + + FINLINE FLMUINT getSigBitsInBlkSize( void) + { + return m_uiSigBitsInBlkSize; + } + + FINLINE F_CachedBlock * getTransLogList( void) + { + return m_pTransLogList; + } + + void releaseLogBlocks( void); + + FINLINE FLMUINT getDirtyCacheCount( void) + { + return m_uiDirtyCacheCount; + } + + FINLINE void incrementDirtyCacheCount( void) + { + m_uiDirtyCacheCount++; + } + + FINLINE void decrementDirtyCacheCount( void) + { + m_uiDirtyCacheCount--; + } + + FLMBOOL neededByReadTrans( + FLMUINT64 ui64LowTransId, + FLMUINT64 ui64HighTransId); + + RCODE getBlock( + F_Db * pDb, + LFILE * pLFile, + FLMUINT uiBlkAddress, + FLMUINT * puiNumLooks, + F_CachedBlock ** ppSCache); + + RCODE logPhysBlk( + F_Db * pDb, + F_CachedBlock ** ppSCache, + F_CachedBlock ** ppOldCache = NULL); + + RCODE createBlock( + F_Db * pDb, + F_CachedBlock ** ppSCache); + + RCODE blockUseNextAvail( + F_Db * pDb, + F_CachedBlock ** ppSCache); + + RCODE blockFree( + F_Db * pDb, + F_CachedBlock * pSCache); + + RCODE moveBtreeBlk( + F_Db * pDb, + FLMUINT uiBlkAddr, + FLMUINT uiLfNumber, + eLFileType eLfType); + + RCODE freeAvailBlk( + F_Db * pDb, + FLMUINT uiBlkAddr); + + RCODE moveLFHBlk( + F_Db * pDb, + FLMUINT uiBlkAddr); + + void unlinkTransLogBlocks( void); + + void freeBlockCache( void); + + void freeNodeCache( void); + + void freeModifiedBlocks( + FLMUINT64 ui64CurrTransId); + + void freeModifiedNodes( + F_Db * pDb, + FLMUINT64 ui64OlderTransId); + + void commitNodeCache( void); + + void getCPInfo( + XFLM_CHECKPOINT_INFO * pCheckpointInfo); + + FINLINE FLMUINT getFlags( void) + { + return( m_uiFlags); + } + + // NOTE: This routine expects that the global mutex is locked when + // it is called. + + FINLINE void incrOpenCount( void) + { + m_uiOpenIFDbCount++; + } + + // NOTE: This routine expects that the global mutex is locked when + // it is called. It may temporarily unlock the mutex (when it + // calls freeDatabase), but the mutex will be locked when it returns. + + FINLINE void decrOpenCount( void) + { + flmAssert( m_uiOpenIFDbCount); + m_uiOpenIFDbCount--; + if (!m_uiOpenIFDbCount) + { + freeDatabase(); + } + } + + RCODE startMaintThread( void); + + FINLINE FLMBOOL inLimitedMode() + { + return m_bInLimitedMode; + } + + RCODE encryptBlock( + F_Dict * pDict, + FLMBYTE * pucBuffer); + + RCODE decryptBlock( + F_Dict * pDict, + FLMBYTE * pucBuffer); + + FINLINE void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + FINLINE void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + +private: + + void logMustCloseReason( + const char * pszFileName, + FLMINT iLineNumber); + + RCODE readDbHdr( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBYTE * pszPassword, + FLMBOOL bAllowLimited); + + RCODE physOpen( + F_Db * pDb, + const char * pszFilePath, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiOpenFlags, + FLMBOOL bNewDatabase, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus); + + RCODE doRecover( + F_Db * pDb, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus); + + RCODE outputNode( + FLMBYTE * pucKeyBuf, + F_DOMNode * pNode, + FLMBOOL bAdd, + F_Btree * pBTree); + + RCODE readTheBlock( + F_Db * pDb, + TMP_READ_STATS * pTmpReadStats, + F_BLK_HDR * pBlkHdr, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress); + + RCODE readBlock( + F_Db * pDb, + LFILE * pLFile, + FLMUINT uiFilePos, + FLMUINT uiBlkAddress, + FLMUINT64 ui64NewerBlkLowTransID, + F_CachedBlock * pSCache, + FLMBOOL * pbFoundVerRV, + FLMBOOL * pbDiscardRV); + + RCODE readIntoCache( + F_Db * pDb, + LFILE * pLFile, + FLMUINT uiBlkAddress, + F_CachedBlock * pPrevInVerList, + F_CachedBlock * pNextInVerList, + F_CachedBlock ** ppSCacheRV, + FLMBOOL * pbGotFromDisk); + + void setBlkDirty( + F_CachedBlock * pSCache); + + RCODE allocBlocksArray( + FLMUINT uiNewSize, + FLMBOOL bOneArray); + + RCODE flushLogBlocks( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bIsCPThread, + FLMUINT uiMaxDirtyCache, + FLMBOOL * pbForceCheckpoint, + FLMBOOL * pbWroteAll); + + RCODE reduceNewBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT * puiBlocksFlushed); + + RCODE writeContiguousBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + F_IOBuffer * pIOBuffer, + FLMUINT uiBlkAddress, + FLMBOOL bDoAsync); + + RCODE writeSortedBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxDirtyCache, + FLMUINT * puiDirtyCacheLeft, + FLMBOOL * pbForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL bDoAsync, + FLMUINT uiNumSortedBlocks, + FLMBOOL * pbWroteAll); + + RCODE flushDirtyBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxDirtyCache, + FLMBOOL bForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL * pbWroteAll); + + RCODE reduceDirtyCache( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl); + + RCODE finishCheckpoint( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoTruncate, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset, + FLMUINT uiCPStartTime, + FLMUINT uiTotalToWrite); + + RCODE doCheckpoint( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoTruncate, + FLMBOOL bForceCheckpoint, + FLMINT iForceReason, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset); + + RCODE lgFlushLogBuffer( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoAsync); + + RCODE lgOutputBlock( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + F_CachedBlock * pLogBlock, + F_BLK_HDR * pBlkHdr, + FLMBOOL bDoAsync, + FLMUINT * puiLogEofRV); + + FLMUINT lFileFindEmpty( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr); + + RCODE lFileRead( + F_Db * pDb, + LFILE * pLFile, + F_COLLECTION * pCollection); + + RCODE lFileWrite( + F_Db * pDb, + F_COLLECTION * pCollection, + LFILE * pLFile); + + RCODE lFileCreate( + F_Db * pDb, + LFILE * pLFile, + F_COLLECTION * pCollection, + FLMUINT uiLfNum, + eLFileType eLfType, + FLMBOOL bCounts, + FLMBOOL bHaveData, + FLMUINT uiEncId = 0); + + RCODE startPendingInput( + FLMUINT uiPendingType, + F_CachedNode * pPendingNode); + + void endPendingInput( void); + + RCODE lFileDelete( + F_Db * pDb, + F_COLLECTION * pCollection, + LFILE * pLFile, + FLMBOOL bCounts, + FLMBOOL bHaveData); + + static RCODE maintenanceThread( + F_Thread * pThread); + + F_Database * m_pNext; // Next F_Database structure in in name hash + // bucket, dependent store hash + // bucket, or avail list. + F_Database * m_pPrev; // Previous F_Database structure in name hash + // bucket or dependent store hash + // bucket. + F_Query * m_pFirstQuery; // First query currently linked to this DB + F_Query * m_pLastQuery; // Last query currently linked to this DB + FLMUINT m_uiBlockSize; // Block size for database + FLMUINT m_uiDefaultLanguage; // Default language for database. + FLMUINT m_uiMaxFileSize; // Maximum file size for the database. + FLMUINT m_uiOpenIFDbCount; // Number of IF_Dbs currently using this + // database. Does NOT count internal uses + FLMBOOL m_bTempDb; // Is this a temporary database? If so, + // minimize writing out to disk. + F_Db * m_pFirstDb; // List of ALL F_Db's associated with + // this database. + char * m_pszDbPath; // Database file name. + char * m_pszDataDir; // Path for data files. + F_Database * m_pNextNUDatabase; // Next F_Database structure in list of + // unused databases. When use count goes + // to zero, the structure is linked + // into a list of unused databases off of + // the FSYSDATA structure. + F_Database * m_pPrevNUDatabase; // Previous F_Database structure in list of + // unused databases. + F_CachedBlock * m_pSCacheList; // This is a pointer to a linked list + // of all shared cache blocks + // belonging to this database. + F_CachedNode * m_pFirstNode; // First cached DOM node that belongs + // to this database. Also points to + // the first dirty DOM node, if any. + F_CachedNode * m_pLastNode; // Last cached DOM node that belongs + // to this database. + F_CachedNode * m_pLastDirtyNode; // Last dirty DOM node that belongs + // to this database. + F_CachedBlock * m_pPendingWriteList; // This is a pointer to a linked list + // of all shared cache blocks + // that are in the pending-write state. + F_CachedBlock * m_pLastDirtyBlk; // Pointer to last dirty block in the + // list. + F_CachedBlock * m_pFirstInLogList; // First block that needs to be logged + F_CachedBlock * m_pLastInLogList; // Last block that needs to be logged + FLMUINT m_uiLogListCount; // Number of items in the log list + F_CachedBlock * m_pFirstInNewList; // First new block that is dirty + F_CachedBlock * m_pLastInNewList; // Last new block that is dirty + FLMUINT m_uiNewCount; // Number of items in new list + FLMUINT m_uiDirtyCacheCount; // Number of dirty blocks + FLMUINT m_uiLogCacheCount; // Log blocks needing to be written. + F_CachedBlock ** m_ppBlocksDone; // List of blocks to be written to rollback + // log or database. + FLMUINT m_uiBlocksDoneArraySize;// Size of ppBlocksDone array. + FLMUINT m_uiBlocksDone; // Number of blocks currently in the + // ppBlocksDone array. + F_CachedBlock * m_pTransLogList; // This is a pointer to a linked list + // of all shared cache blocks + // belonging to this database that need + // to be logged to the rollback log + // for the current transaction. + FNOTIFY * m_pOpenNotifies; // Pointer to a list of notifies to + // perform when this database is finally + // opened (points to a linked list of + // FNOTIFY structures). + FNOTIFY * m_pCloseNotifies; // Pointer to a list of notifies to + // perform when this database is finally + // closed (points to a linked list of + // FNOTIFY structures). + F_Dict * m_pDictList; // Pointer to linked list of + // dictionaries currently being used + // for this database. The linked list + // is a list of versions of the + // dictionary. When a version is no + // longer used, it is removed from the + // list. Hence, the list is usually + // has only one member. + FLMBOOL m_bMustClose; // The database is being forced to close + // because of a critical error. + RCODE m_rcMustClose; // Return code that caused bMustClose to + // be set. + F_Pool m_krefPool; // Kref pool to be used during update + // transactions. + FLMUINT m_uiSigBitsInBlkSize;// Significant bits in the database's + // block size. + FLMUINT m_uiFileExtendSize; // Bytes to extend files by. + F_Rfl * m_pRfl; // Pointer RFL object. + + + XFLM_DB_HDR m_lastCommittedDbHdr;// This is the last committed DBheader. + XFLM_DB_HDR m_checkpointDbHdr; // This is the DB header as of the start + // of the last checkpoint. + XFLM_DB_HDR m_uncommittedDbHdr; // This is the uncommitted DB header. + // It is used by the current update + // transaction. + F_FileIdList * m_pFileIdList; // List of unique IDs that have been + // assigned to the physical files that + // are mananaged by the FFILE. + F_IOBufferMgr * m_pBufferMgr; + + F_IOBuffer * m_pCurrLogBuffer; + FLMUINT m_uiCurrLogWriteOffset; // Offset in current write buffer + FLMUINT m_uiCurrLogBlkAddr; + // Address of first block in the current + // buffer. + XFLM_DB_HDR * m_pDbHdrWriteBuf; // Aligned buffer (on win32) for writing + // the DB header. + FLMBYTE * m_pucUpdBuffer; // Buffer for writing out records. + FLMUINT m_uiUpdBufferSize; // Size of update buffer. + FLMBYTE m_ucIV [16]; // Used when outputting encrypted data. + F_CachedNode * m_pPendingInput; // Node that is having its value + // set across multiple calls. + F_Btree * m_pPendingBTree; // B-Tree used by node that is having + // its value set across multiple calls + FLMBOOL m_bUpdFirstBuf; + FLMUINT m_uiUpdByteCount; + FLMUINT m_uiUpdCharCount; + FLMUINT m_uiPendingType; + FLMBYTE * m_pucBTreeTmpBlk; // Temporary buffer for F_Btree object + // to use during a call that updates + // a B-Tree - btInsertEntry, + // btReplaceEntry, etc. SHOULD NOT + // be used in read/find operations! + FLMBYTE * m_pucBTreeTmpDefragBlk; + FLMBYTE * m_pucEntryArray; // Temporary buffer for F_Btree object + // to use during update operations. + FLMBYTE * m_pucSortedArray; // Temporary buffer for F_Btree object + // to use during update operations. + FLMBYTE * m_pucBtreeBuffer; // Buffer used by the Btree during moves + // between blocks. + FLMBYTE * m_pucReplaceStruct; // Buffer used by the Btree to hold additional + // replace information during updates *only*. + ServerLockObject * m_pDatabaseLockObj; // Object for locking the database. + ServerLockObject * m_pWriteLockObj; // Object for locking to do writing. + IF_FileHdl * m_pLockFileHdl; // Lock file handle. + FNOTIFY * m_pLockNotifies; // Pointer to a list of notifies to + // perform when this database is finally + // locked (points to a linked list of + // FNOTIFY structures). + FLMBOOL m_bBeingLocked; // Flag indicating whether or not this + // database is in the process of being + // locked for exclusive access. + F_Db * m_pFirstReadTrans; // Pointer to first read transaction for + // this database. + F_Db * m_pLastReadTrans; // Pointer to last read transaction for + // this database. + F_Db * m_pFirstKilledTrans; // List of read transactions that have + // been killed. + FLMUINT m_uiFirstLogBlkAddress; + // Address of first block logged for the + // current update transaction. + + FLMUINT m_uiFirstLogCPBlkAddress; + // Address of first block logged for the + // current checkpoint. + FLMUINT m_uiLastCheckpointTime; + // Last time we successfully completed a + // checkpoint. + F_Thread * m_pCPThrd; // Checkpoint thread. + CP_INFO * m_pCPInfo; // Pointer to checkpoint thread's + // information buffer - used for + // communicating information to the + // checkpoint thread. + RCODE m_CheckpointRc; // Return code from last checkpoint + // that was attempted. + FLMUINT m_uiBucket; // Hash bucket this database is in. + // 0xFFFF means it is not currently + // in a bucket. + FLMUINT m_uiFlags; // Flags for this database. + FLMBOOL m_bBackupActive; // Backup is currently being run against the + // database. + F_NodeList m_DocumentList; // List of documents modified in a transaction + F_Thread * m_pMaintThrd; // Background maintenance thread + F_SEM m_hMaintSem; // Maintenance thread "work-to-do" semaphore + FLMBYTE * m_pszDbPasswd; // The database encryption password + F_CCS * m_pWrappingKey; // The database wrapping key + FLMBOOL m_bHaveEncKey; // + FLMBOOL m_bAllowLimitedMode; // Is this database allowed to be opened in limited mode? + FLMBOOL m_bInLimitedMode; // Has this database been opened in limited mode? + RCODE m_rcLimitedCode; + F_MUTEX m_hMutex; // Database mutex. + +friend class F_Db; +friend class F_Rfl; +friend class F_Btree; +friend class F_Dict; +friend class F_DbSystem; +friend class F_Backup; +friend class FFileItemId; +friend class F_DOMNode; +friend class F_BTreeIStream; +friend class F_DbRebuild; +friend class F_DbCheck; +friend class F_Query; +friend class F_BtResultSet; +friend class F_BtRSFactory; +friend class FSIndexCursor; +friend class FSCollectionCursor; +friend class F_CachedNode; +friend class F_CachedBlock; +friend class F_BlockCacheMgr; +friend class F_NodeCacheMgr; +friend class F_GlobalCacheMgr; +friend class F_QueryResultSet; +friend class F_BTreeInfo; +friend class F_NodeRelocator; +friend class F_NodeDataRelocator; +friend class F_NodeListRelocator; +friend class F_AttrItemRelocator; +friend class F_AttrBufferRelocator; +friend class F_BlockRelocator; +}; + +/*************************************************************************** +Desc: This is the hash bucket header structure. Each bucket header + points to a list of items that belong to the bucket. +***************************************************************************/ +typedef struct FBucket +{ + void * pFirstInBucket; // Pointer to first item in the bucket. + // The type of structure being pointed to + // depends on the usage of the hash bucket. + FLMUINT uiHashValue; // Hash value for this bucket. +} FBUCKET; + +typedef struct QueryHdrTag +{ + F_Query * pQuery; + QueryHdrTag * pNext; + QueryHdrTag * pPrev; +} QUERY_HDR; + +/*************************************************************************** +Desc: This is the FLAIM Event Structure. It keeps track of a registered + event callback function that has been registered for a particular + event category. +***************************************************************************/ +typedef struct F_Event +{ + IF_EventClient * pEventClient; + F_Event * pNext; + F_Event * pPrev; +} FEVENT; + +/*************************************************************************** +Desc: This is the FLAIM Event Header Structure. It is the header for + the list of events that have been registered for a particular + event category. +***************************************************************************/ +typedef struct F_Event_Hdr +{ + FEVENT * pEventCBList; // List of registered event callbacks. + F_MUTEX hMutex; // Mutex to control access to the + // the event list. +} FEVENT_HDR; + +typedef enum +{ + HASH_SESSION_OBJ = 0, + HASH_DB_OBJ +} eHashObjType; + +/*=========================================================================== +Desc: FLAIM object base class +===========================================================================*/ +class F_HashObject : public XF_RefCount, public XF_Base +{ +public: + +#define F_INVALID_HASH_BUCKET (~((FLMUINT)0)) + + F_HashObject() + { + m_pNextInBucket = NULL; + m_pPrevInBucket = NULL; + m_pNextInGlobal = NULL; + m_pPrevInGlobal = NULL; + m_uiHashBucket = F_INVALID_HASH_BUCKET; + m_ui32CRC = 0xFFFFFFFF; + } + + virtual ~F_HashObject() + { + flmAssert( !m_pNextInBucket); + flmAssert( !m_pPrevInBucket); + flmAssert( !m_pNextInGlobal); + flmAssert( !m_pPrevInGlobal); + flmAssert( !getRefCount()); + } + + virtual void * getKey( + FLMUINT * puiKeyLen) = 0; + + FLMUINT getHashBucket( void) + { + return( m_uiHashBucket); + } + + FLMUINT32 getKeyCRC( void) + { + return( m_ui32CRC); + } + + FINLINE F_HashObject * getNextInGlobal( void) + { + return( m_pNextInGlobal); + } + + virtual eHashObjType objectType( void) = 0; + +protected: + + // Methods + + void setHashBucket( + FLMUINT uiHashBucket) + { + m_uiHashBucket = uiHashBucket; + } + + void setKeyCRC( + FLMUINT32 ui32CRC) + { + m_ui32CRC = ui32CRC; + } + + // Data + + F_HashObject * m_pNextInBucket; + F_HashObject * m_pPrevInBucket; + F_HashObject * m_pNextInGlobal; + F_HashObject * m_pPrevInGlobal; + FLMUINT m_uiHashBucket; + FLMUINT32 m_ui32CRC; + +friend class F_HashTable; +}; + +/*=========================================================================== +Desc: FLAIM hash table +===========================================================================*/ +class F_HashTable : public XF_RefCount, public XF_Base +{ +public: + + F_HashTable(); + + ~F_HashTable(); + + RCODE setupHashTable( + FLMBOOL bMultithreaded, + FLMUINT uiNumBuckets, + FLMUINT32 * pCRCTable); + + RCODE addObject( + F_HashObject * pObject); + + RCODE getNextObjectInGlobal( + F_HashObject ** ppObject); + + RCODE getObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject, + FLMBOOL bRemove = FALSE); + + RCODE removeObject( + void * pvKey, + FLMUINT uiKeyLen); + + RCODE removeObject( + F_HashObject * pObject); + +private: + + // Methods + + FLMUINT getHashBucket( + void * pvKey, + FLMUINT uiLen, + FLMUINT32 * pui32KeyCRC = NULL); + + void linkObject( + F_HashObject * pObject, + FLMUINT uiBucket); + + void unlinkObject( + F_HashObject * pObject); + + RCODE findObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject); + + // Data + + F_MUTEX m_hMutex; + F_HashObject * m_pGlobalList; + F_HashObject ** m_ppHashTable; + FLMUINT m_uiBuckets; + FLMUINT32 * m_pCRCTable; + FLMBOOL m_bOwnCRCTable; +}; + +typedef struct node_loc +{ + FLMUINT32 ui32DatabaseId; + FLMUINT32 ui32BlockAddr; + FLMUINT32 ui32OffsetIndex; + FLMUINT32 ui32Collection; + FLMUINT64 ui64NodeId; + node_loc * pPrevInBucket; + node_loc * pNextInBucket; +} F_NODE_LOCATION; + +/*************************************************************************** +Desc: This is the FLAIM Shared System Data Structure. It is the anchor + for all of the other shared structures. +***************************************************************************/ +typedef struct FlmSystemData +{ + FBUCKET * pDatabaseHashTbl; // Database name hash table (array of FBUCKET). +#define FILE_HASH_ENTRIES 256 + + F_MUTEX hShareMutex; // Mutex for controlling access to + // various global items. + F_MUTEX hNodeCacheMutex; + // Mutex for controlling access to + // node cache. + F_MUTEX hBlockCacheMutex; + // Mutex for controlling access to + // block cache. + F_FileHdlMgr * pFileHdlMgr; // Used to Manage all FileHdl objects + + FLMBOOL bTempDirSet; // TRUE if temporary directory has been set + + FLMBOOL bOkToDoAsyncWrites; + // OK To do async writes, if available. + FLMBOOL bOkToUseESM; // OK to use Extended Server Memory, + // if available + ServerLockManager * pServerLockMgr; + // Pointer to server lock manager. + FLMUINT uiMaxCPInterval; + // Maximum number of seconds to allow between + // checkpoints + F_GlobalCacheMgr * pGlobalCacheMgr; + F_BlockCacheMgr * pBlockCacheMgr; + F_NodeCacheMgr * pNodeCacheMgr; // Node cache manager + FLMUINT uiRehashAfterFailureBackoffTime; + // Amount of time to wait after trying + // to reallocate a cache manager's hash + // table before trying again. + F_NodePool * pNodePool; // Pool of nodes that can be re-used + F_Thread * pMonitorThrd; // Monitor thread + F_Thread * pCacheCleanupThrd; + XFLM_STATS Stats; // Statistics structure + F_MUTEX hStatsMutex; // Mutex for statistics structure + + F_MUTEX hQueryMutex; // Mutex for managing query list + QUERY_HDR * pNewestQuery; // Head of query list (newest) + QUERY_HDR * pOldestQuery; // Tail of query list (oldest) + FLMUINT uiQueryCnt; // Number of queries in the list + FLMUINT uiMaxQueries; // Maximum number of queries to keep around + FLMBOOL bNeedToUnsetMaxQueries; + // When TRUE, indicates that a call to stop + // statistics should also stop saving + // queries. + FLMBOOL bStatsInitialized; + // Has statistics structure been + // initialized? + + char szTempDir[ F_PATH_MAX_SIZE]; + // Temporary working directory for + // ResultSets, RecordCache + // and other sub-systems that need + // temporary files. This is aligned + // on a 4-byte boundary + + FLMUINT uiMaxUnusedTime; + // Maximum number of timer units to keep + // unused structures in memory before + // freeing them. + FEVENT_HDR EventHdrs [XFLM_MAX_EVENT_CATEGORIES]; + F_Pool * pKRefPool; // Memory Pool that is only used by + // record updaters for key building + + FLMUINT uiMaxFileSize; + IF_LoggerClient * pLogger; + FLMUINT uiPendingLogMessages; + F_MUTEX hLoggerMutex; + +#ifdef FLM_LINUX + FLMUINT uiLinuxMajorVer; + FLMUINT uiLinuxMinorVer; + FLMUINT uiLinuxRevision; +#endif + +#ifdef FLM_DEBUG + // Variables for memory allocation tracking. + + FLMBOOL bTrackLeaks; + FLMBOOL bLogLeaks; + FLMBOOL bStackWalk; + FLMBOOL bMemTrackingInitialized; + FLMUINT uiInitThreadId; + F_MUTEX hMemTrackingMutex; + void ** ppvMemTrackingPtrs; + FLMUINT uiMemTrackingPtrArraySize; + FLMUINT uiMemNumPtrs; + FLMUINT uiMemNextPtrSlotToUse; + FLMUINT uiAllocCnt; + #if defined( FLM_WIN) + HANDLE hMemProcess; + #endif + + #ifdef DEBUG_SIM_OUT_OF_MEM + FLMUINT uiOutOfMemSimEnabledFlag; + // We pick a random number for the flag so that it is hard to accidentally + // turn this flag on by writing memory out-of-bounds. + + #define OUT_OF_MEM_SIM_ENABLED_FLAG 2149614134UL + + F_RandomGenerator memSimRandomGen; + FLMUINT uiSimOutOfMemFailTotal; + FLMUINT uiSimOutOfMemFailSequence; + #endif +#endif + + F_MUTEX hIniMutex; + F_ThreadMgr * pThreadMgr; + F_MUTEX hHttpSessionMutex; + F_BtPool * pBtPool; + F_XML * pXml; +#ifdef FLM_NLM + FLMBOOL bUseNSSFileHdls; +#endif +} FLMSYSDATA; + +#ifndef ALLOCATE_SYS_DATA + extern FLMSYSDATA gv_XFlmSysData; +#else + FLMSYSDATA gv_XFlmSysData; +#endif + +FINLINE FLMBOOL f_isWhiteSpace( + FLMBYTE ucChar) +{ + return( ucChar == ASCII_SPACE || ucChar == ASCII_TAB ? TRUE : FALSE); +} + +FINLINE FLMUNICODE flmConvertChar( + FLMUNICODE uzChar, + FLMUINT uiCompareRules) +{ + if (uzChar == ASCII_SPACE || + (uzChar == ASCII_UNDERSCORE && + (uiCompareRules & XFLM_COMP_NO_UNDERSCORES)) || + (gv_XFlmSysData.pXml->isWhitespace( uzChar) && + (uiCompareRules & XFLM_COMP_WHITESPACE_AS_SPACE))) + { + return( (FLMUNICODE)((uiCompareRules & + (XFLM_COMP_NO_WHITESPACE | + XFLM_COMP_IGNORE_LEADING_SPACE)) + ? (FLMUNICODE)0 + : (FLMUNICODE)ASCII_SPACE)); + } + else if (uzChar == ASCII_DASH && (uiCompareRules & XFLM_COMP_NO_DASHES)) + { + return( (FLMUNICODE)0); + } + else + { + return( uzChar); + } +} + +#if defined( FLM_WIN) || defined( FLM_NLM) || defined( FLM_LINUX) + #pragma pack(pop) +#else + #pragma pack() +#endif + +#endif // FSTRUCTS_H diff --git a/version5/src/fsuperfl.cpp b/version5/src/fsuperfl.cpp new file mode 100644 index 0000000..0f65c9a --- /dev/null +++ b/version5/src/fsuperfl.cpp @@ -0,0 +1,1235 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the methods for FLAIM's +// super file 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: fsuperfl.cpp 3114 2006-01-19 13:22:45 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC char base24ToDigit( + FLMUINT uiBaseValue); + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FileIdList::F_FileIdList() +{ + m_hMutex = F_MUTEX_NULL; + m_uiFileIdTblSize = 0; + m_puiFileIdTbl = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FileIdList::~F_FileIdList() +{ + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_puiFileIdTbl) + { + for( FLMUINT uiLoop = 0; uiLoop < m_uiFileIdTblSize; uiLoop++) + { + if( m_puiFileIdTbl[ uiLoop]) + { + (void)gv_XFlmSysData.pFileHdlMgr->removeFileHdls( + m_puiFileIdTbl[ uiLoop]); + } + } + + f_free( &m_puiFileIdTbl); + } +} + +/**************************************************************************** +Desc: Allocates the mutex used by the file ID list object +****************************************************************************/ +RCODE F_FileIdList::setup( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_hMutex == F_MUTEX_NULL); + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Translates a database file number into a file ID. +****************************************************************************/ +RCODE F_FileIdList::getFileId( + FLMUINT uiFileNumber, + FLMUINT * puiFileId) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = TRUE; + FLMUINT uiLoop = 0; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( uiFileNumber >= m_uiFileIdTblSize) + { + FLMUINT uiOldTableSize = m_uiFileIdTblSize; + + /* + Re-size the table + */ + + if( RC_BAD( rc = f_recalloc( (uiFileNumber + 1) * sizeof( FLMUINT), + &m_puiFileIdTbl))) + { + goto Exit; + } + m_uiFileIdTblSize = uiFileNumber + 1; + + for( uiLoop = uiOldTableSize; uiLoop < m_uiFileIdTblSize; uiLoop++) + { + m_puiFileIdTbl[ uiLoop] = gv_XFlmSysData.pFileHdlMgr->getUniqueId(); + } + } + + *puiFileId = m_puiFileIdTbl[ uiFileNumber]; + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SuperFileHdl::F_SuperFileHdl( void) +{ + m_pFileIdList = NULL; + m_pszDbFileName = NULL; + m_pszDataFileNameBase = NULL; + f_memset( &m_CheckedOutFileHdls[ 0], 0, sizeof( m_CheckedOutFileHdls)); + m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; + m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; + m_uiBlockSize = 0; + m_uiExtendSize = XFLM_DEFAULT_FILE_EXTEND_SIZE; + m_uiMaxAutoExtendSize = gv_XFlmSysData.uiMaxFileSize; + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + m_uiHighestUsedSlot = 0; + m_uiHighestFileNumber = 0; + m_bMinimizeFlushes = FALSE; + m_bSetupCalled = FALSE; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_SuperFileHdl::~F_SuperFileHdl() +{ + /* + Release any file handles still being held and close the files. + */ + + if( m_bSetupCalled) + { + (void)ReleaseFiles( TRUE); + } + + /* + Release the ID list + */ + + if( m_pFileIdList) + { + m_pFileIdList->Release(); + } + + if (m_pszDbFileName) + { + f_free( &m_pszDbFileName); + } +} + +/**************************************************************************** +Desc: Configures the super file object +****************************************************************************/ +RCODE F_SuperFileHdl::Setup( + F_FileIdList * pFileIdList, + const char * pszDbFileName, + const char * pszDataDir) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNameLen; + FLMUINT uiDataNameLen; + char szDir [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + + flmAssert( !m_bSetupCalled); + + if( !pszDbFileName && *pszDbFileName == 0) + { + rc = RC_SET( NE_XFLM_IO_INVALID_FILENAME); + goto Exit; + } + + if( !pFileIdList) + { + if( (m_pFileIdList = f_new F_FileIdList) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pFileIdList->setup())) + { + FLMUINT uiRefCnt; + + uiRefCnt = m_pFileIdList->Release(); + flmAssert( !uiRefCnt); + m_pFileIdList = NULL; + goto Exit; + } + } + else + { + pFileIdList->AddRef(); + m_pFileIdList = pFileIdList; + } + + uiNameLen = f_strlen( pszDbFileName); + if (pszDataDir && *pszDataDir) + { + if (RC_BAD( rc = gv_pFileSystem->pathReduce( + pszDbFileName, szDir, szBaseName))) + { + goto Exit; + } + f_strcpy( szDir, pszDataDir); + if (RC_BAD( rc = gv_pFileSystem->pathAppend( + szDir, szBaseName))) + { + goto Exit; + } + uiDataNameLen = f_strlen( szDir); + + if (RC_BAD( rc = f_alloc( (uiNameLen + 1) + (uiDataNameLen + 1), + &m_pszDbFileName))) + { + goto Exit; + } + + f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); + m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; + F_DbSystem::getDbBasePath( m_pszDataFileNameBase, szDir, &m_uiDataExtOffset); + m_uiExtOffset = uiNameLen - (uiDataNameLen - m_uiDataExtOffset); + } + else + { + if (RC_BAD( rc = f_alloc( (uiNameLen + 1) * 2, &m_pszDbFileName))) + { + goto Exit; + } + + f_memcpy( m_pszDbFileName, pszDbFileName, uiNameLen + 1); + m_pszDataFileNameBase = m_pszDbFileName + uiNameLen + 1; + F_DbSystem::getDbBasePath( m_pszDataFileNameBase, + m_pszDbFileName, &m_uiDataExtOffset); + m_uiExtOffset = m_uiDataExtOffset; + } + + m_bSetupCalled = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Creates a file +****************************************************************************/ +RCODE F_SuperFileHdl::CreateFile( + FLMUINT uiFileNumber) +{ + char szFilePath[ F_PATH_MAX_SIZE]; + F_FileHdl * pFileHdl = NULL; + FLMUINT uiFileId; + RCODE rc = NE_XFLM_OK; + + /* + Sanity checks + */ + + flmAssert( m_bSetupCalled && m_uiBlockSize); + flmAssert( uiFileNumber <= MAX_LOG_BLOCK_FILE_NUMBER); + + /* + See if we already have an open file handle (or if we can open the file). + If so, truncate the file and use it. + */ + + if( RC_OK( rc = GetFileHdl( uiFileNumber, TRUE, (IF_FileHdl **)&pFileHdl))) + { + rc = pFileHdl->Truncate( 0); + pFileHdl = NULL; // Don't want to release the file handle at exit + goto Exit; + } + else if( rc != NE_XFLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + // The file was not found above. Allocate a new file handle. + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pFileHdl->SetBlockSize( m_uiBlockSize); + + // Configure the file handle. + + if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNumber, &uiFileId))) + { + goto Exit; + } + + flmAssert( uiFileId); // File ID should always be non-zero + + pFileHdl->setupFileHdl( uiFileId, FALSE); + + // Build the file path + + if( RC_BAD( rc = GetFilePath( uiFileNumber, szFilePath))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Create( szFilePath, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_DIRECT | XFLM_IO_SH_DENYNONE))) + { + goto Exit; + } + + // Insert into the file handle manager + + gv_XFlmSysData.pFileHdlMgr->insertInUsedList( FALSE, pFileHdl, TRUE); + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Reads a database block into a buffer +****************************************************************************/ +RCODE F_SuperFileHdl::ReadBlock( + FLMUINT uiBlkAddress, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + IF_FileHdl * pFileHdl = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled && m_uiBlockSize); + + if( RC_BAD( rc = GetFileHdl( + FSGetFileNumber( uiBlkAddress), FALSE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->SectorRead( + FSGetFileOffset( uiBlkAddress), uiBytesToRead, + pvBuffer, puiBytesRead))) + { + if (rc != NE_XFLM_IO_END_OF_FILE && rc != NE_XFLM_MEM) + { + ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Writes a block to the database +****************************************************************************/ +RCODE F_SuperFileHdl::WriteBlock( + FLMUINT uiBlkAddress, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT uiBufferSize, + F_IOBuffer * pIOBuffer, + FLMUINT * puiBytesWritten) +{ + IF_FileHdl * pFileHdl = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled && m_uiBlockSize); + +Get_Handle: + if( RC_BAD( rc = GetFileHdl( + FSGetFileNumber( uiBlkAddress), TRUE, &pFileHdl))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + if (RC_BAD( rc = CreateFile( FSGetFileNumber( uiBlkAddress)))) + { + goto Exit; + } + else + { + goto Get_Handle; + } + } + goto Exit; + } + + pFileHdl->setExtendSize( m_uiExtendSize); + pFileHdl->setMaxAutoExtendSize( m_uiMaxAutoExtendSize); + if( RC_BAD( rc = pFileHdl->SectorWrite( + FSGetFileOffset( uiBlkAddress), uiBytesToWrite, + pvBuffer, uiBufferSize, pIOBuffer, puiBytesWritten))) + { + if (rc != NE_XFLM_IO_DISK_FULL && rc != NE_XFLM_MEM) + { + ReleaseFile( FSGetFileNumber( uiBlkAddress), TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads data from the database header +****************************************************************************/ +RCODE F_SuperFileHdl::ReadHeader( + FLMUINT uiOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead) +{ + RCODE rc = NE_XFLM_OK; + IF_FileHdl * pFileHdl; + +#ifdef FLM_DEBUG + if( m_uiBlockSize) + { + /* + Note: Block size may not be set because we are in the process of + opening the file for the first time and we don't know the block + size until after the header has been read. + */ + + flmAssert( (FLMUINT)(uiOffset + uiBytesToRead) <= m_uiBlockSize); + } +#endif + + if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Read( uiOffset, + uiBytesToRead, pvBuffer, puiBytesRead))) + { + if (rc != NE_XFLM_IO_END_OF_FILE && rc != NE_XFLM_MEM) + { + ReleaseFile( (FLMUINT)0, TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Writes data to the database header +****************************************************************************/ +RCODE F_SuperFileHdl::WriteHeader( + FLMUINT uiOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_OK; + IF_FileHdl * pFileHdl; + +#ifdef FLM_DEBUG + if( m_uiBlockSize) + { + flmAssert( (FLMUINT)(uiOffset + uiBytesToWrite) <= m_uiBlockSize); + } +#endif + + if( RC_BAD( rc = GetFileHdl( 0, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Write( uiOffset, + uiBytesToWrite, pvBuffer, puiBytesWritten))) + { + if (rc != NE_XFLM_IO_DISK_FULL && rc != NE_XFLM_MEM) + { + ReleaseFile( (FLMUINT)0, TRUE); + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Releases all file handle objects and optionally closes the files +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFile( + FLMUINT uiFileNum, + FLMBOOL bCloseFile) +{ + RCODE rc = NE_XFLM_OK; + CHECKED_OUT_FILE_HDL * pCkoFileHdl; + FLMUINT uiSlot; + + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + if( pCkoFileHdl->uiFileNumber == uiFileNum) + { + if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, bCloseFile))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Releases all file handle objects and optionally closes the files +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFiles( + FLMBOOL bCloseFiles) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + + flmAssert( m_bSetupCalled); + + for( uiLoop = 0; uiLoop <= m_uiHighestUsedSlot; uiLoop++) + { + if( RC_BAD( rc = ReleaseFile( + &m_CheckedOutFileHdls[ uiLoop], bCloseFiles))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Releases a file handle object +****************************************************************************/ +RCODE F_SuperFileHdl::ReleaseFile( + CHECKED_OUT_FILE_HDL * pCkoFileHdl, + FLMBOOL bCloseFile) +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl * pFileHdl = (F_FileHdl *)pCkoFileHdl->pFileHdl; + + if( pFileHdl) + { + flmAssert( pFileHdl->getFileId()); + + if( pCkoFileHdl->bDirty) + { + (void)pFileHdl->Flush(); + } + + if( bCloseFile) + { + FLMUINT uiRefCnt; + + /* + We must remove this handle from all lists and release + the file handle. + */ + + gv_XFlmSysData.pFileHdlMgr->removeFileHdls( pFileHdl->getFileId()); + uiRefCnt = pFileHdl->Release(); + flmAssert( uiRefCnt == 0); // pFileHdl should have been freed. + } + else + { + + // Link out of the used list and move to the available list. + + gv_XFlmSysData.pFileHdlMgr->makeAvailAndRelease( FALSE, pFileHdl); + + // NOTE: makeAvailAndRelease will perform a release on the + // file handle object for the caller. + } + + clearCkoFileHdl( pCkoFileHdl); + } + + return( rc); +} + +/**************************************************************************** +Desc: Copy one CKO array into another. +****************************************************************************/ +void F_SuperFileHdl::copyCkoFileHdls( + CHECKED_OUT_FILE_HDL * pSrcCkoArray, + FLMUINT uiSrcHighestUsedSlot + ) +{ + FLMUINT uiNewSlot; + FLMUINT uiSrcSlot; + + // Zeroeth element is always copied. + + f_memcpy( m_pCheckedOutFileHdls, pSrcCkoArray, + sizeof( CHECKED_OUT_FILE_HDL)); + + // Memset the rest of the destination array to zero. + + f_memset( &m_pCheckedOutFileHdls[1], 0, sizeof( CHECKED_OUT_FILE_HDL) * + (m_uiCkoArraySize - 1)); + + m_uiHighestUsedSlot = 0; + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + for (uiSrcSlot = 1, pSrcCkoArray++; + uiSrcSlot <= uiSrcHighestUsedSlot; + uiSrcSlot++, pSrcCkoArray++) + { + if (pSrcCkoArray->pFileHdl && pSrcCkoArray->uiFileNumber) + { + uiNewSlot = pSrcCkoArray->uiFileNumber % (m_uiCkoArraySize - 1) + 1; + + // Only overwrite the destination one if the file number is + // lower than the one already there + + if (pSrcCkoArray->uiFileNumber < + m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber || + !m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) + { + if (m_pCheckedOutFileHdls [uiNewSlot].uiFileNumber) + { + ReleaseFile( &m_pCheckedOutFileHdls [uiNewSlot], FALSE); + } + f_memcpy( &m_pCheckedOutFileHdls [uiNewSlot], pSrcCkoArray, + sizeof( CHECKED_OUT_FILE_HDL)); + if (uiNewSlot > m_uiHighestUsedSlot) + { + m_uiHighestUsedSlot = uiNewSlot; + } + if (m_uiHighestFileNumber < pSrcCkoArray->uiFileNumber) + { + m_uiHighestFileNumber = pSrcCkoArray->uiFileNumber; + } + if (pSrcCkoArray->bDirty) + { + if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) + + { + m_uiLowestDirtySlot = + m_uiHighestDirtySlot = uiNewSlot; + } + else if( m_uiHighestDirtySlot < uiNewSlot) + { + m_uiHighestDirtySlot = uiNewSlot; + } + else if (m_uiLowestDirtySlot < uiNewSlot) + { + m_uiLowestDirtySlot = uiNewSlot; + } + } + } + else + { + ReleaseFile( pSrcCkoArray, FALSE); + } + } + } +} + +/**************************************************************************** +Desc: Disable flush minimizing. +****************************************************************************/ +void F_SuperFileHdl::disableFlushMinimize( void) +{ + + // Copy the allocated array back into the fixed array. + // This doesn't necessarily copy all of the file handles. + + if (m_pCheckedOutFileHdls != &m_CheckedOutFileHdls [0]) + { + CHECKED_OUT_FILE_HDL * pOldCkoArray = m_pCheckedOutFileHdls; + FLMUINT uiOldHighestUsedSlot = m_uiHighestUsedSlot; + + m_pCheckedOutFileHdls = &m_CheckedOutFileHdls [0]; + m_uiCkoArraySize = MAX_CHECKED_OUT_FILE_HDLS + 1; + copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); + + f_free( &pOldCkoArray); + } + m_bMinimizeFlushes = FALSE; +} + +/**************************************************************************** +Desc: Flush dirty files to disk. +****************************************************************************/ +RCODE F_SuperFileHdl::Flush( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + + // Flush all dirty files + + for (uiLoop = m_uiLowestDirtySlot; + uiLoop <= m_uiHighestDirtySlot; + uiLoop++) + { + if( m_pCheckedOutFileHdls[ uiLoop].bDirty) + { + RCODE tmpRc; + + if (RC_BAD( tmpRc = + m_pCheckedOutFileHdls[ uiLoop].pFileHdl->Flush())) + { + rc = tmpRc; + ReleaseFile( &m_pCheckedOutFileHdls [uiLoop], TRUE); + } + m_pCheckedOutFileHdls[ uiLoop].bDirty = FALSE; + } + } + m_uiLowestDirtySlot = 1; + m_uiHighestDirtySlot = 0; + return( rc); +} + +/**************************************************************************** +Desc: Truncates back to an end of file block address. + This may only be called from reduce() because there cannot + be any other cases to reduce a 3x block file. +****************************************************************************/ +RCODE F_SuperFileHdl::TruncateFile( + FLMUINT uiEOFBlkAddress) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNumber = (FLMUINT)FSGetFileNumber( uiEOFBlkAddress); + FLMUINT uiBlockOffset = (FLMUINT)FSGetFileOffset( uiEOFBlkAddress); + IF_FileHdl * pFileHdl; + + /* + Truncate the current block file. + */ + + if( RC_BAD( rc = GetFileHdl( uiFileNumber, TRUE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Truncate( uiBlockOffset))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + + /* + Visit the rest of the high block files until a NULL file hdl is hit. + */ + + for( ;;) + { + if( RC_BAD( GetFileHdl( ++uiFileNumber, TRUE, &pFileHdl))) + { + break; + } + + if( RC_BAD( rc = pFileHdl->Truncate( (FLMUINT)0 ))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + + if( RC_BAD( rc = ReleaseFile( uiFileNumber, TRUE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Truncate to zero length any files between the specified start + and end files. +****************************************************************************/ +void F_SuperFileHdl::TruncateFiles( + FLMUINT uiStartFileNum, + FLMUINT uiEndFileNum) +{ + FLMUINT uiFileNumber; + IF_FileHdl * pFileHdl; + + for( uiFileNumber = uiStartFileNum; + uiFileNumber <= uiEndFileNum; + uiFileNumber++ ) + { + if( RC_OK( GetFileHdl( uiFileNumber, TRUE, &pFileHdl ))) + { + (void)pFileHdl->Truncate( (FLMUINT)0 ); + (void)ReleaseFile( uiFileNumber, TRUE); + } + } +} + +/**************************************************************************** +Desc: Returns the physical size of a file +****************************************************************************/ +RCODE F_SuperFileHdl::GetFileSize( + FLMUINT uiFileNumber, + FLMUINT64 * pui64FileSize) +{ + IF_FileHdl * pFileHdl = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled); + flmAssert( pui64FileSize); + + *pui64FileSize = 0; + + // Get the file handle. + + if( RC_BAD( rc = GetFileHdl( uiFileNumber, FALSE, &pFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pFileHdl->Size( pui64FileSize))) + { + ReleaseFile( uiFileNumber, TRUE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns the path of a file given its file number +****************************************************************************/ +RCODE F_SuperFileHdl::GetFilePath( + FLMUINT uiFileNumber, + char * pszIoPath) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiExtOffset; + + // Sanity checks + + flmAssert( m_bSetupCalled); + + if (!uiFileNumber) + { + f_strcpy( pszIoPath, m_pszDbFileName); + goto Exit; + } + + if( uiFileNumber <= MAX_DATA_BLOCK_FILE_NUMBER) + { + f_memcpy( pszIoPath, m_pszDataFileNameBase, m_uiDataExtOffset); + uiExtOffset = m_uiDataExtOffset; + } + else + { + f_memcpy( pszIoPath, m_pszDbFileName, m_uiExtOffset); + uiExtOffset = m_uiExtOffset; + } + + // Modify the file's extension. + + bldSuperFileExtension( uiFileNumber, &pszIoPath[ uiExtOffset]); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reallocates the checked out file handle array. +****************************************************************************/ +RCODE F_SuperFileHdl::reallocCkoArray( + FLMUINT uiFileNum + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNewSize; + CHECKED_OUT_FILE_HDL * pNewCkoArray; + CHECKED_OUT_FILE_HDL * pOldCkoArray; + FLMUINT uiOldHighestUsedSlot; + + if (uiFileNum < m_uiHighestFileNumber) + { + uiFileNum = m_uiHighestFileNumber; + } + uiNewSize = uiFileNum + 128; + + // Reallocate so we can guarantee that all of the current file + // numbers will copy and there is room for this new one as well. + + if (uiNewSize > MAX_LOG_BLOCK_FILE_NUMBER + 1) + { + flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER); + uiNewSize = MAX_LOG_BLOCK_FILE_NUMBER + 1; + } + + // No need to call f_calloc, because copyCkoFileHdls will initialize + // it below. + + if (RC_BAD( rc = f_alloc( sizeof( CHECKED_OUT_FILE_HDL) * uiNewSize, + &pNewCkoArray))) + { + goto Exit; + } + + pOldCkoArray = m_pCheckedOutFileHdls; + uiOldHighestUsedSlot = m_uiHighestUsedSlot; + + m_pCheckedOutFileHdls = pNewCkoArray; + m_uiCkoArraySize = uiNewSize; + + copyCkoFileHdls( pOldCkoArray, uiOldHighestUsedSlot); + + // Can't free the old one until after the copy! + + if (pOldCkoArray != &m_CheckedOutFileHdls [0]) + { + f_free( &pOldCkoArray); + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Returns a file handle given the file's number +****************************************************************************/ +RCODE F_SuperFileHdl::GetFileHdl( + FLMUINT uiFileNum, + FLMBOOL bGetForUpdate, + IF_FileHdl ** ppFileHdl) +{ + F_FileHdl * pFileHdl = NULL; + FLMUINT uiFileId; + CHECKED_OUT_FILE_HDL * pCkoFileHdl; + char szFilePath[ F_PATH_MAX_SIZE]; + FLMUINT uiSlot; + RCODE rc = NE_XFLM_OK; + + /* + Get the file handle + */ + + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + if( pCkoFileHdl->uiFileNumber != uiFileNum && + pCkoFileHdl->pFileHdl) + { + if (pCkoFileHdl->bDirty && m_bMinimizeFlushes) + { + flmAssert( pCkoFileHdl->uiFileNumber); + if (RC_BAD( reallocCkoArray( uiFileNum))) + { + goto Exit; + } + pCkoFileHdl = getCkoFileHdlPtr( uiFileNum, &uiSlot); + + // Better have reallocated so that the new slot for + // the file number has nothing in it. + + flmAssert( !pCkoFileHdl->uiFileNumber && + !pCkoFileHdl->pFileHdl); + } + else + { + if( RC_BAD( rc = ReleaseFile( pCkoFileHdl, FALSE))) + { + goto Exit; + } + } + } + + if( !pCkoFileHdl->pFileHdl) + { + /* + Get the file ID + */ + + if( RC_BAD( rc = m_pFileIdList->getFileId( uiFileNum, &uiFileId))) + { + goto Exit; + } + + /* + Look for an available file handle if not opening exclusive. + NOTE: AddRef() performed for caller by findAvail if a file + handle is found. + */ + + gv_XFlmSysData.pFileHdlMgr->findAvail( uiFileId, FALSE, &pFileHdl); + + if (!pFileHdl) + { + /* + Allocate a new file handle, open the file and + link into the used directory. + */ + + if ((pFileHdl = f_new F_FileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + /* + If m_uiBlockSize is 0, direct I/O will not be used + */ + + pFileHdl->SetBlockSize( m_uiBlockSize); + + flmAssert( uiFileId); // File ID must be non-zero + + pFileHdl->setupFileHdl( uiFileId, FALSE); + + // Build the file path + + if( RC_BAD( rc = GetFilePath( uiFileNum, szFilePath))) + { + goto Exit; + } + + // Open the file + + if( RC_BAD( rc = pFileHdl->Open( szFilePath, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT))) + { + goto Exit; + } + + // Insert into the manager + + gv_XFlmSysData.pFileHdlMgr->insertInUsedList( FALSE, pFileHdl, TRUE); + } + + pCkoFileHdl->pFileHdl = (IF_FileHdl *)pFileHdl; + pFileHdl = NULL; + pCkoFileHdl->uiFileNumber = uiFileNum; + pCkoFileHdl->bDirty = FALSE; + + if( m_uiHighestUsedSlot < uiSlot) + { + m_uiHighestUsedSlot = uiSlot; + } + if (m_uiHighestFileNumber < uiFileNum) + { + m_uiHighestFileNumber = uiFileNum; + } + } + + *ppFileHdl = pCkoFileHdl->pFileHdl; + if( bGetForUpdate) + { + pCkoFileHdl->bDirty = TRUE; + if (m_uiLowestDirtySlot > m_uiHighestDirtySlot) + + { + m_uiLowestDirtySlot = + m_uiHighestDirtySlot = uiSlot; + } + else if( m_uiHighestDirtySlot < uiSlot) + { + m_uiHighestDirtySlot = uiSlot; + } + else if (m_uiLowestDirtySlot < uiSlot) + { + m_uiLowestDirtySlot = uiSlot; + } + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Generates a file name given a super file number. + Adds ".xx" to pFileExtension. Use lower case characters. +Notes: This is a base 24 alphanumeric value where + { a, b, c, d, e, f, i, l, o, r, u, v } values are removed. +****************************************************************************/ +void bldSuperFileExtension( + FLMUINT uiFileNum, + char * pszFileExtension) +{ + char ucLetter; + + if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 1536) + { + // No additional letter - File numbers 1 to 511 + // This is just like pre-4.3 numbering. + ucLetter = 0; + } + else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 1024) + { + // File numbers 512 to 1023 + ucLetter = 'r'; + } + else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER - 512) + { + // File numbers 1024 to 1535 + ucLetter = 's'; + } + else if (uiFileNum <= MAX_DATA_BLOCK_FILE_NUMBER) + { + // File numbers 1536 to 2047 + ucLetter = 't'; + } + else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 1536) + { + // File numbers 2048 to 2559 + ucLetter = 'v'; + } + else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 1024) + { + // File numbers 2560 to 3071 + ucLetter = 'w'; + } + else if (uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER - 512) + { + // File numbers 3072 to 3583 + ucLetter = 'x'; + } + else + { + flmAssert( uiFileNum <= MAX_LOG_BLOCK_FILE_NUMBER); + + // File numbers 3584 to 4095 + ucLetter = 'z'; + } + + *pszFileExtension++ = '.'; + *pszFileExtension++ = (char)(base24ToDigit( (uiFileNum & 511) / 24)); + *pszFileExtension++ = (char)(base24ToDigit( (uiFileNum & 511) % 24)); + *pszFileExtension++ = ucLetter; + *pszFileExtension = 0; +} + +/**************************************************************************** +Desc: Turn a base 24 value into a native alphanumeric value. +Notes: This is a base 24 alphanumeric value where + {a, b, c, d, e, f, i, l, o, r, u, v } values are removed. +****************************************************************************/ +FSTATIC char base24ToDigit( + FLMUINT uiValue) +{ + flmAssert( uiValue <= 23); + + if( uiValue <= 9) + { + uiValue += (FLMUINT) NATIVE_ZERO; + } + else + { + uiValue = f_toascii(uiValue) - 10 + (FLMUINT)f_toascii('g'); + if( uiValue >= (FLMUINT)'i') + { + uiValue++; + if( uiValue >= (FLMUINT)'l') + { + uiValue++; + if( uiValue >= (FLMUINT)'o') + { + uiValue++; + if( uiValue >= (FLMUINT)'r') + { + uiValue++; + if( uiValue >= (FLMUINT)'u') + { + uiValue++; + if( uiValue >= (FLMUINT)'v') + { + uiValue++; + } + } + } + } + } + } + } + return (char)uiValue; +} diff --git a/version5/src/fsuperfl.h b/version5/src/fsuperfl.h new file mode 100644 index 0000000..10b8182 --- /dev/null +++ b/version5/src/fsuperfl.h @@ -0,0 +1,295 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's +// super file 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: fsuperfl.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FSUPERFL_H +#define FSUPERFL_H + +#include "fsrvlock.h" + +/* +Constants +*/ + +#define MAX_CHECKED_OUT_FILE_HDLS 8 + +/* +Forward references +*/ + +class F_SuperFileHdl; +typedef F_SuperFileHdl * F_SuperFileHdl_p; + +class F_FileIdList; +typedef F_FileIdList * F_FileIdList_p; + +/* +Typedefs +*/ + +typedef struct CheckedOutFileHdlTag +{ + IF_FileHdl * pFileHdl; + FLMUINT uiFileNumber; + FLMBOOL bDirty; +} CHECKED_OUT_FILE_HDL; + +// Misc. prototypes + +void bldSuperFileExtension( + FLMUINT uiFileNum, + char * pszFileExtension); + +/*=========================================================================== +Desc: This class keeps a list of file IDs for file numbers in a database. + It does not know the use of the file IDs, and it is not limited in + the number of file IDs it can keep track of, but it will generally + be as follows: + + FileNumber + 0 This is the database file (xxx.db) + 1-4095 These are the data files (xxx_data.nnn) + 4096-8192 These are the rollback files (xxx_rb.nnn). In this + case, the caller will map the file number in and + out of this range. +===========================================================================*/ +class F_FileIdList : public XF_RefCount, public XF_Base +{ +public: + F_FileIdList(); + + ~F_FileIdList(); + + RCODE setup( void); + + RCODE getFileId( + FLMUINT uiFileNumber, + FLMUINT * puiFileId); + + FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( ftkAtomicIncrement( &m_ui32RefCnt)); + } + + FINLINE FLMUINT32 XFLMAPI Release( void) + { + FLMUINT32 ui32RefCnt = ftkAtomicDecrement( &m_ui32RefCnt); + + if( !ui32RefCnt) + { + delete this; + } + return( ui32RefCnt); + } + +private: + + F_MUTEX m_hMutex; + FLMUINT m_uiFileIdTblSize; + FLMUINT * m_puiFileIdTbl; +}; + +/*=========================================================================== +Desc: The F_SuperFileHdl object manages the control and block files + associated with a FLAIM Super File. This class also provides + backward compatibility with prior file formats. +Note: +===========================================================================*/ +class F_SuperFileHdl : public XF_RefCount, public XF_Base +{ +public: + F_SuperFileHdl(); // F_SuperFileHdl Constructor + + ~F_SuperFileHdl(); // F_SuperFileHdl Destructor + + RCODE Setup( // Configures the object. Should + // be called exactly once. + F_FileIdList * pFileIdList, + const char * pszDbFileName, + const char * pszDataDir); + + RCODE CreateFile( // Create a block file (>= 3.0 only) + FLMUINT uiFileNumber); // File number to create + + RCODE ReadBlock( // Reads a block from a block file or + // the log + FLMUINT uiBlkAddress, // Block address + FLMUINT uiBytesToRead, // Number of bytes to read from block + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesRead); // [out] number of bytes read + + RCODE WriteBlock( // Writes a block to a block file or + // the log + FLMUINT uiBlkAddress, // Block address + FLMUINT uiBytesToWrite, // Number of bytes to write + const void * pvBuffer, // Buffer to write bytes from + FLMUINT uiBufferSize, // Actual size of buffer + F_IOBuffer * pIOBuffer, // If non-NULL, contains info for + // doing an async write. + FLMUINT * puiBytesWritten); // [out] number of bytes written + + RCODE ReadHeader( // Reads data from the DB header + FLMUINT uiOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesRead); + + RCODE WriteHeader( // Writes data to the DB header + FLMUINT uiOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT * puiBytesWritten); + + RCODE GetFilePath( // Generates a block file's path + FLMUINT uiFileNumber, // File number + char * pszPath); // Returned path + + RCODE GetFileHdl( // Returns a file's handle given + // the file's number + FLMUINT uiFileNumber, + FLMBOOL bGetForUpdate, + IF_FileHdl ** ppFileHdlRV); + + RCODE GetFileSize( // Returns the physical size of + // a file + FLMUINT uiFileNumber, // File number + FLMUINT64 * pui64FileSize); // File size return value + + RCODE ReleaseFile( // Release a single file + FLMUINT uiFileNum, + FLMBOOL bCloseFile); + + RCODE ReleaseFiles( // Releases all file handles and + // returns them to the file handle + // manager. This is called at + // the end of a transaction. + FLMBOOL bCloseFiles); // Should files be closed? + + RCODE TruncateFile( // Truncate the file(s) to given address. + FLMUINT uiEOFBlkAddress); // End of file block address. + + void TruncateFiles( // Truncate the files specified by the + FLMUINT uiStartFileNum, // start and end file numbers. + FLMUINT uiEndFileNum); + + RCODE ReleaseFile( // Release a single file + CHECKED_OUT_FILE_HDL * pChkFileHdl, + FLMBOOL bCloseFile); + + FINLINE void enableFlushMinimize( void) + { + m_bMinimizeFlushes = TRUE; + } + + void disableFlushMinimize( void); + + RCODE Flush( void); + + FINLINE void SetBlockSize( + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + FINLINE void setExtendSize( + FLMUINT uiExtendSize) + { + m_uiExtendSize = uiExtendSize; + } + + FINLINE void setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) + { + m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; + } + + FINLINE FLMBOOL CanDoAsync( void) + { + if (m_pCheckedOutFileHdls[ 0].pFileHdl) + { + return( m_pCheckedOutFileHdls[ 0].pFileHdl->CanDoAsync()); + } + else + { + IF_FileHdl * pFileHdl; + + if( RC_OK( GetFileHdl( 0, FALSE, &pFileHdl))) + { + return( pFileHdl->CanDoAsync()); + } + } + + return( FALSE); + } + +private: + + FINLINE CHECKED_OUT_FILE_HDL * getCkoFileHdlPtr( + FLMUINT uiFileNum, + FLMUINT * puiSlot) + { + *puiSlot = (uiFileNum + ? (uiFileNum % (m_uiCkoArraySize - 1)) + 1 + : 0); + + return( &m_pCheckedOutFileHdls[ *puiSlot]); + } + + FINLINE void clearCkoFileHdl( + CHECKED_OUT_FILE_HDL * pCkoFileHdl) + { + pCkoFileHdl->pFileHdl = NULL; + pCkoFileHdl->uiFileNumber = 0; + pCkoFileHdl->bDirty = FALSE; + } + + void copyCkoFileHdls( + CHECKED_OUT_FILE_HDL * pSrcCkoArray, + FLMUINT uiSrcHighestUsedSlot); + + RCODE reallocCkoArray( + FLMUINT uiFileNum); + + char * m_pszDbFileName; + char * m_pszDataFileNameBase; + FLMUINT m_uiExtOffset; + FLMUINT m_uiDataExtOffset; + F_FileIdList * m_pFileIdList; + CHECKED_OUT_FILE_HDL m_CheckedOutFileHdls[ + MAX_CHECKED_OUT_FILE_HDLS + 1]; + CHECKED_OUT_FILE_HDL * m_pCheckedOutFileHdls; + FLMUINT m_uiCkoArraySize; + FLMUINT m_uiBlockSize; + FLMUINT m_uiExtendSize; + FLMUINT m_uiMaxAutoExtendSize; + FLMUINT m_uiLowestDirtySlot; + FLMUINT m_uiHighestDirtySlot; + FLMUINT m_uiHighestUsedSlot; + FLMUINT m_uiHighestFileNumber; + FLMBOOL m_bMinimizeFlushes; + FLMBOOL m_bSetupCalled; +}; + +#endif // FSUPERFL_H diff --git a/version5/src/fsysdata.cpp b/version5/src/fsysdata.cpp new file mode 100644 index 0000000..938b333 --- /dev/null +++ b/version5/src/fsysdata.cpp @@ -0,0 +1,5091 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the routines that initialize and shut down FLAIM, +// as well as routines for configuring FLAIM. +// +// Tabs: 3 +// +// Copyright (c) 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: fsysdata.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#define ALLOCATE_SYS_DATA +#define ALLOC_ERROR_TABLES + +#include +#include "flaimsys.h" +#include "inifile.h" + +#define HIGH_FLMUINT (~((FLMUINT)0)) +#define FLM_MIN_FREE_BYTES (2 * 1024 * 1024) + +#ifdef FLM_32BIT + #if defined( FLM_LINUX) + + // With mmap'd memory on Linux, you're effectively limited to about ~2 GB. + // Userspace only gets ~3GB of usable address space anyway, and then you + // have all of the thread stacks too, which you can't have + // overlapping the heap. + + #define FLM_MAX_CACHE_SIZE (1500 * 1024 * 1024) + #else + #define FLM_MAX_CACHE_SIZE (2000 * 1024 * 1024) + #endif +#else + #define FLM_MAX_CACHE_SIZE (~((FLMUINT)0)) +#endif + +FLMUINT32 F_DbSystem::m_ui32FlmSysSpinLock = 0; +FLMUINT F_DbSystem::m_uiFlmSysStartupCount = 0; + +static FLMBYTE ucSENPrefixArray[] = + {0, 0, 0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF}; + +// Local Function Prototypes + +#ifdef FLM_CAN_GET_PHYS_MEM +FSTATIC FLMUINT flmGetCacheBytes( + FLMUINT uiPercent, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bCalcOnAvailMem, + FLMUINT uiBytesCurrentlyInUse); +#endif + +FSTATIC RCODE flmVerifyDiskStructOffsets( void); + +FSTATIC void flmFreeEvent( + FEVENT * pEvent, + F_MUTEX hMutex, + FEVENT ** ppEventListRV); + +FSTATIC void flmGetStringParam( + const char * pszParamName, + char ** ppszValue, + F_IniFile * pIniFile); + +FSTATIC void flmGetNumParam( + char ** ppszParam, + FLMUINT * puiNum); + +FSTATIC void flmGetUintParam( + const char * pszParamName, + FLMUINT uiDefaultValue, + FLMUINT * puiUint, + F_IniFile * pIniFile); + +void flmGetBoolParam( + const char * pszParamName, + FLMBOOL uiDefaultValue, + FLMBOOL * pbBool, + F_IniFile * pIniFile); + +FSTATIC RCODE flmGetIniFileName( + FLMBYTE * pszIniFileName, + FLMUINT uiBufferSz); + +/**************************************************************************** +Desc: Error code to string mapping tables +****************************************************************************/ +typedef struct +{ + RCODE rc; + const char * pszErrorStr; +} F_ERROR_CODE_MAP; + +#define flmErrorCodeEntry(c) { c, #c } + +#ifdef FLM_NLM + FLMBOOL gv_bNetWareStartupCalled = FALSE; +#endif + +F_ERROR_CODE_MAP gv_FlmCommonErrors[ + NE_XFLM_LAST_COMMON_ERROR - NE_XFLM_FIRST_COMMON_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_NOT_IMPLEMENTED), + flmErrorCodeEntry( NE_XFLM_MEM), + { XFLM_ERROR_BASE( 0x0003), "NotUsed"}, + { XFLM_ERROR_BASE( 0x0004), "NotUsed"}, + flmErrorCodeEntry( NE_XFLM_INVALID_PARM), + { XFLM_ERROR_BASE( 0x0006), "NotUsed"}, + { XFLM_ERROR_BASE( 0x0007), "NotUsed"}, + { XFLM_ERROR_BASE( 0x0008), "NotUsed"}, + flmErrorCodeEntry( NE_XFLM_TIMEOUT), + flmErrorCodeEntry( NE_XFLM_NOT_FOUND), + { XFLM_ERROR_BASE( 0x000B), "NotUsed"}, + flmErrorCodeEntry( NE_XFLM_EXISTS), + { XFLM_ERROR_BASE( 0x000D), "NotUsed"}, + { XFLM_ERROR_BASE( 0x000E), "NotUsed"}, + { XFLM_ERROR_BASE( 0x000F), "NotUsed"}, + flmErrorCodeEntry( NE_XFLM_USER_ABORT), + flmErrorCodeEntry( NE_XFLM_FAILURE) +}; + +F_ERROR_CODE_MAP gv_FlmGeneralErrors[ + NE_XFLM_LAST_GENERAL_ERROR - NE_XFLM_FIRST_GENERAL_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_BOF_HIT), + flmErrorCodeEntry( NE_XFLM_EOF_HIT), + flmErrorCodeEntry( NE_XFLM_END), + flmErrorCodeEntry( NE_XFLM_BAD_PREFIX), + flmErrorCodeEntry( NE_XFLM_ATTRIBUTE_PURGED), + flmErrorCodeEntry( NE_XFLM_BAD_COLLECTION), + flmErrorCodeEntry( NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_DATA_COMPONENT), + flmErrorCodeEntry( NE_XFLM_BAD_DATA_TYPE), + flmErrorCodeEntry( NE_XFLM_MUST_INDEX_ON_PRESENCE), + flmErrorCodeEntry( NE_XFLM_BAD_IX), + flmErrorCodeEntry( NE_XFLM_BACKUP_ACTIVE), + flmErrorCodeEntry( NE_XFLM_SERIAL_NUM_MISMATCH), + flmErrorCodeEntry( NE_XFLM_BAD_RFL_DB_SERIAL_NUM), + flmErrorCodeEntry( NE_XFLM_BTREE_ERROR), + flmErrorCodeEntry( NE_XFLM_BTREE_FULL), + flmErrorCodeEntry( NE_XFLM_BAD_RFL_FILE_NUMBER), + flmErrorCodeEntry( NE_XFLM_CANNOT_DEL_ELEMENT), + flmErrorCodeEntry( NE_XFLM_CANNOT_MOD_DATA_TYPE), + flmErrorCodeEntry( NE_XFLM_CANNOT_INDEX_DATA_TYPE), + flmErrorCodeEntry( NE_XFLM_CONV_BAD_DIGIT), + flmErrorCodeEntry( NE_XFLM_CONV_DEST_OVERFLOW), + flmErrorCodeEntry( NE_XFLM_CONV_ILLEGAL), + flmErrorCodeEntry( NE_XFLM_CONV_NULL_SRC), + flmErrorCodeEntry( NE_XFLM_CONV_NUM_OVERFLOW), + flmErrorCodeEntry( NE_XFLM_CONV_NUM_UNDERFLOW), + flmErrorCodeEntry( NE_XFLM_BAD_ELEMENT_NUM), + flmErrorCodeEntry( NE_XFLM_BAD_ATTRIBUTE_NUM), + flmErrorCodeEntry( NE_XFLM_BAD_ENCDEF_NUM), + flmErrorCodeEntry( NE_XFLM_DATA_ERROR), + flmErrorCodeEntry( NE_XFLM_INVALID_FILE_SEQUENCE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_OP), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_ELEMENT_NUM), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_TRANS_TYPE), + flmErrorCodeEntry( NE_XFLM_UNSUPPORTED_VERSION), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_TRANS_OP), + flmErrorCodeEntry( NE_XFLM_INCOMPLETE_LOG), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_INDEX_DEF), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_INDEX_ON), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_STATE_CHANGE), + flmErrorCodeEntry( NE_XFLM_BAD_RFL_SERIAL_NUM), + flmErrorCodeEntry( NE_XFLM_NEWER_FLAIM), + flmErrorCodeEntry( NE_XFLM_CANNOT_MOD_ELEMENT_STATE), + flmErrorCodeEntry( NE_XFLM_CANNOT_MOD_ATTRIBUTE_STATE), + flmErrorCodeEntry( NE_XFLM_NO_MORE_ELEMENT_NUMS), + flmErrorCodeEntry( NE_XFLM_NO_TRANS_ACTIVE), + flmErrorCodeEntry( NE_XFLM_NOT_UNIQUE), + flmErrorCodeEntry( NE_XFLM_NOT_FLAIM), + flmErrorCodeEntry( NE_XFLM_OLD_VIEW), + flmErrorCodeEntry( NE_XFLM_SHARED_LOCK), + flmErrorCodeEntry( NE_XFLM_SYNTAX), + flmErrorCodeEntry( NE_XFLM_TRANS_ACTIVE), + flmErrorCodeEntry( NE_XFLM_RFL_TRANS_GAP), + flmErrorCodeEntry( NE_XFLM_BAD_COLLATED_KEY), + flmErrorCodeEntry( NE_XFLM_UNSUPPORTED_FEATURE), + flmErrorCodeEntry( NE_XFLM_MUST_DELETE_INDEXES), + flmErrorCodeEntry( NE_XFLM_RFL_INCOMPLETE), + flmErrorCodeEntry( NE_XFLM_CANNOT_RESTORE_RFL_FILES), + flmErrorCodeEntry( NE_XFLM_INCONSISTENT_BACKUP), + flmErrorCodeEntry( NE_XFLM_BLOCK_CRC), + flmErrorCodeEntry( NE_XFLM_ABORT_TRANS), + flmErrorCodeEntry( NE_XFLM_NOT_RFL), + flmErrorCodeEntry( NE_XFLM_BAD_RFL_PACKET), + flmErrorCodeEntry( NE_XFLM_DATA_PATH_MISMATCH), + flmErrorCodeEntry( NE_XFLM_STREAM_EXISTS), + flmErrorCodeEntry( NE_XFLM_FILE_EXISTS), + flmErrorCodeEntry( NE_XFLM_COULD_NOT_CREATE_SEMAPHORE), + flmErrorCodeEntry( NE_XFLM_MUST_CLOSE_DATABASE), + flmErrorCodeEntry( NE_XFLM_INVALID_ENCKEY_CRC), + flmErrorCodeEntry( NE_XFLM_BAD_UTF8), + flmErrorCodeEntry( NE_XFLM_COULD_NOT_CREATE_MUTEX), + flmErrorCodeEntry( NE_XFLM_ERROR_WAITING_ON_SEMPAHORE), + flmErrorCodeEntry( NE_XFLM_BAD_PLATFORM_FORMAT), + flmErrorCodeEntry( NE_XFLM_HDR_CRC), + flmErrorCodeEntry( NE_XFLM_NO_NAME_TABLE), + flmErrorCodeEntry( NE_XFLM_MULTIPLE_MATCHES), + flmErrorCodeEntry( NE_XFLM_UNALLOWED_UPGRADE), + flmErrorCodeEntry( NE_XFLM_BTREE_BAD_STATE), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_ATTRIBUTE_NUM), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_INDEX_NUM), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_COLLECTION_NUM), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_ELEMENT_NAME), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_ATTRIBUTE_NAME), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_INDEX_NAME), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_COLLECTION_NAME), + flmErrorCodeEntry( NE_XFLM_ELEMENT_PURGED), + flmErrorCodeEntry( NE_XFLM_TOO_MANY_OPEN_DATABASES), + flmErrorCodeEntry( NE_XFLM_DATABASE_OPEN), + flmErrorCodeEntry( NE_XFLM_CACHE_ERROR), + flmErrorCodeEntry( NE_XFLM_BTREE_KEY_SIZE), + flmErrorCodeEntry( NE_XFLM_DB_FULL), + flmErrorCodeEntry( NE_XFLM_QUERY_SYNTAX), + flmErrorCodeEntry( NE_XFLM_COULD_NOT_START_THREAD), + flmErrorCodeEntry( NE_XFLM_INDEX_OFFLINE), + flmErrorCodeEntry( NE_XFLM_RFL_DISK_FULL), + flmErrorCodeEntry( NE_XFLM_MUST_WAIT_CHECKPOINT), + flmErrorCodeEntry( NE_XFLM_MISSING_ENC_ALGORITHM), + flmErrorCodeEntry( NE_XFLM_INVALID_ENC_ALGORITHM), + flmErrorCodeEntry( NE_XFLM_INVALID_ENC_KEY_SIZE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_DATA_TYPE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_STATE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_ELEMENT_NAME), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_ATTRIBUTE_NAME), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_COLLECTION_NAME), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_INDEX_NAME), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_ELEMENT_NUMBER), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_ATTRIBUTE_NUMBER), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_COLLECTION_NUMBER), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_INDEX_NUMBER), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_ENCDEF_NUMBER), + flmErrorCodeEntry( NE_XFLM_COLLECTION_NAME_MISMATCH), + flmErrorCodeEntry( NE_XFLM_ELEMENT_NAME_MISMATCH), + flmErrorCodeEntry( NE_XFLM_ATTRIBUTE_NAME_MISMATCH), + flmErrorCodeEntry( NE_XFLM_INVALID_COMPARE_RULE), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_KEY_COMPONENT), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_DATA_COMPONENT), + flmErrorCodeEntry( NE_XFLM_MISSING_KEY_COMPONENT), + flmErrorCodeEntry( NE_XFLM_MISSING_DATA_COMPONENT), + flmErrorCodeEntry( NE_XFLM_INVALID_INDEX_OPTION), + flmErrorCodeEntry( NE_XFLM_NO_MORE_ATTRIBUTE_NUMS), + flmErrorCodeEntry( NE_XFLM_MISSING_ELEMENT_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_ATTRIBUTE_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_ELEMENT_NUMBER), + flmErrorCodeEntry( NE_XFLM_MISSING_ATTRIBUTE_NUMBER), + flmErrorCodeEntry( NE_XFLM_MISSING_INDEX_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_INDEX_NUMBER), + flmErrorCodeEntry( NE_XFLM_MISSING_COLLECTION_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_COLLECTION_NUMBER), + flmErrorCodeEntry( NE_XFLM_BAD_SEN), + flmErrorCodeEntry( NE_XFLM_MISSING_ENCDEF_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_ENCDEF_NUMBER), + flmErrorCodeEntry( NE_XFLM_NO_MORE_INDEX_NUMS), + flmErrorCodeEntry( NE_XFLM_NO_MORE_COLLECTION_NUMS), + flmErrorCodeEntry( NE_XFLM_CANNOT_DEL_ATTRIBUTE), + flmErrorCodeEntry( NE_XFLM_TOO_MANY_PENDING_NODES), + flmErrorCodeEntry( NE_XFLM_UNSUPPORTED_INTERFACE), + flmErrorCodeEntry( NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG), + flmErrorCodeEntry( NE_XFLM_DUP_SIBLING_IX_COMPONENTS), + flmErrorCodeEntry( NE_XFLM_RFL_FILE_NOT_FOUND), + flmErrorCodeEntry( NE_XFLM_BAD_RCODE_TABLE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_KEY_COMPONENT_NUM), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_DATA_COMPONENT_NUM), + flmErrorCodeEntry( NE_XFLM_CLASS_NOT_AVAILABLE), + flmErrorCodeEntry( NE_XFLM_BUFFER_OVERFLOW), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_PREFIX_NUMBER), + flmErrorCodeEntry( NE_XFLM_MISSING_PREFIX_NAME), + flmErrorCodeEntry( NE_XFLM_MISSING_PREFIX_NUMBER), + flmErrorCodeEntry( NE_XFLM_UNDEFINED_ELEMENT_NAME), + flmErrorCodeEntry( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME), + flmErrorCodeEntry( NE_XFLM_DUPLICATE_PREFIX_NAME), + flmErrorCodeEntry( NE_XFLM_KEY_OVERFLOW), + flmErrorCodeEntry( NE_XFLM_UNESCAPED_METACHAR), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_QUANTIFIER), + flmErrorCodeEntry( NE_XFLM_UNEXPECTED_END_OF_EXPR), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_MIN_COUNT), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_MAX_COUNT), + flmErrorCodeEntry( NE_XFLM_EMPTY_BRANCH_IN_EXPR), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_RPAREN_IN_EXPR), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_CLASS_SUBTRACTION), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_CHAR_RANGE_IN_EXPR), + flmErrorCodeEntry( NE_XFLM_BAD_BASE64_ENCODING), + flmErrorCodeEntry( NE_XFLM_NAMESPACE_NOT_ALLOWED), + flmErrorCodeEntry( NE_XFLM_INVALID_NAMESPACE_DECL), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_NAMESPACE_DECL_DATATYPE), + flmErrorCodeEntry( NE_XFLM_UNEXPECTED_END_OF_INPUT), + flmErrorCodeEntry( NE_XFLM_NO_MORE_PREFIX_NUMS), + flmErrorCodeEntry( NE_XFLM_NO_MORE_ENCDEF_NUMS), + flmErrorCodeEntry( NE_XFLM_COLLECTION_OFFLINE), + flmErrorCodeEntry( NE_XFLM_INVALID_XML), + flmErrorCodeEntry( NE_XFLM_READ_ONLY), + flmErrorCodeEntry( NE_XFLM_DELETE_NOT_ALLOWED), + flmErrorCodeEntry( NE_XFLM_RESET_NEEDED), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_REQUIRED_VALUE), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_INDEX_COMPONENT), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE), + flmErrorCodeEntry( NE_XFLM_DATA_TYPE_MUST_BE_NO_DATA), + flmErrorCodeEntry( NE_XFLM_ILLEGAL_FLAG), + flmErrorCodeEntry( NE_XFLM_CANNOT_SET_REQUIRED), + flmErrorCodeEntry( NE_XFLM_CANNOT_SET_LIMIT), + flmErrorCodeEntry( NE_XFLM_CANNOT_SET_INDEX_ON), + flmErrorCodeEntry( NE_XFLM_CANNOT_SET_COMPARE_RULES), + flmErrorCodeEntry( NE_XFLM_INPUT_PENDING), + flmErrorCodeEntry( NE_XFLM_INVALID_NODE_TYPE), + flmErrorCodeEntry( NE_XFLM_INVALID_CHILD_ELM_NODE_ID) +}; + +F_ERROR_CODE_MAP gv_FlmDomErrors[ + NE_XFLM_LAST_DOM_ERROR - NE_XFLM_FIRST_DOM_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_DOM_HIERARCHY_REQUEST_ERR), + flmErrorCodeEntry( NE_XFLM_DOM_WRONG_DOCUMENT_ERR), + flmErrorCodeEntry( NE_XFLM_DOM_DATA_ERROR), + flmErrorCodeEntry( NE_XFLM_DOM_NODE_NOT_FOUND), + flmErrorCodeEntry( NE_XFLM_DOM_INVALID_CHILD_TYPE), + flmErrorCodeEntry( NE_XFLM_DOM_NODE_DELETED), + flmErrorCodeEntry( NE_XFLM_DOM_DUPLICATE_ELEMENT) +}; + +F_ERROR_CODE_MAP gv_FlmIoErrors[ + NE_XFLM_LAST_IO_ERROR - NE_XFLM_FIRST_IO_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_IO_ACCESS_DENIED), + flmErrorCodeEntry( NE_XFLM_IO_BAD_FILE_HANDLE), + flmErrorCodeEntry( NE_XFLM_IO_COPY_ERR), + flmErrorCodeEntry( NE_XFLM_IO_DISK_FULL), + flmErrorCodeEntry( NE_XFLM_IO_END_OF_FILE), + flmErrorCodeEntry( NE_XFLM_IO_OPEN_ERR), + flmErrorCodeEntry( NE_XFLM_IO_SEEK_ERR), + flmErrorCodeEntry( NE_XFLM_IO_DIRECTORY_ERR), + flmErrorCodeEntry( NE_XFLM_IO_PATH_NOT_FOUND), + flmErrorCodeEntry( NE_XFLM_IO_TOO_MANY_OPEN_FILES), + flmErrorCodeEntry( NE_XFLM_IO_PATH_TOO_LONG), + flmErrorCodeEntry( NE_XFLM_IO_NO_MORE_FILES), + flmErrorCodeEntry( NE_XFLM_IO_DELETING_FILE), + flmErrorCodeEntry( NE_XFLM_IO_FILE_LOCK_ERR), + flmErrorCodeEntry( NE_XFLM_IO_FILE_UNLOCK_ERR), + flmErrorCodeEntry( NE_XFLM_IO_PATH_CREATE_FAILURE), + flmErrorCodeEntry( NE_XFLM_IO_RENAME_FAILURE), + flmErrorCodeEntry( NE_XFLM_IO_INVALID_PASSWORD), + flmErrorCodeEntry( NE_XFLM_SETTING_UP_FOR_READ), + flmErrorCodeEntry( NE_XFLM_SETTING_UP_FOR_WRITE), + flmErrorCodeEntry( NE_XFLM_IO_CANNOT_REDUCE_PATH), + flmErrorCodeEntry( NE_XFLM_INITIALIZING_IO_SYSTEM), + flmErrorCodeEntry( NE_XFLM_FLUSHING_FILE), + flmErrorCodeEntry( NE_XFLM_IO_INVALID_FILENAME), + flmErrorCodeEntry( NE_XFLM_IO_CONNECT_ERROR), + flmErrorCodeEntry( NE_XFLM_OPENING_FILE), + flmErrorCodeEntry( NE_XFLM_DIRECT_OPENING_FILE), + flmErrorCodeEntry( NE_XFLM_CREATING_FILE), + flmErrorCodeEntry( NE_XFLM_DIRECT_CREATING_FILE), + flmErrorCodeEntry( NE_XFLM_READING_FILE), + flmErrorCodeEntry( NE_XFLM_DIRECT_READING_FILE), + flmErrorCodeEntry( NE_XFLM_WRITING_FILE), + flmErrorCodeEntry( NE_XFLM_DIRECT_WRITING_FILE), + flmErrorCodeEntry( NE_XFLM_POSITIONING_IN_FILE), + flmErrorCodeEntry( NE_XFLM_GETTING_FILE_SIZE), + flmErrorCodeEntry( NE_XFLM_TRUNCATING_FILE), + flmErrorCodeEntry( NE_XFLM_PARSING_FILE_NAME), + flmErrorCodeEntry( NE_XFLM_CLOSING_FILE), + flmErrorCodeEntry( NE_XFLM_GETTING_FILE_INFO), + flmErrorCodeEntry( NE_XFLM_EXPANDING_FILE), + flmErrorCodeEntry( NE_XFLM_CHECKING_FILE_EXISTENCE), + flmErrorCodeEntry( NE_XFLM_RENAMING_FILE), + flmErrorCodeEntry( NE_XFLM_SETTING_FILE_INFO) +}; + +F_ERROR_CODE_MAP gv_FlmNetErrors[ + NE_XFLM_LAST_NET_ERROR - NE_XFLM_FIRST_NET_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_SVR_NOIP_ADDR), + flmErrorCodeEntry( NE_XFLM_SVR_SOCK_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_CONNECT_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_BIND_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_LISTEN_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_ACCEPT_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_SELECT_ERR), + flmErrorCodeEntry( NE_XFLM_SVR_SOCKOPT_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_DISCONNECT), + flmErrorCodeEntry( NE_XFLM_SVR_READ_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_WRT_FAIL), + flmErrorCodeEntry( NE_XFLM_SVR_READ_TIMEOUT), + flmErrorCodeEntry( NE_XFLM_SVR_WRT_TIMEOUT), + flmErrorCodeEntry( NE_XFLM_SVR_ALREADY_CLOSED) +}; + +F_ERROR_CODE_MAP gv_FlmQueryErrors[ + NE_XFLM_LAST_QUERY_ERROR - NE_XFLM_FIRST_QUERY_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_Q_UNMATCHED_RPAREN), + flmErrorCodeEntry( NE_XFLM_Q_UNEXPECTED_LPAREN), + flmErrorCodeEntry( NE_XFLM_Q_UNEXPECTED_RPAREN), + flmErrorCodeEntry( NE_XFLM_Q_EXPECTING_OPERAND), + flmErrorCodeEntry( NE_XFLM_Q_EXPECTING_OPERATOR), + flmErrorCodeEntry( NE_XFLM_Q_UNEXPECTED_COMMA), + flmErrorCodeEntry( NE_XFLM_Q_EXPECTING_LPAREN), + flmErrorCodeEntry( NE_XFLM_Q_UNEXPECTED_VALUE), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_NUM_FUNC_ARGS), + flmErrorCodeEntry( NE_XFLM_Q_UNEXPECTED_XPATH_COMPONENT), + flmErrorCodeEntry( NE_XFLM_Q_ILLEGAL_LBRACKET), + flmErrorCodeEntry( NE_XFLM_Q_ILLEGAL_RBRACKET), + flmErrorCodeEntry( NE_XFLM_Q_ILLEGAL_OPERAND), + flmErrorCodeEntry( NE_XFLM_Q_ALREADY_OPTIMIZED), + flmErrorCodeEntry( NE_XFLM_Q_MISMATCHED_DB), + flmErrorCodeEntry( NE_XFLM_Q_ILLEGAL_OPERATOR), + flmErrorCodeEntry( NE_XFLM_Q_ILLEGAL_COMPARE_RULES), + flmErrorCodeEntry( NE_XFLM_Q_INCOMPLETE_QUERY_EXPR), + flmErrorCodeEntry( NE_XFLM_Q_NOT_POSITIONED), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_NODE_ID_VALUE), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_META_DATA_TYPE), + flmErrorCodeEntry( NE_XFLM_Q_NEW_EXPR_NOT_ALLOWED), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_CONTEXT_POS), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_FUNC_ARG), + flmErrorCodeEntry( NE_XFLM_Q_EXPECTING_RPAREN), + flmErrorCodeEntry( NE_XFLM_Q_TOO_LATE_TO_ADD_SORT_KEYS), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_SORT_KEY_COMPONENT), + flmErrorCodeEntry( NE_XFLM_Q_DUPLICATE_SORT_KEY_COMPONENT), + flmErrorCodeEntry( NE_XFLM_Q_MISSING_SORT_KEY_COMPONENT), + flmErrorCodeEntry( NE_XFLM_Q_NO_SORT_KEY_COMPONENTS_SPECIFIED), + flmErrorCodeEntry( NE_XFLM_Q_SORT_KEY_CONTEXT_MUST_BE_ELEMENT), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_ELEMENT_NUM_IN_SORT_KEYS), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_ATTR_NUM_IN_SORT_KEYS), + flmErrorCodeEntry( NE_XFLM_Q_NON_POSITIONABLE_QUERY), + flmErrorCodeEntry( NE_XFLM_Q_INVALID_POSITION) +}; + +F_ERROR_CODE_MAP gv_FlmStreamErrors[ + NE_XFLM_LAST_STREAM_ERROR - NE_XFLM_FIRST_STREAM_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_STREAM_DECOMPRESS_ERROR), + flmErrorCodeEntry( NE_XFLM_STREAM_NOT_COMPRESSED), + flmErrorCodeEntry( NE_XFLM_STREAM_TOO_MANY_FILES) +}; + +F_ERROR_CODE_MAP gv_FlmNiciErrors[ + NE_XFLM_LAST_NICI_ERROR - NE_XFLM_FIRST_NICI_ERROR - 1] = +{ + flmErrorCodeEntry( NE_XFLM_NICI_CONTEXT), + flmErrorCodeEntry( NE_XFLM_NICI_ATTRIBUTE_VALUE), + flmErrorCodeEntry( NE_XFLM_NICI_BAD_ATTRIBUTE), + flmErrorCodeEntry( NE_XFLM_NICI_WRAPKEY_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_UNWRAPKEY_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_INVALID_ALGORITHM), + flmErrorCodeEntry( NE_XFLM_NICI_GENKEY_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_BAD_RANDOM), + flmErrorCodeEntry( NE_XFLM_PBE_ENCRYPT_FAILED), + flmErrorCodeEntry( NE_XFLM_PBE_DECRYPT_FAILED), + flmErrorCodeEntry( NE_XFLM_DIGEST_INIT_FAILED), + flmErrorCodeEntry( NE_XFLM_DIGEST_FAILED), + flmErrorCodeEntry( NE_XFLM_INJECT_KEY_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_FIND_INIT), + flmErrorCodeEntry( NE_XFLM_NICI_FIND_OBJECT), + flmErrorCodeEntry( NE_XFLM_NICI_KEY_NOT_FOUND), + flmErrorCodeEntry( NE_XFLM_NICI_ENC_INIT_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_ENCRYPT_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_DECRYPT_INIT_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_DECRYPT_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_WRAPKEY_NOT_FOUND), + flmErrorCodeEntry( NE_XFLM_NOT_EXPECTING_PASSWORD), + flmErrorCodeEntry( NE_XFLM_EXPECTING_PASSWORD), + flmErrorCodeEntry( NE_XFLM_EXTRACT_KEY_FAILED), + flmErrorCodeEntry( NE_XFLM_NICI_INIT_FAILED), + flmErrorCodeEntry( NE_XFLM_BAD_ENCKEY_SIZE), + flmErrorCodeEntry( NE_XFLM_ENCRYPTION_UNAVAILABLE) +}; + +/**************************************************************************** +Desc: This routine allocates and initializes a hash table. +****************************************************************************/ +RCODE flmAllocHashTbl( + FLMUINT uiHashTblSize, + FBUCKET ** ppHashTblRV) +{ + RCODE rc = NE_XFLM_OK; + FBUCKET * pHashTbl = NULL; + F_RandomGenerator RandGen; + FLMUINT uiCnt; + FLMUINT uiRandVal; + FLMUINT uiTempVal; + + // Allocate memory for the hash table + + if (RC_BAD( rc = f_calloc( + (FLMUINT)(sizeof( FBUCKET)) * uiHashTblSize, &pHashTbl))) + { + goto Exit; + } + + // Initialize the hash table + + RandGen.randomSetSeed( 1); + + for (uiCnt = 0; uiCnt < uiHashTblSize; uiCnt++) + { + pHashTbl [uiCnt].uiHashValue = (FLMBYTE)uiCnt; + pHashTbl [uiCnt].pFirstInBucket = NULL; + } + + if( uiHashTblSize <= 256) + { + for( uiCnt = 0; uiCnt < uiHashTblSize - 1; uiCnt++) + { + uiRandVal = (FLMBYTE) RandGen.randomChoice( (FLMINT32)uiCnt, + (FLMINT32)(uiHashTblSize - 1)); + if( uiRandVal != uiCnt) + { + uiTempVal = (FLMBYTE)pHashTbl [uiCnt].uiHashValue; + pHashTbl [uiCnt].uiHashValue = pHashTbl [uiRandVal].uiHashValue; + pHashTbl [uiRandVal].uiHashValue = uiTempVal; + } + } + } + +Exit: + + *ppHashTblRV = pHashTbl; + return( rc); +} + +#ifdef FLM_CAN_GET_PHYS_MEM +/**************************************************************************** +Desc: This routine determines the number of cache bytes to use for caching + based on a percentage of available physical memory or a percentage + of physical memory (depending on bCalcOnAvailMem flag). + uiBytesCurrentlyInUse indicates how many bytes are currently allocated + by FLAIM - so it can factor that in if the calculation is to be based + on the available memory. + Lower limit is 1 megabyte. +****************************************************************************/ +FSTATIC FLMUINT flmGetCacheBytes( + FLMUINT uiPercent, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bCalcOnAvailMem, + FLMUINT uiBytesCurrentlyInUse + ) +{ + FLMUINT uiMem; +#ifdef FLM_WIN + MEMORYSTATUS MemStatus; +#elif defined( FLM_UNIX) + FLMUINT uiProcMemLimit = HIGH_FLMUINT; + FLMUINT uiProcVMemLimit = HIGH_FLMUINT; +#endif + +#ifdef FLM_WIN + GlobalMemoryStatus( &MemStatus); + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)MemStatus.dwAvailPhys + : (FLMUINT)MemStatus.dwTotalPhys); +#elif defined( FLM_UNIX) + { + #if defined( RLIMIT_VMEM) + { + struct rlimit rlim; + + // Bump the process soft virtual limit up to the hard limit + + if( getrlimit( RLIMIT_VMEM, &rlim) == 0) + { + if( rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + (void)setrlimit( RLIMIT_VMEM, &rlim); + if( getrlimit( RLIMIT_VMEM, &rlim) != 0) + { + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + } + } + + if( rlim.rlim_cur != RLIM_INFINITY) + { + uiProcVMemLimit = (FLMUINT)rlim.rlim_cur; + } + } + } + #endif + + #if defined( RLIMIT_DATA) + { + struct rlimit rlim; + + // Bump the process soft heap limit up to the hard limit + + if( getrlimit( RLIMIT_DATA, &rlim) == 0) + { + if( rlim.rlim_cur < rlim.rlim_max) + { + rlim.rlim_cur = rlim.rlim_max; + (void)setrlimit( RLIMIT_DATA, &rlim); + if( getrlimit( RLIMIT_DATA, &rlim) != 0) + { + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + } + } + + if( rlim.rlim_cur != RLIM_INFINITY) + { + uiProcMemLimit = (FLMUINT)rlim.rlim_cur; + } + } + } + #endif + +#ifdef FLM_AIX + struct vminfo tmpvminfo; + #ifdef _SC_PAGESIZE + long iPageSize = sysconf(_SC_PAGESIZE); + #else + long iPageSize = 4096; + #endif + + if( iPageSize == -1) + { + // If sysconf returned an error, resort to using the default + // page size for the Power architecture. + + iPageSize = 4096; + } + + uiMem = HIGH_FLMUINT; + + if( vmgetinfo( &tmpvminfo, VMINFO, sizeof( tmpvminfo)) != -1) + { + if( bCalcOnAvailMem) + { + if( tmpvminfo.numfrb < HIGH_FLMUINT) + { + uiMem = (FLMUINT)tmpvminfo.numfrb; + } + } + else + { + if( tmpvminfo.memsizepgs < HIGH_FLMUINT) + { + uiMem = (FLMUINT)tmpvminfo.memsizepgs; + } + } + } + + if (HIGH_FLMUINT / (FLMUINT)iPageSize >= uiMem) + { + uiMem *= (FLMUINT)iPageSize; + } + else + { + uiMem = HIGH_FLMUINT; + } +#elif defined( FLM_LINUX) + + FLMUINT64 ui64TotalMem; + FLMUINT64 ui64AvailMem; + FLMUINT64 ui64Mem; + + flmGetLinuxMemInfo( &ui64TotalMem, &ui64AvailMem); + + ui64Mem = bCalcOnAvailMem + ? ui64AvailMem + : ui64TotalMem; + + if( HIGH_FLMUINT >= ui64Mem) + { + uiMem = (FLMUINT)ui64Mem; + } + else + { + uiMem = HIGH_FLMUINT; + } + +#else + + long iPageSize = sysconf( _SC_PAGESIZE); + + // Get the amount of memory available to the system + + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)sysconf(_SC_AVPHYS_PAGES) + : (FLMUINT)sysconf(_SC_PHYS_PAGES)); + + if (HIGH_FLMUINT / (FLMUINT)iPageSize >= uiMem) + { + uiMem *= (FLMUINT)iPageSize; + } + else + { + uiMem = HIGH_FLMUINT; + } +#endif + } +#elif defined( FLM_NLM) + { + + #ifndef _SC_PHYS_PAGES + #define _SC_PHYS_PAGES 56 + #endif + #ifndef _SCAVPHYS_PAGES + #define _SC_AVPHYS_PAGES 57 + #endif + + long iPageSize = sysconf(_SC_PAGESIZE); + + // Get the amount of memory available to the system + + uiMem = (FLMUINT)((bCalcOnAvailMem) + ? (FLMUINT)sysconf(_SC_AVPHYS_PAGES) + : (FLMUINT)sysconf(_SC_PHYS_PAGES)); + + + if (HIGH_FLMUINT / (FLMUINT)iPageSize >= uiMem) + { + uiMem *= (FLMUINT)iPageSize; + } + else + { + uiMem = HIGH_FLMUINT; + } + } +#else + #error Getting physical memory is not supported by this platform. +#endif + + // If we are basing the calculation on available physical memory, + // take in to account what has already been allocated. + + if (bCalcOnAvailMem) + { + if (uiMem > HIGH_FLMUINT - uiBytesCurrentlyInUse) + { + uiMem = HIGH_FLMUINT; + } + else + { + uiMem += uiBytesCurrentlyInUse; + } + } + + // Determine if there are limits on the amount of memory the + // process can access and reset uiMem accordingly. There may + // be more available memory than the process is able to access. + +#ifdef FLM_WIN + + // There could be more physical memory in the system than we could + // actually allocate in our virtual address space. Thus, we need to + // make sure that we never exceed our total virtual address space. + + if (uiMem > (FLMUINT)MemStatus.dwTotalVirtual) + { + uiMem = (FLMUINT)MemStatus.dwTotalVirtual; + } + +#elif defined( FLM_UNIX) + + // The process might be limited in the amount of memory it + // can access. + + if ( uiMem > uiProcMemLimit) + { + uiMem = uiProcMemLimit; + } + + if( uiMem > uiProcVMemLimit) + { + uiMem = uiProcVMemLimit; + } + +#endif + + // If uiMax is zero, use uiMinToLeave to calculate the maximum. + + if (!uiMax) + { + if (!uiMinToLeave) + { + uiMax = uiMem; + } + else if (uiMinToLeave < uiMem) + { + uiMax = uiMem - uiMinToLeave; + } + else + { + uiMax = 0; + } + } + + // Calculate memory as a percentage of memory. + + uiMem = (FLMUINT)((uiMem > HIGH_FLMUINT / 100) + ? (FLMUINT)(uiMem / 100) * uiPercent + : (FLMUINT)(uiMem * uiPercent) / 100); + + // Don't go above the maximum. + + if (uiMem > uiMax) + { + uiMem = uiMax; + } + + // Don't go below the minimum. + + if (uiMem < uiMin) + { + uiMem = uiMin; + } + return( uiMem); +} +#endif + +/*************************************************************************** +Desc: Verify that the distance (in bytes) between pvStart and pvEnd is + what was specified in uiOffset. +****************************************************************************/ +FINLINE void flmVerifyOffset( + FLMUINT uiCompilerOffset, + FLMUINT uiOffset, + RCODE * pRc + ) +{ + if (RC_OK( *pRc)) + { + if ( uiCompilerOffset != uiOffset) + { + *pRc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + } +} + +/*************************************************************************** +Desc: Verify the offsets of each member of every on-disk structure. This + is a safety check to ensure that things work correctly on every + platform. +****************************************************************************/ +FSTATIC RCODE flmVerifyDiskStructOffsets( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSizeOf; + + // Verify the XFLM_DB_HDR offsets. + + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, szSignature[0]), + XFLM_DB_HDR_szSignature_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8IsLittleEndian), + XFLM_DB_HDR_ui8IsLittleEndian_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8DefaultLanguage), + XFLM_DB_HDR_ui8DefaultLanguage_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui16BlockSize), + XFLM_DB_HDR_ui16BlockSize_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32DbVersion), + XFLM_DB_HDR_ui32DbVersion_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8BlkChkSummingEnabled), + XFLM_DB_HDR_ui8BlkChkSummingEnabled_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8RflKeepFiles), + XFLM_DB_HDR_ui8RflKeepFiles_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8RflAutoTurnOffKeep), + XFLM_DB_HDR_ui8RflAutoTurnOffKeep_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui8RflKeepAbortedTrans), + XFLM_DB_HDR_ui8RflKeepAbortedTrans_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflCurrFileNum), + XFLM_DB_HDR_ui32RflCurrFileNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui64LastRflCommitID), + XFLM_DB_HDR_ui64LastRflCommitID_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflLastFileNumDeleted), + XFLM_DB_HDR_ui32RflLastFileNumDeleted_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflLastTransOffset), + XFLM_DB_HDR_ui32RflLastTransOffset_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflLastCPFileNum), + XFLM_DB_HDR_ui32RflLastCPFileNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflLastCPOffset), + XFLM_DB_HDR_ui32RflLastCPOffset_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui64RflLastCPTransID), + XFLM_DB_HDR_ui64RflLastCPTransID_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflMinFileSize), + XFLM_DB_HDR_ui32RflMinFileSize_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RflMaxFileSize), + XFLM_DB_HDR_ui32RflMaxFileSize_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui64CurrTransID), + XFLM_DB_HDR_ui64CurrTransID_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui64TransCommitCnt), + XFLM_DB_HDR_ui64TransCommitCnt_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RblEOF), + XFLM_DB_HDR_ui32RblEOF_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32RblFirstCPBlkAddr), + XFLM_DB_HDR_ui32RblFirstCPBlkAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32FirstAvailBlkAddr), + XFLM_DB_HDR_ui32FirstAvailBlkAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32FirstLFBlkAddr), + XFLM_DB_HDR_ui32FirstLFBlkAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32LogicalEOF), + XFLM_DB_HDR_ui32LogicalEOF_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32MaxFileSize), + XFLM_DB_HDR_ui32MaxFileSize_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui64LastBackupTransID), + XFLM_DB_HDR_ui64LastBackupTransID_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32IncBackupSeqNum), + XFLM_DB_HDR_ui32IncBackupSeqNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32BlksChangedSinceBackup), + XFLM_DB_HDR_ui32BlksChangedSinceBackup_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ucDbSerialNum[0]), + XFLM_DB_HDR_ucDbSerialNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ucLastTransRflSerialNum[0]), + XFLM_DB_HDR_ucLastTransRflSerialNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ucNextRflSerialNum[0]), + XFLM_DB_HDR_ucNextRflSerialNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ucIncBackupSerialNum[0]), + XFLM_DB_HDR_ucIncBackupSerialNum_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32DbKeyLen), + XFLM_DB_HDR_ui32DbKeyLen, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ucReserved[0]), + XFLM_DB_HDR_ucReserved_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, ui32HdrCRC), + XFLM_DB_HDR_ui32HdrCRC_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(XFLM_DB_HDR, DbKey[0]), + XFLM_DB_HDR_DbKey, &rc); + + // Have to use a variable for sizeof. If we don't, compiler barfs + // because we are comparing two constants. + + uiSizeOf = XFLM_DB_HDR_DbKey + XFLM_MAX_ENC_KEY_SIZE; + if( sizeof( XFLM_DB_HDR) != uiSizeOf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + + // Verify the offsets in the F_BLK_HDR structure. + + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32BlkAddr), + F_BLK_HDR_ui32BlkAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32PrevBlkInChain), + F_BLK_HDR_ui32PrevBlkInChain_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32NextBlkInChain), + F_BLK_HDR_ui32NextBlkInChain_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32PriorBlkImgAddr), + F_BLK_HDR_ui32PriorBlkImgAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui64TransID), + F_BLK_HDR_ui64TransID_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui32BlkCRC), + F_BLK_HDR_ui32BlkCRC_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui16BlkBytesAvail), + F_BLK_HDR_ui16BlkBytesAvail_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui8BlkFlags), + F_BLK_HDR_ui8BlkFlags_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.stdBlkHdr.ui8BlkType), + F_BLK_HDR_ui8BlkType_OFFSET, &rc); + + // Have to use a variable for sizeof. If we don't, compiler barfs + // because we are comparing two constants. + + uiSizeOf = SIZEOF_STD_BLK_HDR; + if (sizeof( F_BLK_HDR) != uiSizeOf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + + // Verify the offsets in the F_BTREE_BLK_HDR structure + + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.stdBlkHdr), + F_BTREE_BLK_HDR_stdBlkHdr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16LogicalFile), + F_BTREE_BLK_HDR_ui16LogicalFile_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16NumKeys), + F_BTREE_BLK_HDR_ui16NumKeys_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui8BlkLevel), + F_BTREE_BLK_HDR_ui8BlkLevel_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui8BTreeFlags), + F_BTREE_BLK_HDR_ui8BTreeFlags_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LARGEST_BLK_HDR, all.BTreeBlkHdr.ui16HeapSize), + F_BTREE_BLK_HDR_ui16HeapSize_OFFSET, &rc); + + // Have to use a variable for sizeof. If we don't, compiler barfs + // because we are comparing two constants. + + uiSizeOf = 40; + if (sizeof( F_BTREE_BLK_HDR) != uiSizeOf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + + // Have to use a variable for sizeof. If we don't, compiler barfs + // because we are comparing two constants. + + uiSizeOf = 40; + if (sizeof( F_LARGEST_BLK_HDR) != uiSizeOf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + + // Verify the offsets in the F_LF_HDR structure + + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui32LfNumber), + F_LF_HDR_ui32LfNumber_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui32LfType), + F_LF_HDR_ui32LfType_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui32RootBlkAddr), + F_LF_HDR_ui32RootBlkAddr_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui32EncId), + F_LF_HDR_ui32EncId_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui64NextNodeId), + F_LF_HDR_ui64NextNodeId_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui64FirstDocId), + F_LF_HDR_ui64FirstDocId_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ui64LastDocId), + F_LF_HDR_ui64LastDocId_OFFSET, &rc); + flmVerifyOffset( (FLMUINT)offsetof(F_LF_HDR, ucZeroes[0]), + F_LF_HDR_ucZeroes_OFFSET, &rc); + + // Have to use a variable for sizeof. If we don't, compiler barfs + // because we are comparing two constants. + + uiSizeOf = 64; + if (sizeof( F_LF_HDR) != uiSizeOf) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_PLATFORM_FORMAT); + } + + return( rc); +} + +/**************************************************************************** +Desc: Logs the reason for the "must close" flag being set +****************************************************************************/ +void F_Database::logMustCloseReason( + const char * pszFileName, + FLMINT iLineNumber) +{ + char * pszMsgBuf = NULL; + IF_LogMessageClient * pLogMsg = NULL; + + // Log a message indicating why the "must close" flag was set + + if( (pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) + { + if( RC_OK( f_alloc( F_PATH_MAX_SIZE + 512, &pszMsgBuf))) + { + f_sprintf( (char *)pszMsgBuf, + "Database (%s) must be closed because of a 0x%04X error, " + "File=%s, Line=%d.", + (char *)(m_pszDbPath ? (char *)m_pszDbPath : (char *)""), + (unsigned)m_rcMustClose, + pszFileName, (int)iLineNumber); + + pLogMsg->changeColor( XFLM_YELLOW, XFLM_BLACK); + pLogMsg->appendString( pszMsgBuf); + } + flmEndLogMessage( &pLogMsg); + } + + if( pszMsgBuf) + { + f_free( &pszMsgBuf); + } +} + +/**************************************************************************** +Desc: Acquires a write lock on the database +****************************************************************************/ +RCODE F_Database::dbWriteLock( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + FLMUINT uiTimeout) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = m_pWriteLockObj->Lock( NULL, hWaitSem, FALSE, + (FLMBOOL)(uiTimeout ? TRUE : FALSE), + TRUE, uiTimeout, 0, pDbStats))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine unlocks the write lock on a database. +****************************************************************************/ +void F_Database::dbWriteUnlock( + XFLM_DB_STATS * pDbStats) +{ + (void)m_pWriteLockObj->Unlock( FALSE, NULL, FALSE, pDbStats); +} + +/**************************************************************************** +Desc: This shuts down the background threads +Note: This routine assumes that the global mutex is locked. The mutex will + be unlocked internally, but will always be locked on exit. +****************************************************************************/ +void F_Database::shutdownDatabaseThreads( void) +{ + RCODE rc = NE_XFLM_OK; + F_BKGND_IX * pBackgroundIx; + F_Db * pDb; + F_Thread * pThread; + FLMUINT uiThreadId; + FLMUINT uiThreadCount; + FLMBOOL bMutexLocked = TRUE; + + // Signal all background indexing threads to shutdown and all + // threads in the FLM_DB_THREAD_GROUP that are associated with + // this F_Database. + + for( ;;) + { + uiThreadCount = 0; + + // Shut down all background threads. + + uiThreadId = 0; + for( ;;) + { + if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( + &pThread, FLM_BACKGROUND_INDEXING_THREAD_GROUP, &uiThreadId))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + RC_UNEXPECTED_ASSERT( rc); + } + } + else + { + pBackgroundIx = (F_BKGND_IX *)pThread->getParm1(); + if( pBackgroundIx && pBackgroundIx->pDatabase == this) + { + // Set the thread's terminate flag. + + uiThreadCount++; + pThread->setShutdownFlag(); + } + + pThread->Release(); + pThread = NULL; + } + } + + // Shut down all threads in the FLM_DB_THREAD_GROUP. + + uiThreadId = 0; + for( ;;) + { + if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getNextGroupThread( + &pThread, FLM_DB_THREAD_GROUP, &uiThreadId))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + RC_UNEXPECTED_ASSERT( rc); + } + } + else + { + pDb = (F_Db *)pThread->getParm2(); + if (pDb && pDb->m_pDatabase == this) + { + + // Set the thread's terminate flag. + + uiThreadCount++; + pThread->setShutdownFlag(); + } + + pThread->Release(); + pThread = NULL; + } + } + + if( !uiThreadCount) + { + break; + } + + // Unlock the global mutex + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + bMutexLocked = FALSE; + + // Give the threads a chance to terminate + + f_sleep( 50); + + // Re-lock the mutex and see if any threads are still active + + f_mutexLock( gv_XFlmSysData.hShareMutex); + bMutexLocked = TRUE; + + } + + // Shut down the maintenance thread + + if( m_pMaintThrd) + { + flmAssert( bMutexLocked); + + m_pMaintThrd->setShutdownFlag(); + f_semSignal( m_hMaintSem); + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + m_pMaintThrd->stopThread(); + f_mutexLock( gv_XFlmSysData.hShareMutex); + + m_pMaintThrd->Release(); + m_pMaintThrd = NULL; + f_semDestroy( &m_hMaintSem); + } + + // Re-lock the mutex + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: This routine frees a registered event. +****************************************************************************/ +FSTATIC void flmFreeEvent( + FEVENT * pEvent, + F_MUTEX hMutex, + FEVENT ** ppEventListRV) +{ + pEvent->pEventClient->Release(); + f_mutexLock( hMutex); + if (pEvent->pPrev) + { + pEvent->pPrev->pNext = pEvent->pNext; + } + else + { + *ppEventListRV = pEvent->pNext; + } + if (pEvent->pNext) + { + pEvent->pNext->pPrev = pEvent->pPrev; + } + f_mutexUnlock( hMutex); + f_free( &pEvent); +} + +/**************************************************************************** +Desc: This routine determines the hash bucket for a string. +****************************************************************************/ +FLMUINT flmStrHashBucket( + char * pszStr, // Pointer to string to be hashed + FBUCKET * pHashTbl, // Hash table to use + FLMUINT uiNumBuckets) // Number of hash buckets +{ + FLMUINT uiHashIndex; + + if ((uiHashIndex = (FLMUINT)*pszStr) >= uiNumBuckets) + { + uiHashIndex -= uiNumBuckets; + } + + while (*pszStr) + { + if ((uiHashIndex = + (FLMUINT)((pHashTbl [uiHashIndex].uiHashValue) ^ (FLMUINT)(f_toupper( *pszStr)))) >= + uiNumBuckets) + { + uiHashIndex -= uiNumBuckets; + } + pszStr++; + } + + return( uiHashIndex); +} + +/**************************************************************************** +Desc: This routine links a notify request into a notification list and + then waits to be notified that the event has occurred. +Notes: This routine assumes that the shared mutex is locked and that + it is supposed to unlock it. +****************************************************************************/ +RCODE flmWaitNotifyReq( + F_MUTEX hMutex, + F_SEM hSem, + FNOTIFY ** ppNotifyListRV, + void * pvUserData) +{ + RCODE rc = NE_XFLM_OK; + RCODE TempRc; + FNOTIFY notifyItem; + + notifyItem.hSem = hSem; + notifyItem.uiThreadId = f_threadId(); + notifyItem.pRc = &rc; + notifyItem.pvUserData = pvUserData; + notifyItem.pNext = *ppNotifyListRV; + *ppNotifyListRV = ¬ifyItem; + + f_mutexUnlock( hMutex); + + if( RC_BAD( TempRc = f_semWait( notifyItem.hSem, F_SEM_WAITFOREVER))) + { + rc = TempRc; + } + + f_mutexLock( hMutex); + return( rc); +} + +/**************************************************************************** +Desc: This routine links an F_Database structure to its name hash bucket. + NOTE: This function assumes that the global mutex has been + locked. +****************************************************************************/ +RCODE F_Database::linkToBucket( void) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pTmpDatabase; + FBUCKET * pBucket; + FLMUINT uiBucket; + + pBucket = gv_XFlmSysData.pDatabaseHashTbl; + uiBucket = flmStrHashBucket( m_pszDbPath, pBucket, FILE_HASH_ENTRIES); + pBucket = &pBucket [uiBucket]; + if (pBucket->pFirstInBucket) + { + pTmpDatabase = (F_Database *)pBucket->pFirstInBucket; + pTmpDatabase->m_pPrev = this; + } + + m_uiBucket = uiBucket; + m_pPrev = NULL; + m_pNext = (F_Database *)pBucket->pFirstInBucket; + pBucket->pFirstInBucket = this; + + return( rc); +} + +/**************************************************************************** +Desc: This routine checks unused structures to see if any have been unused + longer than the maximum unused time. If so, it frees them up. +Note: This routine assumes that the calling routine has locked the global + mutex prior to calling this routine. The mutex may be unlocked and + re-locked by one of the called routines. +****************************************************************************/ +void F_DbSystem::checkNotUsedObjects( void) +{ + + // Look for unused file handles + + if( gv_XFlmSysData.pFileHdlMgr) + { + gv_XFlmSysData.pFileHdlMgr->checkAgedFileHdls( + gv_XFlmSysData.pFileHdlMgr->getMaxAvailTime()); + } +} + +/**************************************************************************** +Desc: This routine links an FDB structure to an F_Database structure. + NOTE: This routine assumes that the global mutex has been + locked. +****************************************************************************/ +RCODE F_Db::linkToDatabase( + F_Database * pDatabase + ) +{ + RCODE rc = NE_XFLM_OK; + + // If the use count on the file used to be zero, unlink it from the + // unused list. + + flmAssert( !m_pDatabase); + m_pPrevForDatabase = NULL; + if ((m_pNextForDatabase = pDatabase->m_pFirstDb) != NULL) + { + pDatabase->m_pFirstDb->m_pPrevForDatabase = this; + } + + pDatabase->m_pFirstDb = this; + m_pDatabase = pDatabase; + + if (!(m_uiFlags & FDB_INTERNAL_OPEN)) + { + pDatabase->incrOpenCount(); + } + + // Allocate the super file object + + if (!m_pSFileHdl) + { + if ((m_pSFileHdl = f_new F_SuperFileHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Set up the super file + + if( RC_BAD( rc = m_pSFileHdl->Setup( pDatabase->m_pFileIdList, + pDatabase->m_pszDbPath, pDatabase->m_pszDataDir))) + { + goto Exit; + } + + if( pDatabase->m_lastCommittedDbHdr.ui32DbVersion) + { + m_pSFileHdl->SetBlockSize( pDatabase->m_uiBlockSize); + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine unlinks F_Db object from its F_Database structure. + NOTE: This routine assumes that the global mutex has been + locked. +****************************************************************************/ +void F_Db::unlinkFromDatabase( void) +{ + if (!m_pDatabase) + { + return; + } + + // Unlink the F_Db from the F_Database. + + if (m_pNextForDatabase) + { + m_pNextForDatabase->m_pPrevForDatabase = m_pPrevForDatabase; + } + + if (m_pPrevForDatabase) + { + m_pPrevForDatabase->m_pNextForDatabase = m_pNextForDatabase; + } + else + { + m_pDatabase->m_pFirstDb = m_pNextForDatabase; + } + m_pNextForDatabase = m_pPrevForDatabase = NULL; + + // Decrement use counts in the F_Database, unless this was + // an internal open. + + if (!(m_uiFlags & FDB_INTERNAL_OPEN)) + { + m_pDatabase->decrOpenCount(); + } + m_pDatabase = NULL; +} + +/**************************************************************************** +Desc: This routine dynamically adjusts the cache limit if that is the mode + we are in. +****************************************************************************/ +RCODE F_GlobalCacheMgr::adjustCache( + FLMUINT * puiCurrTime, + FLMUINT * puiLastCacheAdjustTime) +{ +#ifndef FLM_CAN_GET_PHYS_MEM + F_UNREFERENCED_PARM( puiCurrTime); + F_UNREFERENCED_PARM( puiLastCacheAdjustTime); + return( RC_SET( NE_XFLM_NOT_IMPLEMENTED)); +#else + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurrTime = *puiCurrTime; + FLMUINT uiLastCacheAdjustTime = *puiLastCacheAdjustTime; + + if (m_bDynamicCacheAdjust && + FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= + m_uiCacheAdjustInterval) + { + FLMUINT uiCacheBytes; + + lockMutex(); + + // Make sure the dynamic adjust flag is still set. + + if (m_bDynamicCacheAdjust && + FLM_ELAPSED_TIME( uiCurrTime, uiLastCacheAdjustTime) >= + m_uiCacheAdjustInterval) + { + uiCacheBytes = flmGetCacheBytes( m_uiCacheAdjustPercent, + m_uiCacheAdjustMin, m_uiCacheAdjustMax, + m_uiCacheAdjustMinToLeave, TRUE, totalBytes()); + if (RC_BAD( rc = setCacheLimit( uiCacheBytes, FALSE))) + { + unlockMutex(); + goto Exit; + } + } + unlockMutex(); + *puiCurrTime = *puiLastCacheAdjustTime = FLM_GET_TIMER(); + } + +Exit: + + return( rc); +#endif +} + +/**************************************************************************** +Desc: This routine functions as a thread. It monitors open files and + frees up files which have been closed longer than the maximum + close time. +****************************************************************************/ +RCODE F_DbSystem::monitorThrd( + F_Thread * pThread) +{ + FLMUINT uiLastUnusedCleanupTime = 0; + FLMUINT uiCurrTime; +#ifdef FLM_CAN_GET_PHYS_MEM + FLMUINT uiLastCacheAdjustTime = 0; +#endif + + for (;;) + { + // See if we should shut down + + if( pThread->getShutdownFlag()) + { + break; + } + + uiCurrTime = FLM_GET_TIMER(); + + // Check the not used stuff and lock timeouts. + + if ( FLM_ELAPSED_TIME( uiCurrTime, uiLastUnusedCleanupTime) >= + gv_XFlmSysData.pGlobalCacheMgr->m_uiUnusedCleanupInterval) + { + // See if any unused structures have bee unused longer than the + // maximum unused time. Free them if they have. + // May unlock and re-lock the global mutex. + + f_mutexLock( gv_XFlmSysData.hShareMutex); + F_DbSystem::checkNotUsedObjects(); + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + + // Reset the timer + + uiCurrTime = uiLastUnusedCleanupTime = FLM_GET_TIMER(); + } + + // Call the lock manager to check timeouts. It is critial + // that this routine be called on a regular interval to + // timeout lock waiters that have expired. + + gv_XFlmSysData.pServerLockMgr->CheckLockTimeouts( FALSE, FALSE); + + // Check the adjusting cache limit + +#ifdef FLM_CAN_GET_PHYS_MEM + (void)gv_XFlmSysData.pGlobalCacheMgr->adjustCache( &uiCurrTime, + &uiLastCacheAdjustTime); +#endif + + f_sleep( 1000); + } + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_DbSystem::cacheCleanupThrd( + F_Thread * pThread) +{ + FLMUINT uiCurrTime; + FLMUINT uiLastDefragTime = 0; + FLMUINT uiLastCleanupTime = 0; + FLMUINT uiDefragInterval; + FLMUINT uiCleanupInterval = gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval; + FLMBOOL bDoNodeCacheFirst = TRUE; + + FLM_SECS_TO_TIMER_UNITS( 120, uiDefragInterval); + + for (;;) + { + if( pThread->getShutdownFlag()) + { + break; + } + + uiCurrTime = FLM_GET_TIMER(); + + // Alternate between reducing node cache and block cache first. + + if (gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || + FLM_ELAPSED_TIME( uiCurrTime, uiLastCleanupTime) >= uiCleanupInterval) + { + if (bDoNodeCacheFirst) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->reduceCache(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + (void)gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + bDoNodeCacheFirst = FALSE; + } + else + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + (void)gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->reduceCache(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + bDoNodeCacheFirst = TRUE; + } + + uiLastCleanupTime = FLM_GET_TIMER(); + } + + if( FLM_ELAPSED_TIME( uiCurrTime, uiLastDefragTime) >= uiDefragInterval) + { + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory(); + gv_XFlmSysData.pNodeCacheMgr->defragmentMemory(); + uiLastDefragTime = FLM_GET_TIMER(); + } + + f_sleep( 500); + } + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: This routine does an event callback. Note that the mutex is + locked during the callback. +****************************************************************************/ +void flmDoEventCallback( + eEventCategory eCategory, + eEventType eEvent, + IF_Db * pDb, + FLMUINT uiThreadId, + FLMUINT64 ui64TransID, + FLMUINT uiIndexOrCollection, + FLMUINT64 ui64NodeId, + RCODE rc) +{ + FEVENT * pEvent; + + f_mutexLock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); + pEvent = gv_XFlmSysData.EventHdrs [eCategory].pEventCBList; + while (pEvent) + { + pEvent->pEventClient->catchEvent( eEvent, pDb, uiThreadId, ui64TransID, + uiIndexOrCollection, ui64NodeId, rc); + pEvent = pEvent->pNext; + } + f_mutexUnlock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); +} + +/**************************************************************************** +Desc: This routine sets the "must close" flags on the + F_Database and its FDBs +****************************************************************************/ +void F_Database::setMustCloseFlags( + RCODE rcMustClose, + FLMBOOL bMutexLocked) +{ + F_Db * pTmpDb; + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hShareMutex); + } + + if( !m_bMustClose) + { + m_bMustClose = TRUE; + m_rcMustClose = rcMustClose; + pTmpDb = m_pFirstDb; + while( pTmpDb) + { + pTmpDb->m_bMustClose = TRUE; + pTmpDb = pTmpDb->m_pNextForDatabase; + } + + // Log a message indicating why the "must close" flag has been + // set. Calling checkState with the bMustClose flag + // already set to TRUE will cause a message to be logged. + + (void)checkState( __FILE__, __LINE__); + } + + if( !bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + } +} + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_HashTable::F_HashTable() +{ + m_hMutex = F_MUTEX_NULL; + m_pGlobalList = NULL; + m_ppHashTable = NULL; + m_uiBuckets = 0; + m_pCRCTable = NULL; + m_bOwnCRCTable = FALSE; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +F_HashTable::~F_HashTable() +{ + F_HashObject * pCur; + F_HashObject * pNext; + + pCur = m_pGlobalList; + while( pCur) + { + pNext = pCur->m_pNextInGlobal; + unlinkObject( pCur); + pCur->Release(); + pCur = pNext; + } + + if( m_ppHashTable) + { + f_free( &m_ppHashTable); + } + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } + + if( m_pCRCTable && m_bOwnCRCTable) + { + f_freeCRCTable( &m_pCRCTable); + } +} + +/**************************************************************************** +Desc: Configures the hash table prior to first use +****************************************************************************/ +RCODE F_HashTable::setupHashTable( + FLMBOOL bMultithreaded, + FLMUINT uiNumBuckets, + FLMUINT32 * pCRCTable) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( uiNumBuckets); + + // Create the hash table + + if( RC_BAD( rc = f_alloc( + sizeof( F_HashObject *) * uiNumBuckets, &m_ppHashTable))) + { + goto Exit; + } + + m_uiBuckets = uiNumBuckets; + f_memset( m_ppHashTable, 0, sizeof( F_HashObject *) * uiNumBuckets); + + if( bMultithreaded) + { + // Initialize the mutex + + if( RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + } + + if( !pCRCTable) + { + // Initialize the CRC table + + if( RC_BAD( rc = f_initCRCTable( &m_pCRCTable))) + { + goto Exit; + } + m_bOwnCRCTable = TRUE; + } + else + { + m_pCRCTable = pCRCTable; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves an object from the hash table with the specified key. + This routine assumes the table's mutex has already been locked. + A reference IS NOT added to the object for the caller. +****************************************************************************/ +RCODE F_HashTable::findObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject) +{ + F_HashObject * pObject = NULL; + FLMUINT uiBucket; + FLMUINT32 ui32CRC = 0; + RCODE rc = NE_XFLM_OK; + + *ppObject = NULL; + + // Calculate the hash bucket and mutex offset + + uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); + + // Search the bucket for an object with a matching + // key. + + pObject = m_ppHashTable[ uiBucket]; + while( pObject) + { + if( pObject->getKeyCRC() == ui32CRC) + { + void * pvTmpKey; + FLMUINT uiTmpKeyLen; + + pvTmpKey = pObject->getKey( &uiTmpKeyLen); + if( uiTmpKeyLen == uiKeyLen && + f_memcmp( pvTmpKey, pvKey, uiKeyLen) == 0) + { + break; + } + } + pObject = pObject->m_pNextInBucket; + } + + if( !pObject) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + *ppObject = pObject; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Adds an object to the hash table +****************************************************************************/ +RCODE F_HashTable::addObject( + F_HashObject * pObject) +{ + FLMUINT uiBucket; + F_HashObject * pTmp; + void * pvKey; + FLMUINT uiKeyLen; + FLMUINT32 ui32CRC; + FLMBOOL bMutexLocked = FALSE; + RCODE rc = NE_XFLM_OK; + + // Calculate and set the objects hash bucket + + flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); + + pvKey = pObject->getKey( &uiKeyLen); + flmAssert( uiKeyLen); + + uiBucket = getHashBucket( pvKey, uiKeyLen, &ui32CRC); + pObject->setKeyCRC( ui32CRC); + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + // Make sure the object doesn't already exist + + if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pTmp))) + { + if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS); + goto Exit; + } + + // Add a reference to the object + + pObject->AddRef(); + + // Link the object into the appropriate lists + + linkObject( pObject, uiBucket); + +Exit: + + // Unlock the mutex + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next object in the linked list of objects in the hash + table. If *ppObject == NULL, the first object will be returned. +****************************************************************************/ +RCODE F_HashTable::getNextObjectInGlobal( + F_HashObject ** ppObject) +{ + FLMBOOL bMutexLocked = FALSE; + F_HashObject * pOldObj; + RCODE rc = NE_XFLM_OK; + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + if( !(*ppObject)) + { + *ppObject = m_pGlobalList; + } + else + { + pOldObj = *ppObject; + *ppObject = (*ppObject)->m_pNextInGlobal; + pOldObj->Release(); + } + + if( *ppObject == NULL) + { + rc = RC_SET( NE_XFLM_EOF_HIT); + goto Exit; + } + + (*ppObject)->AddRef(); + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Retrieves an object from the hash table with the specified key +****************************************************************************/ +RCODE F_HashTable::getObject( + void * pvKey, + FLMUINT uiKeyLen, + F_HashObject ** ppObject, + FLMBOOL bRemove) +{ + F_HashObject * pObject; + FLMBOOL bMutexLocked = FALSE; + RCODE rc = NE_XFLM_OK; + + // Lock the mutex + + if( m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + } + + // Search for an object with a matching key. + + if( RC_BAD( rc = findObject( pvKey, uiKeyLen, &pObject))) + { + goto Exit; + } + + if( pObject && bRemove) + { + unlinkObject( pObject); + if( !ppObject) + { + pObject->Release(); + pObject = NULL; + } + } + + if( ppObject) + { + if( !bRemove) + { + pObject->AddRef(); + } + *ppObject = pObject; + pObject = NULL; + } + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Removes an object from the hash table by key +****************************************************************************/ +RCODE F_HashTable::removeObject( + void * pvKey, + FLMUINT uiKeyLen) +{ + return( getObject( pvKey, uiKeyLen, NULL, TRUE)); +} + +/**************************************************************************** +Desc: Removes an object from the hash table by object pointer +****************************************************************************/ +RCODE F_HashTable::removeObject( + F_HashObject * pObject) +{ + FLMUINT uiKeyLen; + void * pvKey = pObject->getKey( &uiKeyLen); + + return( getObject( pvKey, uiKeyLen, NULL, TRUE)); +} + +/**************************************************************************** +Desc: Calculates the hash bucket of a key and optionally returns the key's + CRC. +****************************************************************************/ +FLMUINT F_HashTable::getHashBucket( + void * pvKey, + FLMUINT uiLen, + FLMUINT32 * pui32KeyCRC) +{ + FLMUINT32 ui32CRC = 0; + + f_updateCRC( m_pCRCTable, (FLMBYTE *)pvKey, uiLen, &ui32CRC); + if( pui32KeyCRC) + { + *pui32KeyCRC = ui32CRC; + } + return( ui32CRC % m_uiBuckets); +} + +/**************************************************************************** +Desc: Links an object to the global list and also to its bucket +Notes: This routine assumes that the bucket's mutex is already locked + if the hash table is multi-threaded. +****************************************************************************/ +void F_HashTable::linkObject( + F_HashObject * pObject, + FLMUINT uiBucket) +{ + flmAssert( uiBucket < m_uiBuckets); + flmAssert( pObject->getHashBucket() == F_INVALID_HASH_BUCKET); + + // Set the object's bucket + + pObject->setHashBucket( uiBucket); + + // Link the object to its hash bucket + + pObject->m_pNextInBucket = m_ppHashTable[ uiBucket]; + if( m_ppHashTable[ uiBucket]) + { + m_ppHashTable[ uiBucket]->m_pPrevInBucket = pObject; + } + m_ppHashTable[ uiBucket] = pObject; + + // Link to the global list + + pObject->m_pNextInGlobal = m_pGlobalList; + if( m_pGlobalList) + { + m_pGlobalList->m_pPrevInGlobal = pObject; + } + m_pGlobalList = pObject; +} + +/**************************************************************************** +Desc: Unlinks an object from its bucket and the global list. +Notes: This routine assumes that the bucket's mutex is already locked + if the hash table is multi-threaded. +****************************************************************************/ +void F_HashTable::unlinkObject( + F_HashObject * pObject) +{ + FLMUINT uiBucket = pObject->getHashBucket(); + + // Is the bucket valid? + + flmAssert( uiBucket < m_uiBuckets); + + // Unlink from the hash bucket + + if( pObject->m_pNextInBucket) + { + pObject->m_pNextInBucket->m_pPrevInBucket = pObject->m_pPrevInBucket; + } + + if( pObject->m_pPrevInBucket) + { + pObject->m_pPrevInBucket->m_pNextInBucket = pObject->m_pNextInBucket; + } + else + { + m_ppHashTable[ uiBucket] = pObject->m_pNextInBucket; + } + + pObject->m_pPrevInBucket = NULL; + pObject->m_pNextInBucket = NULL; + pObject->setHashBucket( F_INVALID_HASH_BUCKET); + + // Unlink from the global list + + if( pObject->m_pNextInGlobal) + { + pObject->m_pNextInGlobal->m_pPrevInGlobal = pObject->m_pPrevInGlobal; + } + + if( pObject->m_pPrevInGlobal) + { + pObject->m_pPrevInGlobal->m_pNextInGlobal = pObject->m_pNextInGlobal; + } + else + { + m_pGlobalList = pObject->m_pNextInGlobal; + } + + pObject->m_pPrevInGlobal = NULL; + pObject->m_pNextInGlobal = NULL; +} + +/*************************************************************************** +Desc: Lock the system data structure for access - called only by startup + and shutdown. NOTE: On platforms that do not support atomic exchange + this is less than perfect - won't handle tight race conditions. +***************************************************************************/ +void F_DbSystem::lockSysData( void) +{ + // Obtain the spin lock + + while( ftkAtomicExchange( &m_ui32FlmSysSpinLock, 1) == 1) + { + f_sleep( 10); + } +} + +/*************************************************************************** +Desc: Unlock the system data structure for access - called only by startup + and shutdown. +***************************************************************************/ +void F_DbSystem::unlockSysData( void) +{ + (void)ftkAtomicExchange( &m_ui32FlmSysSpinLock, 0); +} + +/**************************************************************************** +Desc : Constructor for global cache manager. +****************************************************************************/ +F_GlobalCacheMgr::F_GlobalCacheMgr() +{ + m_pSlabManager = NULL; + m_bCachePreallocated = FALSE; + +#ifdef FLM_CAN_GET_PHYS_MEM + m_bDynamicCacheAdjust = TRUE; + m_uiCacheAdjustPercent = XFLM_DEFAULT_CACHE_ADJUST_PERCENT; + m_uiCacheAdjustMin = XFLM_DEFAULT_CACHE_ADJUST_MIN; + m_uiCacheAdjustMax = XFLM_DEFAULT_CACHE_ADJUST_MAX; + m_uiCacheAdjustMinToLeave = XFLM_DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE; + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_CACHE_ADJUST_INTERVAL, + m_uiCacheAdjustInterval); + m_uiMaxBytes = flmGetCacheBytes( m_uiCacheAdjustPercent, + m_uiCacheAdjustMin, + m_uiCacheAdjustMax, + m_uiCacheAdjustMinToLeave, TRUE, 0); +#else + m_bDynamicCacheAdjust = FALSE; + m_uiCacheAdjustPercent = 0; + m_uiCacheAdjustMin = 0; + m_uiCacheAdjustMax = 0; + m_uiCacheAdjustMinToLeave = 0; + m_uiCacheAdjustInterval = 0; + m_uiMaxBytes = XFLM_DEFAULT_CACHE_ADJUST_MIN; +#endif + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL, + m_uiCacheCleanupInterval); + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_UNUSED_CLEANUP_INTERVAL, + m_uiUnusedCleanupInterval); + m_hMutex = F_MUTEX_NULL; + m_uiMaxSlabs = 0; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_GlobalCacheMgr::~F_GlobalCacheMgr() +{ + if (m_pSlabManager) + { + m_pSlabManager->Release(); + } + if (m_hMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hMutex); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_GlobalCacheMgr::setup( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE szTmpBuffer[ 64]; + + flmAssert( !m_pSlabManager); + + if (RC_BAD( rc = f_mutexCreate( &m_hMutex))) + { + goto Exit; + } + + if( (m_pSlabManager = f_new F_SlabManager) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_getenv( "XFLM_PREALLOC_CACHE_SIZE", szTmpBuffer, sizeof( szTmpBuffer)); + + if( RC_BAD( rc = m_pSlabManager->setup( f_atoi( (char *)szTmpBuffer)))) + { + m_pSlabManager->Release(); + m_pSlabManager = NULL; + goto Exit; + } + + // Need to set max slabs here because we didn't know the slab size + // in the constructor. + + m_uiMaxSlabs = m_uiMaxBytes / m_pSlabManager->getSlabSize(); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine sets the limits for record cache and block cache - dividing + the total cache between the two caches. It uses the same ratio + currently in force. +****************************************************************************/ +RCODE F_GlobalCacheMgr::setCacheLimit( + FLMUINT uiNewTotalCacheSize, + FLMBOOL bPreallocateCache) +{ + RCODE rc = NE_XFLM_OK; + + if( uiNewTotalCacheSize > FLM_MAX_CACHE_SIZE) + { + uiNewTotalCacheSize = FLM_MAX_CACHE_SIZE; + } + + if( m_bDynamicCacheAdjust || !bPreallocateCache) + { +DONT_PREALLOCATE: + + if( uiNewTotalCacheSize < m_uiMaxBytes) + { + m_uiMaxBytes = uiNewTotalCacheSize; + m_uiMaxSlabs = m_uiMaxBytes / m_pSlabManager->getSlabSize(); + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->reduceCache(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( NULL); + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + if( RC_BAD( rc)) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pSlabManager->resize( 0))) + { + goto Exit; + } + } + + m_bCachePreallocated = FALSE; + } + else + { + if( RC_BAD( m_pSlabManager->resize( + uiNewTotalCacheSize, &uiNewTotalCacheSize))) + { + goto DONT_PREALLOCATE; + } + + m_bCachePreallocated = TRUE; + } + + m_uiMaxBytes = uiNewTotalCacheSize; + m_uiMaxSlabs = m_uiMaxBytes / m_pSlabManager->getSlabSize(); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_GlobalCacheMgr::clearCache( + IF_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSavedMaxBytes; + FLMUINT uiSavedMaxSlabs; + + lockMutex(); + + uiSavedMaxBytes = m_uiMaxBytes; + uiSavedMaxSlabs = m_uiMaxSlabs; + + m_uiMaxBytes = 0; + m_uiMaxSlabs = 0; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->reduceCache(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( (F_Db *)pDb); + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + if( RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + m_uiMaxBytes = uiSavedMaxBytes; + m_uiMaxSlabs = uiSavedMaxSlabs; + unlockMutex(); + return( rc); +} + +/**************************************************************************** +Desc : Startup the database engine. +Notes: This routine may be called multiple times. However, if that is done + exit() should be called for each time this is called successfully. + This routine does not handle race conditions on platforms that do + not support atomic increment. +****************************************************************************/ +RCODE F_DbSystem::init( void) +{ + RCODE rc = NE_XFLM_OK; + FLMINT iEventCategory; +#ifdef FLM_USE_NICI + int iHandle; +#endif + + lockSysData(); + + // See if FLAIM has already been started. If so, + // we are done. + + if (++m_uiFlmSysStartupCount > 1) + { + goto Exit; + } + +#ifdef FLM_NLM + gv_bNetWareStartupCalled = TRUE; + if( RC_BAD( rc = f_netwareStartup())) + { + goto Exit; + } +#endif + + // Sanity check -- make sure we are using the correct + // byte-swap macros for this platform + + flmAssert( FB2UD( (FLMBYTE *)"\x0A\x0B\x0C\x0D") == 0x0D0C0B0A); + flmAssert( FB2UW( (FLMBYTE *)"\x0A\x0B") == 0x0B0A); + + // The memset needs to be first. + + f_memset( &gv_XFlmSysData, 0, sizeof( FLMSYSDATA)); + +#if defined( FLM_LINUX) + flmGetLinuxKernelVersion( &gv_XFlmSysData.uiLinuxMajorVer, + &gv_XFlmSysData.uiLinuxMinorVer, + &gv_XFlmSysData.uiLinuxRevision); + gv_XFlmSysData.uiMaxFileSize = flmGetLinuxMaxFileSize(); +#elif defined( FLM_AIX) + // Call set setrlimit to increase the max allowed file size. + // We don't have a good way to deal with any errors returned by + // setrlimit(), so we just hope that there aren't any... + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + setrlimit( RLIMIT_FSIZE, &rlim); + gv_XFlmSysData.uiMaxFileSize = XFLM_MAXIMUM_FILE_SIZE; +#else + gv_XFlmSysData.uiMaxFileSize = XFLM_MAXIMUM_FILE_SIZE; +#endif + + // Initialize memory tracking variables - should be done before + // call to f_memoryInit(). + +#ifdef FLM_DEBUG + + // Variables for memory allocation tracking. + + gv_XFlmSysData.bTrackLeaks = TRUE; + gv_XFlmSysData.hMemTrackingMutex = F_MUTEX_NULL; +#ifdef DEBUG_SIM_OUT_OF_MEM + gv_XFlmSysData.memSimRandomGen.randomSetSeed( 1); +#endif +#endif + + gv_XFlmSysData.hNodeCacheMutex = F_MUTEX_NULL; + gv_XFlmSysData.hBlockCacheMutex = F_MUTEX_NULL; + gv_XFlmSysData.hShareMutex = F_MUTEX_NULL; + gv_XFlmSysData.hStatsMutex = F_MUTEX_NULL; + gv_XFlmSysData.hLoggerMutex = F_MUTEX_NULL; + gv_XFlmSysData.hIniMutex = F_MUTEX_NULL; + + // Initialize the event categories to have no mutex. + + for (iEventCategory = 0; + iEventCategory < XFLM_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + gv_XFlmSysData.EventHdrs [iEventCategory].hMutex = F_MUTEX_NULL; + } + + // Memory initialization should be first. + + f_memoryInit(); + +#if defined( FLM_NLM) || (defined( FLM_WIN) && !defined( FLM_64BIT)) + initFastBlockCheckSum(); +#endif + + if (RC_BAD( rc = flmVerifyDiskStructOffsets())) + { + goto Exit; + } + + // Check the error code tables + + if( RC_BAD( rc = checkErrorCodeTables())) + { + goto Exit; + } + + // Initialize all of the fields + + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_MAX_UNUSED_TIME, + gv_XFlmSysData.uiMaxUnusedTime); + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_MAX_CP_INTERVAL, + gv_XFlmSysData.uiMaxCPInterval); + + FLM_SECS_TO_TIMER_UNITS( XFLM_DEFAULT_REHASH_BACKOFF_INTERVAL, + gv_XFlmSysData.uiRehashAfterFailureBackoffTime); + + if ((gv_XFlmSysData.pGlobalCacheMgr = f_new F_GlobalCacheMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pGlobalCacheMgr->setup())) + { + goto Exit; + } + + // Initialize the thread manager + + if( (gv_XFlmSysData.pThreadMgr = f_new F_ThreadMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pThreadMgr->setupThreadMgr())) + { + goto Exit; + } + + // Initialize the serial number generator + + if( RC_BAD( rc = f_initSerialNumberGenerator())) + { + goto Exit; + } + + // Initialize the Unicode<->WP mapping tables + + if( RC_BAD( rc = initCharMappingTables())) + { + goto Exit; + } + + // Create the mutexes for controlling access to cache and global structures. + + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hNodeCacheMutex))) + { + goto Exit; + } + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hBlockCacheMutex))) + { + goto Exit; + } + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hShareMutex))) + { + goto Exit; + } + + if ((gv_XFlmSysData.pBlockCacheMgr = f_new F_BlockCacheMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->initCache())) + { + goto Exit; + } + if ((gv_XFlmSysData.pNodeCacheMgr = f_new F_NodeCacheMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->initCache())) + { + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hQueryMutex))) + { + goto Exit; + } + + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hIniMutex))) + { + goto Exit; + } + + // Initialize a statistics structure. + + if (RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hStatsMutex))) + { + goto Exit; + } + + f_memset( &gv_XFlmSysData.Stats, 0, sizeof( XFLM_STATS)); + gv_XFlmSysData.bStatsInitialized = TRUE; + + // Initialize the logging mutex + + if( RC_BAD( rc = f_mutexCreate( &gv_XFlmSysData.hLoggerMutex))) + { + goto Exit; + } + + // Allocate memory for the file name hash table. + + if (RC_BAD(rc = flmAllocHashTbl( FILE_HASH_ENTRIES, + &gv_XFlmSysData.pDatabaseHashTbl))) + { + goto Exit; + } + + // Allocate and Initialize FLAIM Shared File Handle Manager + + if ((gv_XFlmSysData.pFileHdlMgr = f_new F_FileHdlMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_XFlmSysData.pFileHdlMgr->setupFileHdlMgr( + XFLM_DEFAULT_OPEN_THRESHOLD, + XFLM_DEFAULT_MAX_UNUSED_TIME))) + { + goto Exit; + } + + // Allocate and Initialize FLAIM Shared File System object + + if ((gv_pFileSystem = f_new F_FileSystem) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +#ifdef FLM_DBG_LOG + flmDbgLogInit(); +#endif + +#if defined( FLM_WIN) + OSVERSIONINFO versionInfo; + + versionInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO); + if( !GetVersionEx( &versionInfo)) + { + return( MapWinErrorToFlaim( GetLastError(), NE_XFLM_FAILURE)); + } + + // Async writes are not supported on Win32s (3.1) or + // Win95, 98, ME, etc. + + gv_XFlmSysData.bOkToDoAsyncWrites = + (versionInfo.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS && + versionInfo.dwPlatformId != VER_PLATFORM_WIN32s) + ? TRUE + : FALSE; +#else + gv_XFlmSysData.bOkToDoAsyncWrites = TRUE; +#endif + + // Allocate and Initialize FLAIM Server Lock Manager. + + if ((gv_XFlmSysData.pServerLockMgr = f_new ServerLockManager) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = gv_XFlmSysData.pServerLockMgr->setupLockMgr())) + { + goto Exit; + } + + // Set up hash table for lock manager. + + if (RC_BAD( rc = gv_XFlmSysData.pServerLockMgr->SetupHashTbl())) + { + goto Exit; + } + + // Set up mutexes for the event table. + + for (iEventCategory = 0; + iEventCategory < XFLM_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + if (RC_BAD( rc = f_mutexCreate( + &gv_XFlmSysData.EventHdrs [iEventCategory].hMutex))) + { + goto Exit; + } + } + + // Start the monitor thread + + if (RC_BAD( rc = f_threadCreate( &gv_XFlmSysData.pMonitorThrd, + F_DbSystem::monitorThrd, "DB Monitor"))) + { + goto Exit; + } + + // Start the cache cleanup thread + + if (RC_BAD( rc = f_threadCreate( &gv_XFlmSysData.pCacheCleanupThrd, + F_DbSystem::cacheCleanupThrd, "Cache Cleanup Thread"))) + { + goto Exit; + } + + if ((gv_XFlmSysData.pBtPool = f_new F_BtPool()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_XFlmSysData.pBtPool->btpInit())) + { + goto Exit; + } + + if ((gv_XFlmSysData.pNodePool = f_new F_NodePool) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_XFlmSysData.pNodePool->setup())) + { + goto Exit; + } + + if( (gv_XFlmSysData.pXml = f_new F_XML) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = gv_XFlmSysData.pXml->buildCharTable())) + { + goto Exit; + } + + // Check the metaphone routines + +#ifdef FLM_DEBUG + if( RC_BAD( rc = flmVerifyMetaphoneRoutines())) + { + goto Exit; + } +#endif + +#ifdef FLM_USE_NICI + iHandle = f_getpid(); + + // Initialize NICI + + if( RC_BAD( rc = CCS_Init( &iHandle))) + { + rc = RC_SET( NE_XFLM_NICI_INIT_FAILED); + goto Exit; + } +#endif + + // Initialize the XFlaim cache settings from the .ini file (if present) + + readIniFile(); + +#ifdef FLM_NLM + // Detect if we are running in RING0. If so, use NSS File Handles +#define MAX_ADDRSPACE_NAMELEN 63 + char szAddrSpaceName[ MAX_ADDRSPACE_NAMELEN + 1]; + if( getaddressspacename( getaddressspace(), szAddrSpaceName) != NULL) + { + if( f_strcmp( szAddrSpaceName, "OS") == 0) + { + gv_XFlmSysData.bUseNSSFileHdls = TRUE; + } + } +#endif + +Exit: + + // If not successful, free up any resources that were allocated. + + if (RC_BAD( rc)) + { + cleanup(); + } + + unlockSysData(); + return( rc); +} + +/**************************************************************************** +Desc: Shuts the database engine down. +Notes: This routine allows itself to be called multiple times, even before + init() is called or if init() fails. May not handle race + conditions very well on platforms that do not support atomic + exchange. +****************************************************************************/ +void F_DbSystem::exit( void) +{ + lockSysData(); + cleanup(); + unlockSysData(); +} + +/************************************************************************ +Desc : Cleans up - assumes that the spin lock has already been + obtained. This allows it to be called directly from + init() on error conditions. +************************************************************************/ +void F_DbSystem::cleanup( void) +{ + FLMUINT uiCnt; + FLMINT iEventCategory; + + // NOTE: We are checking and decrementing a global variable here. + // However, on platforms that properly support atomic exchange, + // we are OK, because the caller has obtained a spin lock before + // calling this routine, so we are guaranteed to be the only thread + // executing this code at this point. On platforms that don't + // support atomic exchange, our spin lock will be less reliable for + // really tight race conditions. But in reality, nobody should be + // calling FlmStartup and FlmShutdown in race conditions like that + // anyway. We are only doing the spin lock stuff to try and be + // nice about it if they are. + + // This check allows FlmShutdown to be called before calling + // FlmStartup, or even if FlmStartup fails. + + if (!m_uiFlmSysStartupCount) + { + return; + } + + // If we decrement the count and it doesn't go to zero, we are not + // ready to do cleanup yet. + + if (--m_uiFlmSysStartupCount > 0) + { + return; + } + + // Free any queries that have been saved in the query list. + + if (gv_XFlmSysData.hQueryMutex != F_MUTEX_NULL) + { + + // Setting uiMaxQueries to zero will cause flmFreeSavedQueries + // to free the entire list. Also, embedded queries will not be + // added back into the list when uiMaxQueries is zero. + + gv_XFlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( FALSE); + } + + // Shut down the monitor thread, if there is one. + + f_threadDestroy( &gv_XFlmSysData.pMonitorThrd); + + // Shut down the cache reduce thread + + f_threadDestroy( &gv_XFlmSysData.pCacheCleanupThrd); + + // Free all of the files and associated structures + + if (gv_XFlmSysData.pDatabaseHashTbl) + { + FBUCKET * pDatabaseHashTbl; + + // F_Database destructor expects the global mutex to be locked + // IMPORTANT NOTE: pDatabaseHashTbl is ALWAYS allocated + // AFTER the mutex is allocated, so we are guaranteed + // to have a mutex if pDatabaseHashTbl is non-NULL. + + f_mutexLock( gv_XFlmSysData.hShareMutex); + for (uiCnt = 0, pDatabaseHashTbl = gv_XFlmSysData.pDatabaseHashTbl; + uiCnt < FILE_HASH_ENTRIES; + uiCnt++, pDatabaseHashTbl++) + { + F_Database * pDatabase = (F_Database *)pDatabaseHashTbl->pFirstInBucket; + F_Database * pTmpDatabase; + + while (pDatabase) + { + pTmpDatabase = pDatabase; + pDatabase = pDatabase->m_pNext; + pTmpDatabase->freeDatabase(); + } + pDatabaseHashTbl->pFirstInBucket = NULL; + } + + // Unlock the global mutex + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + + // Free the hash table + f_free( &gv_XFlmSysData.pDatabaseHashTbl); + } + + // Free the statistics. + + if (gv_XFlmSysData.bStatsInitialized) + { + f_mutexLock( gv_XFlmSysData.hStatsMutex); + flmStatFree( &gv_XFlmSysData.Stats); + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + gv_XFlmSysData.bStatsInitialized = FALSE; + } + + if (gv_XFlmSysData.hStatsMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hStatsMutex); + } + + // Free (release) FLAIM's File Shared File Handles. + + if (gv_XFlmSysData.pFileHdlMgr) + { + FLMUINT uiRefCnt = gv_XFlmSysData.pFileHdlMgr->Release(); + + // No one else should have a reference to the file handle manager + // after this point. + +#ifdef FLM_DEBUG + flmAssert( 0 == uiRefCnt); +#else + // Quiet the compiler about the unused variable + (void)uiRefCnt; +#endif + gv_XFlmSysData.pFileHdlMgr = NULL; + } + + // Free (release) FLAIM's Server Lock Manager. + + if (gv_XFlmSysData.pServerLockMgr) + { + FLMUINT uiRefCnt; + + // Release all locks. + + gv_XFlmSysData.pServerLockMgr->CheckLockTimeouts( FALSE, TRUE); + + // Release the lock manager. + + uiRefCnt = gv_XFlmSysData.pServerLockMgr->Release(); + + // No one else should have a reference to the server lock manager + // at this point, so lets trip a flmAssert if the object was really + // not deleted. + +#ifdef FLM_DEBUG + flmAssert( 0 == uiRefCnt); +#else + // Quiet the compiler about the unused variable + (void)uiRefCnt; +#endif + gv_XFlmSysData.pServerLockMgr = NULL; + } + + // Make sure the purge list is empty + + if( gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->cleanupPurgedCache(); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + flmAssert( !gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList); + } + + // Free the node cache manager + + if( gv_XFlmSysData.pNodeCacheMgr) + { + gv_XFlmSysData.pNodeCacheMgr->Release(); + gv_XFlmSysData.pNodeCacheMgr = NULL; + } + + // Free the block cache manager + + if( gv_XFlmSysData.pBlockCacheMgr) + { + gv_XFlmSysData.pBlockCacheMgr->Release(); + gv_XFlmSysData.pBlockCacheMgr = NULL; + } + + // Free up callbacks that have been registered for events. + + for (iEventCategory = 0; + iEventCategory < XFLM_MAX_EVENT_CATEGORIES; + iEventCategory++) + { + if (gv_XFlmSysData.EventHdrs [iEventCategory].hMutex != F_MUTEX_NULL) + { + while (gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList) + { + flmFreeEvent( + gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList, + gv_XFlmSysData.EventHdrs [iEventCategory].hMutex, + &gv_XFlmSysData.EventHdrs [iEventCategory].pEventCBList); + } + f_mutexDestroy( &gv_XFlmSysData.EventHdrs [iEventCategory].hMutex); + } + } + + // Free (release) FLAIM's File Shared File System Object. + + if (gv_pFileSystem) + { + FLMUINT uiRefCnt = gv_pFileSystem->Release(); + + // No one else should have a reference to the file system + // after this point. + +#ifdef FLM_DEBUG + flmAssert( 0 == uiRefCnt); +#else + // Quiet the compiler about the unused variable + (void)uiRefCnt; +#endif + gv_pFileSystem = NULL; + } +#ifdef FLM_DBG_LOG + flmDbgLogExit(); +#endif + + // Free the serial number generator + + f_freeSerialNumberGenerator(); + + // Release the logger + + if( gv_XFlmSysData.pLogger) + { + flmAssert( !gv_XFlmSysData.uiPendingLogMessages); + gv_XFlmSysData.pLogger->Release(); + gv_XFlmSysData.pLogger = NULL; + } + + if( gv_XFlmSysData.hLoggerMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hLoggerMutex); + } + + // Release the thread manager + + if( gv_XFlmSysData.pThreadMgr) + { + gv_XFlmSysData.pThreadMgr->Release(); + gv_XFlmSysData.pThreadMgr = NULL; + } + + // Free the btree pool + + if( gv_XFlmSysData.pBtPool) + { + gv_XFlmSysData.pBtPool->Release(); + gv_XFlmSysData.pBtPool = NULL; + } + + // Free the node pool + + if( gv_XFlmSysData.pNodePool) + { + gv_XFlmSysData.pNodePool->Release(); + gv_XFlmSysData.pNodePool = NULL; + } + + // Free the XML object + + if( gv_XFlmSysData.pXml) + { + gv_XFlmSysData.pXml->Release(); + gv_XFlmSysData.pXml = NULL; + } + + // Release the global cache manager. NOTE: This must happen + // only AFTER releasing the node cache manager and block cache + // manager. + + if( gv_XFlmSysData.pGlobalCacheMgr) + { + gv_XFlmSysData.pGlobalCacheMgr->Release(); + gv_XFlmSysData.pGlobalCacheMgr = NULL; + } + + // Free the Unicode<->WP tables + + freeCharMappingTables(); + +#ifdef FLM_USE_NICI + CCS_Shutdown(); +#endif + + // Free the mutexes last of all. + + if (gv_XFlmSysData.hQueryMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hQueryMutex); + } + + if (gv_XFlmSysData.hNodeCacheMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hNodeCacheMutex); + } + if (gv_XFlmSysData.hBlockCacheMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hBlockCacheMutex); + } + if (gv_XFlmSysData.hShareMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hShareMutex); + } + + if (gv_XFlmSysData.hIniMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &gv_XFlmSysData.hIniMutex); + } + + // Memory cleanup + + f_memoryCleanup(); + + // Shutdown the NetWare interfaces + +#ifdef FLM_NLM + if( gv_bNetWareStartupCalled) + { + f_netwareShutdown(); + gv_bNetWareStartupCalled = FALSE; + } +#endif +} + +/**************************************************************************** +Desc: Configures how memory will be dynamically regulated. +****************************************************************************/ +RCODE F_GlobalCacheMgr::setDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, + FLMUINT uiCacheAdjustMin, + FLMUINT uiCacheAdjustMax, + FLMUINT uiCacheAdjustMinToLeave) +{ +#ifndef FLM_CAN_GET_PHYS_MEM + F_UNREFERENCED_PARM( uiCacheAdjustPercent); + F_UNREFERENCED_PARM( uiCacheAdjustMin); + F_UNREFERENCED_PARM( uiCacheAdjustMax); + F_UNREFERENCED_PARM( uiCacheAdjustMinToLeave); + return( RC_SET( NE_XFLM_NOT_IMPLEMENTED)); +#else + RCODE rc = NE_XFLM_OK; + FLMUINT uiCacheBytes; + + lockMutex(); + m_bDynamicCacheAdjust = TRUE; + flmAssert( uiCacheAdjustPercent > 0 && uiCacheAdjustPercent <= 100); + m_uiCacheAdjustPercent = uiCacheAdjustPercent; + m_uiCacheAdjustMin = uiCacheAdjustMin; + m_uiCacheAdjustMax = uiCacheAdjustMax; + m_uiCacheAdjustMinToLeave = uiCacheAdjustMinToLeave; + uiCacheBytes = flmGetCacheBytes( m_uiCacheAdjustPercent, + m_uiCacheAdjustMin, m_uiCacheAdjustMax, + m_uiCacheAdjustMinToLeave, TRUE, totalBytes()); + + rc = setCacheLimit( uiCacheBytes, FALSE); + unlockMutex(); + return( rc); +#endif +} + +/**************************************************************************** +Desc: Sets a hard memory limit for cache. +****************************************************************************/ +RCODE F_GlobalCacheMgr::setHardMemoryLimit( + FLMUINT uiPercent, + FLMBOOL bPercentOfAvail, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bPreallocate) +{ + RCODE rc = NE_XFLM_OK; + + lockMutex(); + m_bDynamicCacheAdjust = FALSE; + if (uiPercent) + { +#ifndef FLM_CAN_GET_PHYS_MEM + F_UNREFERENCED_PARM( bPercentOfAvail); + F_UNREFERENCED_PARM( uiMin); + F_UNREFERENCED_PARM( uiMinToLeave); + rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); +#else + FLMUINT uiCacheBytes; + + uiCacheBytes = flmGetCacheBytes( uiPercent, uiMin, uiMax, uiMinToLeave, + bPercentOfAvail, totalBytes()); + rc = setCacheLimit( uiCacheBytes, bPreallocate); +#endif + } + else + { + rc = setCacheLimit( uiMax, bPreallocate); + } + + unlockMutex(); + + return( rc); +} + +/**************************************************************************** +Desc: Returns information about memory usage +****************************************************************************/ +void F_GlobalCacheMgr::getCacheInfo( + XFLM_CACHE_INFO * pCacheInfo) +{ + f_memset( pCacheInfo, 0, sizeof( XFLM_CACHE_INFO)); + + lockMutex(); + pCacheInfo->uiMaxBytes = m_uiMaxBytes; + pCacheInfo->uiTotalBytesAllocated = totalBytes(); + pCacheInfo->bDynamicCacheAdjust = m_bDynamicCacheAdjust; + pCacheInfo->uiCacheAdjustPercent = m_uiCacheAdjustPercent; + pCacheInfo->uiCacheAdjustMin = m_uiCacheAdjustMin; + pCacheInfo->uiCacheAdjustMax = m_uiCacheAdjustMax; + pCacheInfo->uiCacheAdjustMinToLeave = m_uiCacheAdjustMinToLeave; + pCacheInfo->bPreallocatedCache = m_bCachePreallocated; + unlockMutex(); + + // Return block cache information. + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + f_memcpy( &pCacheInfo->BlockCache, &gv_XFlmSysData.pBlockCacheMgr->m_Usage, + sizeof( XFLM_CACHE_USAGE)); + + pCacheInfo->uiFreeBytes = gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes; + pCacheInfo->uiFreeCount = gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount; + pCacheInfo->uiReplaceableCount = gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount; + pCacheInfo->uiReplaceableBytes = gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes; + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + // Return node cache information. + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + f_memcpy( &pCacheInfo->NodeCache, &gv_XFlmSysData.pNodeCacheMgr->m_Usage, + sizeof( XFLM_CACHE_USAGE)); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + // Gather per-database statistics + + f_mutexLock( gv_XFlmSysData.hShareMutex); + if( gv_XFlmSysData.pDatabaseHashTbl) + { + FLMUINT uiLoop; + F_Database * pDatabase; + + for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) + { + if( (pDatabase = (F_Database *)gv_XFlmSysData.pDatabaseHashTbl[ + uiLoop].pFirstInBucket) != NULL) + { + while( pDatabase) + { + if( pDatabase->m_uiDirtyCacheCount) + { + pCacheInfo->uiDirtyBytes += + pDatabase->m_uiDirtyCacheCount * pDatabase->m_uiBlockSize; + pCacheInfo->uiDirtyCount += pDatabase->m_uiDirtyCacheCount; + } + + if( pDatabase->m_uiNewCount) + { + pCacheInfo->uiNewBytes += + pDatabase->m_uiNewCount * pDatabase->m_uiBlockSize; + pCacheInfo->uiNewCount += pDatabase->m_uiNewCount; + } + + if( pDatabase->m_uiLogCacheCount) + { + pCacheInfo->uiLogBytes += + pDatabase->m_uiLogCacheCount * pDatabase->m_uiBlockSize; + pCacheInfo->uiLogCount += pDatabase->m_uiLogCacheCount; + } + + pDatabase = pDatabase->m_pNext; + } + } + } + } + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: Returns the number of open databases +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getOpenFileCount( void) +{ + return( gv_XFlmSysData.pFileHdlMgr->getOpenedFiles()); +} + +/**************************************************************************** +Desc: Close all files in the file handle manager that have not been + used for the specified number of seconds. +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::closeUnusedFiles( + FLMUINT uiSeconds) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiValue; + FLMUINT uiCurrTime; + FLMUINT uiSave; + + // Convert seconds to timer units + + FLM_SECS_TO_TIMER_UNITS( uiSeconds, uiValue); + gv_XFlmSysData.pFileHdlMgr->checkAgedFileHdls( uiValue); + + // Free any other unused structures that have not been used for the + // specified amount of time. + + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + f_mutexLock( gv_XFlmSysData.hShareMutex); + + // Temporarily set the maximum unused seconds in the FLMSYSDATA structure + // to the value that was passed in to Value1. Restore it after + // calling checkNotUsedObject. + + uiSave = gv_XFlmSysData.uiMaxUnusedTime; + gv_XFlmSysData.uiMaxUnusedTime = uiValue; + + // May unlock and re-lock the global mutex. + + checkNotUsedObjects(); + gv_XFlmSysData.uiMaxUnusedTime = uiSave; + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + + return( rc); +} + +/**************************************************************************** +Desc: Set threshold for number of file handles that are opened by + the file handle manager +****************************************************************************/ +void XFLMAPI F_DbSystem::setOpenThreshold( + FLMUINT uiThreshold) +{ + gv_XFlmSysData.pFileHdlMgr->setOpenThreshold( uiThreshold); +} + +/**************************************************************************** +Desc: Get the maximum number of file handles available. +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getOpenThreshold( void) +{ + return( gv_XFlmSysData.pFileHdlMgr->getOpenThreshold()); +} + +/**************************************************************************** +Desc: Enable/disable cache debugging mode +****************************************************************************/ +void XFLMAPI F_DbSystem::enableCacheDebug( + FLMBOOL bDebug) +{ +#ifdef FLM_DEBUG + gv_XFlmSysData.pBlockCacheMgr->m_bDebug = bDebug; + gv_XFlmSysData.pNodeCacheMgr->m_bDebug = bDebug; +#else + F_UNREFERENCED_PARM( bDebug); +#endif +} + +/**************************************************************************** +Desc: Returns cache debugging mode +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::cacheDebugEnabled( void) +{ +#ifdef FLM_DEBUG + return( gv_XFlmSysData.pBlockCacheMgr->m_bDebug || + gv_XFlmSysData.pNodeCacheMgr->m_bDebug); +#else + return( FALSE); +#endif +} + +/**************************************************************************** +Desc: Start gathering statistics. +****************************************************************************/ +void XFLMAPI F_DbSystem::startStats( void) +{ + f_mutexLock( gv_XFlmSysData.hStatsMutex); + flmStatStart( &gv_XFlmSysData.Stats); + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + + // Start query statistics, if they have not + // already been started. + + f_mutexLock( gv_XFlmSysData.hQueryMutex); + if (!gv_XFlmSysData.uiMaxQueries) + { + gv_XFlmSysData.uiMaxQueries = 20; + gv_XFlmSysData.bNeedToUnsetMaxQueries = TRUE; + } + f_mutexUnlock( gv_XFlmSysData.hQueryMutex); +} + +/**************************************************************************** +Desc: Stop gathering statistics. +****************************************************************************/ +void XFLMAPI F_DbSystem::stopStats( void) +{ + f_mutexLock( gv_XFlmSysData.hStatsMutex); + flmStatStop( &gv_XFlmSysData.Stats); + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + + // Stop query statistics, if they were + // started by a call to FLM_START_STATS. + + f_mutexLock( gv_XFlmSysData.hQueryMutex); + if (gv_XFlmSysData.bNeedToUnsetMaxQueries) + { + gv_XFlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( TRUE); + // NOTE: flmFreeSavedQueries unlocks the mutex. + } + else + { + f_mutexUnlock( gv_XFlmSysData.hQueryMutex); + } +} + +/**************************************************************************** +Desc: Reset statistics. +****************************************************************************/ +void XFLMAPI F_DbSystem::resetStats( void) +{ + FLMUINT uiSaveMax; + + // Reset the block cache statistics. + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits = 0; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHits = 0; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHitLooks = 0; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults = 0; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks = 0; + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + // Reset the node cache statistics. + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + gv_XFlmSysData.pNodeCacheMgr->m_uiIoWaits = 0; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheHits = 0; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheHitLooks = 0; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheFaults = 0; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCacheFaultLooks = 0; + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + f_mutexLock( gv_XFlmSysData.hStatsMutex); + flmStatReset( &gv_XFlmSysData.Stats, TRUE); + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + + f_mutexLock( gv_XFlmSysData.hQueryMutex); + uiSaveMax = gv_XFlmSysData.uiMaxQueries; + gv_XFlmSysData.uiMaxQueries = 0; + flmFreeSavedQueries( TRUE); + // NOTE: flmFreeSavedQueries unlocks the mutex. + + // Restore the old maximum + + if( uiSaveMax) + { + // flmFreeSavedQueries unlocks the mutex, so we + // must relock it to restore the old maximum. + + f_mutexLock( gv_XFlmSysData.hQueryMutex); + gv_XFlmSysData.uiMaxQueries = uiSaveMax; + f_mutexUnlock( gv_XFlmSysData.hQueryMutex); + } +} + +/**************************************************************************** +Desc: Returns statistics that have been collected for a share. +Notes: The statistics returned will be the statistics for ALL databases +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::getStats( + XFLM_STATS * pFlmStats) +{ + RCODE rc = NE_XFLM_OK; + + // Get the statistics + + f_mutexLock( gv_XFlmSysData.hStatsMutex); + rc = flmStatCopy( pFlmStats, &gv_XFlmSysData.Stats); + f_mutexUnlock( gv_XFlmSysData.hStatsMutex); + if (RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Frees memory allocated to a FLM_STATS structure +****************************************************************************/ +void XFLMAPI F_DbSystem::freeStats( + XFLM_STATS * pFlmStats) +{ + flmStatFree( pFlmStats); +} + +/**************************************************************************** +Desc: Sets the path for all temporary files that come into use within a + FLAIM share structure. The share mutex should be locked when + settting when called from FlmConfig(). +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::setTempDir( + const char * pszPath) +{ + RCODE rc = NE_XFLM_OK; + + f_mutexLock( gv_XFlmSysData.hShareMutex); + + // First, test the path + + if( RC_BAD( rc = gv_pFileSystem->Exists( pszPath))) + { + goto Exit; + } + + f_strcpy( gv_XFlmSysData.szTempDir, pszPath); + gv_XFlmSysData.bTempDirSet = TRUE; + +Exit: + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + return( rc); +} + +/**************************************************************************** +Desc: Get the temporary directory. +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::getTempDir( + char * pszPath) +{ + RCODE rc = NE_XFLM_OK; + + f_mutexLock( gv_XFlmSysData.hShareMutex); + + if( !gv_XFlmSysData.bTempDirSet ) + { + *pszPath = 0; + rc = RC_SET( NE_XFLM_IO_PATH_NOT_FOUND); + goto Exit; + } + else + { + f_strcpy( pszPath, gv_XFlmSysData.szTempDir); + } + +Exit: + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); + return( rc); +} + +/**************************************************************************** +Desc: Sets the maximum seconds between checkpoints +****************************************************************************/ +void XFLMAPI F_DbSystem::setCheckpointInterval( + FLMUINT uiSeconds) +{ + FLM_SECS_TO_TIMER_UNITS( uiSeconds, gv_XFlmSysData.uiMaxCPInterval); +} + +/**************************************************************************** +Desc: Gets the maximum seconds between checkpoints +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getCheckpointInterval( void) +{ + FLMUINT uiTmp; + + FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.uiMaxCPInterval, uiTmp); + return( uiTmp); +} + +/**************************************************************************** +Desc: Sets the interval for dynamically adjusting the cache limit. +****************************************************************************/ +void XFLMAPI F_DbSystem::setCacheAdjustInterval( + FLMUINT uiSeconds) +{ + FLM_SECS_TO_TIMER_UNITS( uiSeconds, + gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheAdjustInterval); +} + +/**************************************************************************** +Desc: Sets the interval for dynamically adjusting the cache limit. +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getCacheAdjustInterval( void) +{ + FLMUINT uiTmp; + + FLM_TIMER_UNITS_TO_SECS( + gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheAdjustInterval, uiTmp); + return( uiTmp); +} + +/**************************************************************************** +Desc: Sets the interval for dynamically cleaning out old + cache blocks and records +****************************************************************************/ +void XFLMAPI F_DbSystem::setCacheCleanupInterval( + FLMUINT uiSeconds) +{ + FLM_SECS_TO_TIMER_UNITS( uiSeconds, + gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval); +} + +/**************************************************************************** +Desc: Gets the interval for dynamically cleaning out old + cache blocks and records +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getCacheCleanupInterval( void) +{ + FLMUINT uiTmp; + + FLM_TIMER_UNITS_TO_SECS( + gv_XFlmSysData.pGlobalCacheMgr->m_uiCacheCleanupInterval, uiTmp); + return( uiTmp); +} + +/**************************************************************************** +Desc: Set interval for cleaning up unused structures +****************************************************************************/ +void XFLMAPI F_DbSystem::setUnusedCleanupInterval( + FLMUINT uiSeconds) +{ + FLM_SECS_TO_TIMER_UNITS( uiSeconds, + gv_XFlmSysData.pGlobalCacheMgr->m_uiUnusedCleanupInterval); +} + +/**************************************************************************** +Desc: Gets the interval for cleaning up unused structures +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getUnusedCleanupInterval( void) +{ + FLMUINT uiTmp; + + FLM_TIMER_UNITS_TO_SECS( + gv_XFlmSysData.pGlobalCacheMgr->m_uiUnusedCleanupInterval, uiTmp); + return( uiTmp); +} + +/**************************************************************************** +Desc: Set the maximum time for an item to be unused before it is + cleaned up +****************************************************************************/ +void XFLMAPI F_DbSystem::setMaxUnusedTime( + FLMUINT uiSeconds) +{ + FLM_SECS_TO_TIMER_UNITS( uiSeconds, gv_XFlmSysData.uiMaxUnusedTime); +} + +/**************************************************************************** +Desc: Gets the maximum time for an item to be unused +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getMaxUnusedTime( void) +{ + FLMUINT uiTmp; + + FLM_TIMER_UNITS_TO_SECS( gv_XFlmSysData.uiMaxUnusedTime, uiTmp); + return( uiTmp); +} + +/**************************************************************************** +Desc: Enables or disables out-of-memory simulation +****************************************************************************/ +void F_DbSystem::enableOutOfMemorySimulation( + FLMBOOL bEnable) +{ +#ifdef DEBUG_SIM_OUT_OF_MEM + gv_XFlmSysData.uiOutOfMemSimEnabledFlag = bEnable + ? OUT_OF_MEM_SIM_ENABLED_FLAG + : 0; +#else + F_UNREFERENCED_PARM( bEnable); +#endif +} + +/**************************************************************************** +Desc: Returns the state of out-of-memory simulation +****************************************************************************/ +FLMBOOL F_DbSystem::outOfMemorySimulationEnabled( void) +{ +#ifdef DEBUG_SIM_OUT_OF_MEM + if( gv_XFlmSysData.uiOutOfMemSimEnabledFlag == OUT_OF_MEM_SIM_ENABLED_FLAG) + { + return( TRUE); + } +#endif + + return( FALSE); +} + +/**************************************************************************** +Desc: Sets the logging object to be used for internal status messages +****************************************************************************/ +void XFLMAPI F_DbSystem::setLogger( + IF_LoggerClient * pLogger) +{ + IF_LoggerClient * pOldLogger = NULL; + + f_mutexLock( gv_XFlmSysData.hLoggerMutex); + + for( ;;) + { + // While waiting for the pending message count to go to zero, other + // threads may have come in to set different logger objects. To handle + // this case, we need to check gv_XFlmSysData.pLogger and release it + // if non-NULL. + + if( gv_XFlmSysData.pLogger) + { + if( pOldLogger) + { + pOldLogger->Release(); + } + + pOldLogger = gv_XFlmSysData.pLogger; + gv_XFlmSysData.pLogger = NULL; + } + + if( !gv_XFlmSysData.uiPendingLogMessages) + { + break; + } + + f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); + f_sleep( 100); + f_mutexLock( gv_XFlmSysData.hLoggerMutex); + } + + if( pOldLogger) + { + pOldLogger->Release(); + } + + if( (gv_XFlmSysData.pLogger = pLogger) != NULL) + { + gv_XFlmSysData.pLogger->AddRef(); + } + + f_mutexUnlock( gv_XFlmSysData.hLoggerMutex); +} + +/**************************************************************************** +Desc: Enables or disables the use of ESM (if available) +****************************************************************************/ +void XFLMAPI F_DbSystem::enableExtendedServerMemory( + FLMBOOL bEnable) +{ + gv_XFlmSysData.bOkToUseESM = bEnable; +} + +/**************************************************************************** +Desc: Returns the state of ESM +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::extendedServerMemoryEnabled( void) +{ + return( gv_XFlmSysData.bOkToUseESM); +} + +/**************************************************************************** +Desc: Deactivates open database handles, forcing the database to be + (eventually) closed +Notes: Passing NULL for the path values will cause all active database + handles to be deactivated +****************************************************************************/ +void XFLMAPI F_DbSystem::deactivateOpenDb( + const char * pszDbFileName, + const char * pszDataDir) +{ + F_Database * pTmpDatabase; + + f_mutexLock( gv_XFlmSysData.hShareMutex); + if( pszDbFileName) + { + // Look up the file using findDatabase to see if we have the + // file open. May unlock and re-lock the global mutex. + + if( RC_OK( findDatabase( pszDbFileName, + pszDataDir, &pTmpDatabase)) && pTmpDatabase) + { + pTmpDatabase->setMustCloseFlags( NE_XFLM_OK, TRUE); + } + } + else + { + if( gv_XFlmSysData.pDatabaseHashTbl) + { + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < FILE_HASH_ENTRIES; uiLoop++) + { + pTmpDatabase = + (F_Database *)gv_XFlmSysData.pDatabaseHashTbl[ uiLoop].pFirstInBucket; + + while( pTmpDatabase) + { + pTmpDatabase->setMustCloseFlags( NE_XFLM_OK, TRUE); + pTmpDatabase = pTmpDatabase->m_pNext; + } + } + } + } + + f_mutexUnlock( gv_XFlmSysData.hShareMutex); +} + +/**************************************************************************** +Desc: Sets the maximum number of queries to save +****************************************************************************/ +void XFLMAPI F_DbSystem::setQuerySaveMax( + FLMUINT uiMaxToSave) +{ + f_mutexLock( gv_XFlmSysData.hQueryMutex); + gv_XFlmSysData.uiMaxQueries = uiMaxToSave; + gv_XFlmSysData.bNeedToUnsetMaxQueries = FALSE; + flmFreeSavedQueries( TRUE); +} + +/**************************************************************************** +Desc: Gets the maximum number of queries to save +****************************************************************************/ +FLMUINT XFLMAPI F_DbSystem::getQuerySaveMax( void) +{ + return( gv_XFlmSysData.uiMaxQueries); +} + +/**************************************************************************** +Desc: Sets the maximum amount of dirty cache allowed +****************************************************************************/ +void XFLMAPI F_DbSystem::setDirtyCacheLimits( + FLMUINT uiMaxDirty, + FLMUINT uiLowDirty) +{ + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + if( !uiMaxDirty) + { + gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty = TRUE; + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = 0; + gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = 0; + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty = FALSE; + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = uiMaxDirty; + + // Low threshhold must be no higher than maximum! + + if ((gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = uiLowDirty) > + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache; + } + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: Gets the maximum amount of dirty cache allowed +****************************************************************************/ +void XFLMAPI F_DbSystem::getDirtyCacheLimits( + FLMUINT * puiMaxDirty, + FLMUINT * puiLowDirty) +{ + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + if( puiMaxDirty) + { + *puiMaxDirty = gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache; + } + + if( puiLowDirty) + { + *puiLowDirty = gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache; + } + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: Returns information about threads owned by the database engine +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::getThreadInfo( + IF_ThreadInfo ** ppThreadInfo + ) +{ + RCODE rc = NE_XFLM_OK; + F_ThreadInfo * pThreadInfo = NULL; + + if ((pThreadInfo = f_new F_ThreadInfo) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = gv_XFlmSysData.pThreadMgr->getThreadInfo( + &pThreadInfo->m_Pool, + &pThreadInfo->m_pThreadInfoArray, + &pThreadInfo->m_uiNumThreads))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc) && pThreadInfo) + { + pThreadInfo->Release(); + pThreadInfo = NULL; + } + + *ppThreadInfo = (IF_ThreadInfo *)pThreadInfo; + return( rc); +} + +/**************************************************************************** +Desc: Registers for an event +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::registerForEvent( + eEventCategory eCategory, + IF_EventClient * pEventClient) +{ + RCODE rc = NE_XFLM_OK; + FEVENT * pEvent; + + // Make sure it is a legal event category to register for. + + if (eCategory >= XFLM_MAX_EVENT_CATEGORIES) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + // Allocate an event structure + + if (RC_BAD( rc = f_calloc( + (FLMUINT)(sizeof( FEVENT)), &pEvent))) + { + goto Exit; + } + + // Initialize the structure members and linkt to the + // list of events off of the event category. + + pEvent->pEventClient = pEventClient; + pEvent->pEventClient->AddRef(); + // pEvent->pPrev = NULL; // done by f_calloc above. + + // Mutex should be locked to link into list. + + f_mutexLock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); + if ((pEvent->pNext = + gv_XFlmSysData.EventHdrs [eCategory].pEventCBList) != NULL) + { + pEvent->pNext->pPrev = pEvent; + } + gv_XFlmSysData.EventHdrs [eCategory].pEventCBList = pEvent; + f_mutexUnlock( gv_XFlmSysData.EventHdrs [eCategory].hMutex); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: De-registers for an event +****************************************************************************/ +void XFLMAPI F_DbSystem::deregisterForEvent( + eEventCategory eCategory, + IF_EventClient * pEventClient) +{ + FEVENT * pEvent = gv_XFlmSysData.EventHdrs [eCategory].pEventCBList; + + // Find the event and remove from the list. If it is not + // there, don't worry about it. + + while (pEvent) + { + if (pEventClient == pEvent->pEventClient) + { + flmFreeEvent( pEvent, + gv_XFlmSysData.EventHdrs[ eCategory].hMutex, + &gv_XFlmSysData.EventHdrs[ eCategory].pEventCBList); + break; + } + pEvent = pEvent->pNext; + } +} + +/**************************************************************************** +Desc: Returns TRUE if the specified error indicates that the database + is corrupt +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::getNextMetaphone( + IF_IStream * pIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone) +{ + return( flmGetNextMetaphone( + pIStream, puiMetaphone, puiAltMetaphone)); +} + +/**************************************************************************** +Desc: Returns TRUE if the specified error indicates that the database + is corrupt +****************************************************************************/ +FLMBOOL XFLMAPI F_DbSystem::errorIsFileCorrupt( + RCODE rc) +{ + FLMBOOL bIsCorrupt = FALSE; + + switch( rc) + { + case NE_XFLM_BTREE_ERROR : + case NE_XFLM_DATA_ERROR : + case NE_XFLM_NOT_FLAIM : + case NE_XFLM_BLOCK_CRC : + case NE_XFLM_HDR_CRC : + case NE_XFLM_INCOMPLETE_LOG : + bIsCorrupt = TRUE; + break; + default : + break; + } + + return( bIsCorrupt); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FINLINE FLMBYTE flmShiftRightRetByte( + FLMUINT64 ui64Num, + FLMBYTE ucBits) +{ + return( ucBits < 64 ? (FLMBYTE)(ui64Num >> ucBits) : 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT flmGetSENByteCount( + FLMUINT64 ui64Num) +{ + FLMUINT uiCount = 0; + + if( ui64Num < 0x80) + { + return( 1); + } + + while( ui64Num) + { + uiCount++; + ui64Num >>= 7; + } + + // If the high bit is set, the counter will be incremented 1 beyond + // the actual number of bytes need to represent the SEN. We will need + // to re-visit this if we ever go beyond 64-bits. + + return( uiCount < FLM_MAX_SEN_LEN ? uiCount : FLM_MAX_SEN_LEN); +} + +/**************************************************************************** +Desc: Encodes a number as a SEN +****************************************************************************/ +FLMUINT flmEncodeSEN( + FLMUINT64 ui64Value, + FLMBYTE ** ppucBuffer, + FLMUINT uiSizeWanted) +{ + FLMBYTE * pucBuffer = *ppucBuffer; + FLMUINT uiSenLen = flmGetSENByteCount( ui64Value); + + flmAssert( uiSizeWanted <= FLM_MAX_SEN_LEN && + (!uiSizeWanted || uiSizeWanted >= uiSenLen)); + + uiSenLen = uiSizeWanted > uiSenLen ? uiSizeWanted : uiSenLen; + + if( uiSenLen == 1) + { + *pucBuffer++ = (FLMBYTE)ui64Value; + } + else + { + FLMUINT uiTmp = (uiSenLen - 1) << 3; + + *pucBuffer++ = ucSENPrefixArray[ uiSenLen] + + flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + while( uiTmp) + { + uiTmp -= 8; + *pucBuffer++ = flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + } + } + + *ppucBuffer = pucBuffer; + return( uiSenLen); +} + +/**************************************************************************** +Desc: Encodes a number as a SEN +****************************************************************************/ +RCODE flmEncodeSEN( + FLMUINT64 ui64Value, + FLMBYTE ** ppucBuffer, + FLMBYTE * pucEnd) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucBuffer = *ppucBuffer; + FLMUINT uiSenLen = flmGetSENByteCount( ui64Value); + + if( *ppucBuffer + uiSenLen > pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( uiSenLen == 1) + { + *pucBuffer++ = (FLMBYTE)ui64Value; + } + else + { + FLMUINT uiTmp = (uiSenLen - 1) << 3; + + *pucBuffer++ = ucSENPrefixArray[ uiSenLen] + + flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + while( uiTmp) + { + uiTmp -= 8; + *pucBuffer++ = flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + } + } + + *ppucBuffer = pucBuffer; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Encodes a number as a SEN +****************************************************************************/ +FLMUINT flmEncodeSENKnownLength( + FLMUINT64 ui64Value, + FLMUINT uiSenLen, + FLMBYTE ** ppucBuffer) +{ + FLMBYTE * pucBuffer = *ppucBuffer; + + if( uiSenLen == 1) + { + *pucBuffer++ = (FLMBYTE)ui64Value; + } + else + { + FLMUINT uiTmp = (uiSenLen - 1) << 3; + + *pucBuffer++ = ucSENPrefixArray[ uiSenLen] + + flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + while( uiTmp) + { + uiTmp -= 8; + *pucBuffer++ = flmShiftRightRetByte( ui64Value, (FLMBYTE)uiTmp); + } + } + + *ppucBuffer = pucBuffer; + return( uiSenLen); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE flmDecodeSEN64( + const FLMBYTE ** ppucBuffer, + const FLMBYTE * pucEnd, + FLMUINT64 * pui64Value) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSENLength; + const FLMBYTE * pucBuffer = *ppucBuffer; + + uiSENLength = gv_ucSENLengthArray[ *pucBuffer]; + if( pucBuffer + uiSENLength > pucEnd) + { + if (pui64Value) + { + *pui64Value = 0; + } + rc = RC_SET( NE_XFLM_BAD_SEN); + goto Exit; + } + + if (pui64Value) + { + switch( uiSENLength) + { + case 1: + *pui64Value = *pucBuffer; + break; + + case 2: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x3F)) << 8) + pucBuffer[ 1]; + break; + + case 3: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x1F)) << 16) + + (((FLMUINT64)pucBuffer[ 1]) << 8) + pucBuffer[ 2]; + break; + + case 4: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x0F)) << 24) + + (((FLMUINT64)pucBuffer[ 1]) << 16) + + (((FLMUINT64)pucBuffer[ 2]) << 8) + pucBuffer[ 3]; + break; + + case 5: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x07)) << 32) + + (((FLMUINT64)pucBuffer[ 1]) << 24) + + (((FLMUINT64)pucBuffer[ 2]) << 16) + + (((FLMUINT64)pucBuffer[ 3]) << 8) + pucBuffer[ 4]; + break; + + case 6: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x03)) << 40) + + (((FLMUINT64)pucBuffer[ 1]) << 32) + + (((FLMUINT64)pucBuffer[ 2]) << 24) + + (((FLMUINT64)pucBuffer[ 3]) << 16) + + (((FLMUINT64)pucBuffer[ 4]) << 8) + pucBuffer[ 5]; + break; + + case 7: + *pui64Value = (((FLMUINT64)(*pucBuffer & 0x01)) << 48) + + (((FLMUINT64)pucBuffer[ 1]) << 40) + + (((FLMUINT64)pucBuffer[ 2]) << 32) + + (((FLMUINT64)pucBuffer[ 3]) << 24) + + (((FLMUINT64)pucBuffer[ 4]) << 16) + + (((FLMUINT64)pucBuffer[ 5]) << 8) + pucBuffer[ 6]; + break; + + case 8: + *pui64Value = (((FLMUINT64)pucBuffer[ 1]) << 48) + + (((FLMUINT64)pucBuffer[ 2]) << 40) + + (((FLMUINT64)pucBuffer[ 3]) << 32) + + (((FLMUINT64)pucBuffer[ 4]) << 24) + + (((FLMUINT64)pucBuffer[ 5]) << 16) + + (((FLMUINT64)pucBuffer[ 6]) << 8) + pucBuffer[ 7]; + break; + + case 9: + *pui64Value = (((FLMUINT64)pucBuffer[ 1]) << 56) + + (((FLMUINT64)pucBuffer[ 2]) << 48) + + (((FLMUINT64)pucBuffer[ 3]) << 40) + + (((FLMUINT64)pucBuffer[ 4]) << 32) + + (((FLMUINT64)pucBuffer[ 5]) << 24) + + (((FLMUINT64)pucBuffer[ 6]) << 16) + + (((FLMUINT64)pucBuffer[ 7]) << 8) + pucBuffer[ 8]; + break; + + default: + *pui64Value = 0; + flmAssert( 0); + break; + } + } + +Exit: + + *ppucBuffer = pucBuffer + uiSENLength; + + return( rc); +} + +/**************************************************************************** +Desc: COM query interface +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::QueryInterface( + RXFLMIID riid, + void ** ppvInt) +{ + RCODE rc = NE_XFLM_OK; + + if( (f_memcmp(&riid, &Internal_IID_IF_DbSystem, + sizeof( Internal_IID_IF_DbSystem)) == 0) || + (f_memcmp(&riid, &Internal_IID_XFLMIUnknown, + sizeof( Internal_IID_XFLMIUnknown)) == 0) ) + { + *ppvInt = this; + AddRef(); + } + else + { + rc = RC_SET( NE_XFLM_UNSUPPORTED_INTERFACE); + } + + return( rc); +} + +/**************************************************************************** +Desc: Increment the database system use count +****************************************************************************/ +FLMUINT32 XFLMAPI F_DbSystem::AddRef(void) +{ + FLMUINT32 ui32RefCnt = ftkAtomicIncrement( &m_ui32RefCnt); + + // Note: We don't bother with a call to LockModule() here because + // it's done in the constructor. In fact, it's unlikely that + // this AddRef() will ever be called, because the constructor + // already sets the ref count to 1 and it's unlikely that there + // will be to references to the same DbSystem object... + + return ui32RefCnt; +} + +/**************************************************************************** +Desc: Decrement the database system use count +****************************************************************************/ +FLMUINT32 XFLMAPI F_DbSystem::Release(void) +{ + FLMUINT32 ui32RefCnt = ftkAtomicDecrement( &m_ui32RefCnt); + + if (ui32RefCnt == 0) + { + delete this; + } + + return ui32RefCnt; +} + +/**************************************************************************** +Desc: Returns a pointer to the ASCII string representation + of a return code. +****************************************************************************/ +const char * F_DbSystem::errorString( + RCODE rc) +{ + const char * pszErrorStr; + + if( rc == NE_XFLM_OK) + { + pszErrorStr = "NE_XFLM_OK"; + } + else if( rc > NE_XFLM_FIRST_COMMON_ERROR && + rc < NE_XFLM_LAST_COMMON_ERROR) + { + pszErrorStr = gv_FlmCommonErrors[ + rc - NE_XFLM_FIRST_COMMON_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_GENERAL_ERROR && + rc < NE_XFLM_LAST_GENERAL_ERROR) + { + pszErrorStr = gv_FlmGeneralErrors[ + rc - NE_XFLM_FIRST_GENERAL_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_DOM_ERROR && + rc < NE_XFLM_LAST_DOM_ERROR) + { + pszErrorStr = gv_FlmDomErrors[ + rc - NE_XFLM_FIRST_DOM_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_IO_ERROR && + rc < NE_XFLM_LAST_IO_ERROR) + { + pszErrorStr = gv_FlmIoErrors[ + rc - NE_XFLM_FIRST_IO_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_NET_ERROR && + rc < NE_XFLM_LAST_NET_ERROR) + { + pszErrorStr = gv_FlmNetErrors[ + rc - NE_XFLM_FIRST_NET_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_QUERY_ERROR && + rc < NE_XFLM_LAST_QUERY_ERROR) + { + pszErrorStr = gv_FlmQueryErrors[ + rc - NE_XFLM_FIRST_QUERY_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_STREAM_ERROR && + rc < NE_XFLM_LAST_STREAM_ERROR) + { + pszErrorStr = gv_FlmStreamErrors[ + rc - NE_XFLM_FIRST_STREAM_ERROR - 1].pszErrorStr; + } + else if( rc > NE_XFLM_FIRST_NICI_ERROR && + rc < NE_XFLM_LAST_NICI_ERROR) + { + pszErrorStr = gv_FlmNiciErrors[ + rc - NE_XFLM_FIRST_NICI_ERROR - 1].pszErrorStr; + } + else + { + pszErrorStr = "Unknown error"; + } + + return( pszErrorStr); +} + +/**************************************************************************** +Desc: Checks the error code mapping tables on startup +****************************************************************************/ +RCODE F_DbSystem::checkErrorCodeTables( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_GENERAL_ERROR - NE_XFLM_FIRST_GENERAL_ERROR - 1); + uiLoop++) + { + if( gv_FlmGeneralErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_GENERAL_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_DOM_ERROR - NE_XFLM_FIRST_DOM_ERROR - 1); + uiLoop++) + { + if( gv_FlmDomErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_DOM_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_IO_ERROR - NE_XFLM_FIRST_IO_ERROR - 1); + uiLoop++) + { + if( gv_FlmIoErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_IO_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_NET_ERROR - NE_XFLM_FIRST_NET_ERROR - 1); + uiLoop++) + { + if( gv_FlmNetErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_NET_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_QUERY_ERROR - NE_XFLM_FIRST_QUERY_ERROR - 1); + uiLoop++) + { + if( gv_FlmQueryErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_QUERY_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_STREAM_ERROR - NE_XFLM_FIRST_STREAM_ERROR - 1); + uiLoop++) + { + if( gv_FlmStreamErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_STREAM_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + + for( uiLoop = 0; + uiLoop < (NE_XFLM_LAST_NICI_ERROR - NE_XFLM_FIRST_NICI_ERROR - 1); + uiLoop++) + { + if( gv_FlmNiciErrors[ uiLoop].rc != + (RCODE)(uiLoop + NE_XFLM_FIRST_NICI_ERROR + 1)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RCODE_TABLE); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocates an F_DbSystem object for non-COM applications +****************************************************************************/ +RCODE FlmAllocDbSystem( + IF_DbSystem ** ppDbSystem) +{ + flmAssert( ppDbSystem && *ppDbSystem == NULL); + + if( (*ppDbSystem = (IF_DbSystem *)f_new F_DbSystem) == NULL) + { + return( RC_SET( NE_XFLM_MEM)); + } + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Check for the existence of FLAIM performance tuning file. If + found this routine will parse the contents and set the global + performance tuning variables. + + Looks for the following strings within the .ini file + cache= # Set a hard memory limit + cache= # Set a hard memory limit or dynamically + # adjusting limit. + # Multiple options may be specified, in + # any order, separated by commas. All + # are optional. + %: # percentage of available or physical memory. + AVAIL or TOTAL # percentage is for available physical + # memory or total physical memory. NOTE: + # these are ignored for a dynamic limit. + MIN: # minimum number of bytes + MAX: # maximum number of bytes + LEAVE: # minimum nmber of bytes to leave + DYN or HARD # dynamic or hard limit + PRE # preallocate cache - valid only with HARD setting. + + Examples: + + cache=DYN,%:75,MIN:16000000 # Dynamic limit of 75% available memory, + # minimum of 16 megabytes. + cache=HARD,%:75,MIN:16000000 # Hard limit of 75% total physical memory, + # minimum of 16 megabytes. + cache=HARD,%:75,MIN:16000000,PRE + # Hard limit of 75% total physical memory, + # minimum of 16 megabytes, preallocated. + cache=8000000 # Old style - hard limit of 8 megabytes. + + cacheadjustinterval= + cachecleanupinterval= +****************************************************************************/ + +#define XFLM_INI_FILE_NAME "_xflm.ini" + +RCODE F_DbSystem::readIniFile() +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + F_IniFile * pIniFile = NULL; + char szIniFileName [F_PATH_MAX_SIZE]; + FLMUINT uiParamValue; + FLMUINT uiMaxDirtyCache; + FLMUINT uiLowDirtyCache; + + // Initialize the ini file object... + + if ((pIniFile = f_new F_IniFile) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pIniFile->Init())) + { + goto Exit; + } + + // Lock the ini file mutex + + f_mutexLock( gv_XFlmSysData.hIniMutex); + bMutexLocked = TRUE; + + if (RC_BAD( rc = flmGetIniFileName( (FLMBYTE *)szIniFileName, + F_PATH_MAX_SIZE))) + { + goto Exit; + } + + if (RC_BAD( rc = pIniFile->Read( szIniFileName))) + { + goto Exit; + } + + // Set FLAIM parameters from the buffer read from the .ini file. + + setCacheParams( pIniFile); + + flmGetUintParam( XFLM_INI_CACHE_ADJUST_INTERVAL, + XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL, + &uiParamValue, pIniFile); + setCacheAdjustInterval( uiParamValue); + + flmGetUintParam( XFLM_INI_CACHE_CLEANUP_INTERVAL, + XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL, + &uiParamValue, pIniFile); + setCacheCleanupInterval( uiParamValue); + + flmGetUintParam( XFLM_INI_MAX_DIRTY_CACHE, 0, &uiMaxDirtyCache, pIniFile); + + flmGetUintParam( XFLM_INI_LOW_DIRTY_CACHE, 0, &uiLowDirtyCache, pIniFile); + + // Use FLAIM's default behavior if uiMaxDirtyCache is zero. FLAIM's + // default behavior is to use a maximum value for dirty cache. + + if (uiMaxDirtyCache) + { + setDirtyCacheLimits( uiMaxDirtyCache, uiLowDirtyCache); + } + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hIniMutex); + } + + pIniFile->Release(); + return( rc); +} + +/**************************************************************************** +Desc: Given a tag and a value this function will open/create the + ini file and replace or insert the tag with it's value. + NOTE: This function expects gv_XFlmSysData.hIniMutex to already be + locked! +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::updateIniFile( + const char * pszParamName, + const char * pszValue) +{ + RCODE rc = NE_XFLM_OK; + F_IniFile * pIniFile = NULL; + char szIniFileName [F_PATH_MAX_SIZE]; + + if ((pIniFile = f_new F_IniFile) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pIniFile->Init())) + { + goto Exit; + } + + if (RC_BAD( rc = flmGetIniFileName( (FLMBYTE *)szIniFileName, + F_PATH_MAX_SIZE))) + { + goto Exit; + } + + if (RC_BAD( rc = pIniFile->Read( szIniFileName))) + { + goto Exit; + } + + if (RC_BAD( rc = pIniFile->SetParam( pszParamName, pszValue))) + { + goto Exit; + } + + if (RC_BAD( rc = pIniFile->Write())) + { + goto Exit; + } + +Exit: + + pIniFile->Release(); + return( rc); +} + +/**************************************************************************** +Desc: Sets the parameters for controlling cache. +****************************************************************************/ +RCODE F_DbSystem::setCacheParams( + F_IniFile * pIniFile) +{ + RCODE rc = NE_XFLM_OK; + char * pszCacheParam = NULL; + char cChar; + char * pszSave; + char * pszValueName; + FLMBOOL bDynamicCacheAdjust; + FLMBOOL bCalcLimitOnAvail = FALSE; + FLMUINT uiCachePercent; + FLMUINT uiCacheMin; + FLMUINT uiCacheMax; + FLMUINT uiCacheMinToLeave; + FLMBOOL bPreallocated = FALSE; + + // Set up appropriate defaults. + + bDynamicCacheAdjust = getDynamicCacheSupported(); + + if( bDynamicCacheAdjust) + { + uiCachePercent = XFLM_DEFAULT_CACHE_ADJUST_PERCENT; + bCalcLimitOnAvail = TRUE; + uiCacheMin = XFLM_DEFAULT_CACHE_ADJUST_MIN; + uiCacheMax = XFLM_DEFAULT_CACHE_ADJUST_MAX; + uiCacheMinToLeave = 0; + } + else + { + uiCachePercent = 0; + uiCacheMin = + uiCacheMax = XFLM_DEFAULT_CACHE_ADJUST_MIN; + uiCacheMinToLeave = 0; + } + + // Extract values from the cache parameter + flmGetStringParam( XFLM_INI_CACHE, &pszCacheParam, pIniFile); + + if ((pszCacheParam != NULL) && (pszCacheParam[0] != '\0')) + { + // Parse until we hit white space. + + while (*pszCacheParam && + *pszCacheParam != ' ' && + *pszCacheParam != '\n' && + *pszCacheParam != '\r' && + *pszCacheParam != '\t') + { + + // Get the value name. + + pszValueName = pszCacheParam; + while (*pszCacheParam && + *pszCacheParam != ' ' && + *pszCacheParam != '\n' && + *pszCacheParam != '\r' && + *pszCacheParam != '\t' && + *pszCacheParam != ':' && + *pszCacheParam != ',' && + *pszCacheParam != ';') + { + pszCacheParam++; + } + pszSave = pszCacheParam; + cChar = *pszSave; + *pszSave = 0; + + // New options only supported on platforms that have ways + // of getting physical memory size and available physical + // memory size. + + if ((f_stricmp( pszValueName, "MAX") == 0) || + (f_stricmp( pszValueName, "MAXIMUM") == 0)) + { + if (cChar == ':') + { + pszCacheParam++; + flmGetNumParam( &pszCacheParam, + &uiCacheMax); + } + } + else if ((f_stricmp( pszValueName, "MIN") == 0) || + (f_stricmp( pszValueName, "MINIMUM") == 0)) + { + if (cChar == ':') + { + pszCacheParam++; + flmGetNumParam( &pszCacheParam, + &uiCacheMin); + } + } + else if ((f_stricmp( pszValueName, "%") == 0) || + (f_stricmp( pszValueName, "PERCENT") == 0)) + { + if (cChar == ':') + { + pszCacheParam++; + flmGetNumParam( &pszCacheParam, + &uiCachePercent); + } + } + else if ((f_stricmp( pszValueName, "DYNAMIC") == 0) || + (f_stricmp( pszValueName, "DYN") == 0)) + { + if (cChar == ':' || cChar == ',' || cChar == ';') + { + pszCacheParam++; + } + bDynamicCacheAdjust = TRUE; + } + else if (f_stricmp( pszValueName, "AVAIL") == 0) + { + if (cChar == ':' || cChar == ',' || cChar == ';') + { + pszCacheParam++; + } + bCalcLimitOnAvail = TRUE; + } + else if (f_stricmp( pszValueName, "TOTAL") == 0) + { + if (cChar == ':' || cChar == ',' || cChar == ';') + { + pszCacheParam++; + } + bCalcLimitOnAvail = FALSE; + } + else if (f_stricmp( pszValueName, "LEAVE") == 0) + { + if (cChar == ':') + { + pszCacheParam++; + flmGetNumParam( &pszCacheParam, + &uiCacheMinToLeave); + } + } + else if (f_stricmp( pszValueName, "HARD") == 0) + { + if (cChar == ':' || cChar == ',' || cChar == ';') + { + pszCacheParam++; + } + bDynamicCacheAdjust = FALSE; + } + else if (f_stricmp( pszValueName, "PRE") == 0) + { + if (cChar == ':' || cChar == ',' || cChar == ';') + { + pszCacheParam++; + } + bPreallocated = TRUE; + } + else + { + *pszSave = cChar; + + // This will handle the old way where they just put in a + // number for the byte maximum. + + bDynamicCacheAdjust = FALSE; + uiCachePercent = 0; + uiCacheMax = f_atol( pszValueName); + + // Don't really have to set these, but do it just to + // be clean. + + uiCacheMin = 0; + uiCacheMinToLeave = 0; + bCalcLimitOnAvail = FALSE; + break; + } + *pszSave = cChar; + } + } + + // Set up the stuff for controlling cache - hard limit or dynamically + // adjusting limit. + + if( bDynamicCacheAdjust) + { + if (RC_BAD( rc = setDynamicMemoryLimit( + uiCachePercent, + uiCacheMin, uiCacheMax, + uiCacheMinToLeave))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = setHardMemoryLimit( uiCachePercent, + bCalcLimitOnAvail, uiCacheMin, + uiCacheMax, uiCacheMinToLeave, + bPreallocated))) + { + goto Exit; + } + } + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: Gets a string parameter from the .ini file. +****************************************************************************/ +FSTATIC void flmGetStringParam( + const char * pszParamName, + char ** ppszValue, + F_IniFile * pIniFile) +{ + flmAssert( pIniFile); + (void)pIniFile->GetParam( pszParamName, ppszValue); +} + +/**************************************************************************** +Desc: Gets one of the number parameters for setting cache stuff. +****************************************************************************/ +FSTATIC void flmGetNumParam( + char ** ppszParam, + FLMUINT * puiNum) +{ + char * pszTmp = *ppszParam; + FLMUINT uiNum = 0; + + while ((*pszTmp >= '0') && (*pszTmp <= '9')) + { + uiNum *= 10; + uiNum += (FLMUINT)(*pszTmp - '0'); + pszTmp++; + } + + // Run past any other junk up to allowed terminators. + + while (*pszTmp && + *pszTmp != ' ' && + *pszTmp != '\n' && + *pszTmp != '\r' && + *pszTmp != '\t' && + *pszTmp != ':' && + *pszTmp != ',' && + *pszTmp != ';') + { + pszTmp++; + } + + // Skip past trailing colon, comma, or semicolon. + + if (*pszTmp == ':' || *pszTmp == ',' || *pszTmp == ';') + { + pszTmp++; + } + + *puiNum = uiNum; + *ppszParam = pszTmp; +} + +/**************************************************************************** +Desc: Gets a FLMUINT parameter from the .ini file. +****************************************************************************/ +FSTATIC void flmGetUintParam( + const char * pszParamName, + FLMUINT uiDefaultValue, + FLMUINT * puiUint, + F_IniFile * pIniFile) +{ + flmAssert( pIniFile); + if (!pIniFile->GetParam( pszParamName, puiUint)) + { + *puiUint = uiDefaultValue; + } +} + +/**************************************************************************** +Desc: Gets a FLMBOOL parameter from the .ini file. +****************************************************************************/ +void flmGetBoolParam( + const char * pszParamName, + FLMBOOL bDefaultValue, + FLMBOOL * pbBool, + F_IniFile * pIniFile) +{ + flmAssert( pIniFile); + if (!pIniFile->GetParam( pszParamName, pbBool)) + { + *pbBool = bDefaultValue; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FSTATIC RCODE flmGetIniFileName( + FLMBYTE * pszIniFileName, + FLMUINT uiBufferSz + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNameLen = 0; + F_DirHdl * pDirHdl = NULL; + + if (!uiBufferSz) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + // See if we have an environment variable to tell us wher the ini file should be. + + f_getenv( "XFLM_INI_PATH", + pszIniFileName, + uiBufferSz, + &uiFileNameLen); + + if( !uiFileNameLen) + { + // Perhaps we can find a data directory. If there is one, we will + // look in there. + + if( (pDirHdl = f_new F_DirHdl) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_OK( rc = pDirHdl->OpenDir( (char *)".", (char *)"data"))) + { + if (RC_OK( rc = pDirHdl->Next())) + { + if (pDirHdl->CurrentItemIsDir()) + { + // Directory exists. We will look there. + f_strcpy( pszIniFileName, "data"); + } + else + { + goto NoDataDir; + } + } + else + { + rc = NE_XFLM_OK; + goto NoDataDir; + } + } + else + { +NoDataDir: + // Data directory does not exist. We will look in the + // current (default) dir. + + f_strcpy( pszIniFileName, "."); + } + } + + gv_pFileSystem->pathAppend( (char *)pszIniFileName, XFLM_INI_FILE_NAME); + +Exit: + + if (pDirHdl) + { + pDirHdl->Release(); + } + + return( rc); +} diff --git a/version5/src/ftk.h b/version5/src/ftk.h new file mode 100644 index 0000000..8a4c6c7 --- /dev/null +++ b/version5/src/ftk.h @@ -0,0 +1,2325 @@ +//------------------------------------------------------------------------------ +// 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 FTK_H +#define FTK_H + + class F_DOMNode; + + /**************************************************************************** + Desc: NLM + ****************************************************************************/ + #if defined( FLM_NLM) + + #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 + + // External Netware Symbols + + extern "C" + { + FLMUINT f_getNLMHandle( void); + + RCODE f_netwareStartup( void); + + 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) + + #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 + + /* config.h is automatically generated file from configure. This file is + * platform dependent. configure.in *must* check for AC_C_BIGENDIAN to + * determine whether this is a big-endian or a little-endian + * platform. + */ + #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 + + #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 + + #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 + + /**************************************************************************** + Desc: Internal base class + ****************************************************************************/ + class F_OSBase + { + public: + + F_OSBase() + { + m_ui32RefCnt = 1; + } + + virtual ~F_OSBase() + { + } + + FINLINE FLMUINT getRefCount( void) + { + return( m_ui32RefCnt); + } + + 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 FLMUINT32 XFLMAPI AddRef( void) + { + m_ui32RefCnt++; + return m_ui32RefCnt; + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + FLMUINT32 ui32RefCnt = --m_ui32RefCnt; + + if( !ui32RefCnt) + { + delete this; + } + + return( ui32RefCnt); + } + + protected: + + FLMUINT32 m_ui32RefCnt; + }; + + /**************************************************************************** + Desc: This class is used to do pool memory allocations. + ****************************************************************************/ + class F_Pool : public IF_Pool, public XF_Base + { + public: + + typedef struct PoolMemoryBlock + { + PoolMemoryBlock * pPrevBlock; // Points to the previous block + FLMUINT uiBlockSize; // This block's size + FLMUINT uiFreeOffset; // Free offset in the block + FLMUINT uiFreeSize; // Amount of free memory left in block + } MBLK; + + typedef struct + { + FLMUINT uiAllocBytes; // Total number of bytes requested from + // poolAlloc and poolCalloc methods + FLMUINT uiCount; // Number of Free/Resets performed on + // the pool + } POOL_STATS; + + // Constructors and Destructors + + F_Pool() + { + m_uiBytesAllocated = 0; + m_pLastBlock = NULL; + m_pPoolStats = NULL; + m_uiBlockSize = 0; + } + + ~F_Pool(); + + FINLINE void poolInit( // flalloc.cpp + FLMUINT uiBlockSize) + { + m_uiBlockSize = uiBlockSize; + } + + void smartPoolInit( // flalloc.cpp + POOL_STATS * pPoolStats); + + RCODE poolAlloc( // flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr); + + RCODE poolCalloc( + FLMUINT uiSize, + void ** ppvPtr); + + void poolFree( void); // flalloc.cpp + + void poolReset( // flalloc.cpp + void * pvMark, + FLMBOOL bReduceFirstBlock = FALSE); + + FINLINE void * poolMark( void) + { + return (void *)(m_pLastBlock + ? (FLMBYTE *)m_pLastBlock + m_pLastBlock->uiFreeOffset + : NULL); + } + + FINLINE FLMUINT getBlockSize( void) + { + return( m_uiBlockSize); + } + + FINLINE FLMUINT 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; // Points to last allocated memory block + FLMUINT m_uiBlockSize; // Default size of each memory block + FLMUINT m_uiBytesAllocated; // Number of bytes allocated since pool + // init and/or reset + POOL_STATS * m_pPoolStats; // [optional] only used by smart pools + }; + + /**************************************************************************** + 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 + + /**************************************************************************** + CROSS PLATFORM DEFINITIONS + ****************************************************************************/ + #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)) + + char * f_uwtoa( + FLMUINT16 value, + char * ptr); + + char * f_udtoa( + FLMUINT value, + char * ptr); + + char * f_wtoa( + FLMINT16 value, + char * ptr); + + char * f_dtoa( + FLMINT value, + char * ptr); + + char * f_ui64toa( + FLMUINT64 value, + char * ptr); + + char * f_i64toa( + FLMINT64 value, + char * ptr); + + FLMINT f_atoi( + char * ptr); + + FLMINT f_atol( + char * ptr); + + FLMINT f_atod( + char * ptr); + + FLMUINT f_atoud( + char * ptr, + FLMBOOL bAllowUnprefixedHex = FALSE); + + FLMUINT64 f_atou64( + char * pszBuf); + + FLMUINT f_unilen( + const FLMUNICODE * puzStr); + + FLMUNICODE * f_unicpy( + FLMUNICODE * puzDestStr, + FLMUNICODE * puzSrcStr); + + FLMUNICODE f_unitolower( + FLMUNICODE uChar); + + FLMINT f_unicmp( + FLMUNICODE * puzStr1, + FLMUNICODE * puzStr2); + + FLMINT f_uniicmp( + FLMUNICODE * puzStr1, + FLMUNICODE * puzStr2); + + FLMINT f_uninativecmp( + const FLMUNICODE * puzStr1, + const char * pszStr2); + + FLMINT f_uninativencmp( + const FLMUNICODE * puzStr1, + const char * pszStr2, + FLMUINT uiCount); + + /**************************************************************************** + 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) + #include + + #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); + + /**************************************************************************** + Character Value Constants + ****************************************************************************/ + + #define ASCII_TAB 0x09 + #define ASCII_NEWLINE 0x0A + #define ASCII_CR 0x0D + #define ASCII_CTRLZ 0x1A + #define ASCII_SPACE 0x20 + #define ASCII_DQUOTE 0x22 + #define ASCII_POUND 0x23 + #define ASCII_DOLLAR 0x24 + #define ASCII_SQUOTE 0x27 + #define ASCII_WILDCARD 0x2A + #define ASCII_PLUS 0x2B + #define ASCII_COMMA 0x2C + #define ASCII_DASH 0x2D + #define ASCII_MINUS 0x2D + #define ASCII_DOT 0x2E + #define ASCII_SLASH 0x2F + #define ASCII_COLON 0x3A + #define ASCII_SEMICOLON 0x3B + #define ASCII_EQUAL 0x3D + #define ASCII_QUESTIONMARK 0x3F + #define ASCII_AT 0x40 + #define ASCII_BACKSLASH 0x5C + #define ASCII_CARAT 0x5E + #define ASCII_UNDERSCORE 0x5F + #define ASCII_TILDE 0x7E + #define ASCII_AMP 0x26 + + #define ASCII_UPPER_A 0x41 + #define ASCII_UPPER_B 0x42 + #define ASCII_UPPER_C 0x43 + #define ASCII_UPPER_D 0x44 + #define ASCII_UPPER_E 0x45 + #define ASCII_UPPER_F 0x46 + #define ASCII_UPPER_G 0x47 + #define ASCII_UPPER_H 0x48 + #define ASCII_UPPER_I 0x49 + #define ASCII_UPPER_J 0x4A + #define ASCII_UPPER_K 0x4B + #define ASCII_UPPER_L 0x4C + #define ASCII_UPPER_M 0x4D + #define ASCII_UPPER_N 0x4E + #define ASCII_UPPER_O 0x4F + #define ASCII_UPPER_P 0x50 + #define ASCII_UPPER_Q 0x51 + #define ASCII_UPPER_R 0x52 + #define ASCII_UPPER_S 0x53 + #define ASCII_UPPER_T 0x54 + #define ASCII_UPPER_U 0x55 + #define ASCII_UPPER_V 0x56 + #define ASCII_UPPER_W 0x57 + #define ASCII_UPPER_X 0x58 + #define ASCII_UPPER_Y 0x59 + #define ASCII_UPPER_Z 0x5A + + #define ASCII_LOWER_A 0x61 + #define ASCII_LOWER_B 0x62 + #define ASCII_LOWER_C 0x63 + #define ASCII_LOWER_D 0x64 + #define ASCII_LOWER_E 0x65 + #define ASCII_LOWER_F 0x66 + #define ASCII_LOWER_G 0x67 + #define ASCII_LOWER_H 0x68 + #define ASCII_LOWER_I 0x69 + #define ASCII_LOWER_J 0x6A + #define ASCII_LOWER_K 0x6B + #define ASCII_LOWER_L 0x6C + #define ASCII_LOWER_M 0x6D + #define ASCII_LOWER_N 0x6E + #define ASCII_LOWER_O 0x6F + #define ASCII_LOWER_P 0x70 + #define ASCII_LOWER_Q 0x71 + #define ASCII_LOWER_R 0x72 + #define ASCII_LOWER_S 0x73 + #define ASCII_LOWER_T 0x74 + #define ASCII_LOWER_U 0x75 + #define ASCII_LOWER_V 0x76 + #define ASCII_LOWER_W 0x77 + #define ASCII_LOWER_X 0x78 + #define ASCII_LOWER_Y 0x79 + #define ASCII_LOWER_Z 0x7A + + #define ASCII_ZERO 0x30 + #define ASCII_ONE 0x31 + #define ASCII_TWO 0x32 + #define ASCII_THREE 0x33 + #define ASCII_FOUR 0x34 + #define ASCII_FIVE 0x35 + #define ASCII_SIX 0x36 + #define ASCII_SEVEN 0x37 + #define ASCII_EIGHT 0x38 + #define ASCII_NINE 0x39 + + #define NATIVE_SPACE ' ' + #define NATIVE_DOT '.' + #define NATIVE_PLUS '+' + #define NATIVE_MINUS '-' + #define NATIVE_WILDCARD '*' + #define NATIVE_QUESTIONMARK '?' + + #define NATIVE_UPPER_A 'A' + #define NATIVE_UPPER_F 'F' + #define NATIVE_UPPER_X 'X' + #define NATIVE_UPPER_Z 'Z' + #define NATIVE_LOWER_A 'a' + #define NATIVE_LOWER_F 'f' + #define NATIVE_LOWER_X 'x' + #define NATIVE_LOWER_Z 'z' + #define NATIVE_ZERO '0' + #define NATIVE_NINE '9' + + #define f_stringToAscii( str) + + #define f_toascii( native) (native) + + #define f_tonative( ascii) (ascii) + + #define f_toupper( native) (((native) >= '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 + + // Unicode character constants + + #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); + } + + /**************************************************************************** + WORD/BYTE ORDERING MACROS + ****************************************************************************/ + + FLMUINT32 byteToLong( FLMBYTE * ptr); + #define byteToLong(p) ( \ + ((FLMUINT32) ( ((((FLMBYTE *)(p))[ 0]) << 8) | (((FLMBYTE *)(p))[ 1]) ) << 16 ) | \ + (FLMUINT16) ( ((((FLMBYTE *)(p))[ 2]) << 8) | (((FLMBYTE *)(p))[ 3]) ) ) + + FINLINE FLMUINT64 byteToLong64( + 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 byteToInt( FLMBYTE * ptr); + #define byteToInt(p) ( \ + (FLMUINT16) ( ((((FLMBYTE *)(p))[ 0]) << 8) | (((FLMBYTE *)(p))[ 1]) ) ) + + void longToByte( FLMINT32 uiNum, FLMBYTE *ptr); + #define 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 long64ToByte( FLMINT64 uiNum, FLMBYTE *ptr); + #define 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 intToByte( FLMINT16 uiNum, FLMBYTE *ptr); + #define 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 + + /**************************************************************************** + File Path Functions & 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 + + /**************************************************************************** + CPU Release Functions + ****************************************************************************/ + + #ifdef FLM_NLM + #define f_yieldCPU() pthread_yield() + #else + #define f_yieldCPU() + #endif + + void f_sleep( + FLMUINT uiMilliseconds); + + #ifdef FLM_WIN + #define f_sleep( uiMilliseconds) Sleep( (DWORD)uiMilliseconds) + #endif + + /**************************************************************************** + Random Numbers + ****************************************************************************/ + + #define MAX_RANDOM 2147483646L + + class F_RandomGenerator : public XF_RefCount, public XF_Base + { + public: + + void randomize( void); + + void randomSetSeed( + FLMINT32 i32seed); + + FLMINT32 randomLong( void); + + FLMINT32 randomChoice( + FLMINT32 lo, + FLMINT32 hi); + + FLMINT randomTruth( + FLMINT iPercentageTrue); + + FLMINT getSeed( void) + { + return( m_i32Seed); + } + + private: + + FLMINT32 m_i32Seed; + }; + + /**************************************************************************** + 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); + + #if defined( FLM_UNIX) + unsigned f_timeGetMilliTime(); + #endif + + FINLINE FLMUINT flmLocalToUTC( + FLMUINT uiSeconds) + { + return( uiSeconds + f_timeGetLocalOffset()); + } + + /**************************************************************************** + Atomic Increment, Decrement, Exchange Functions + ****************************************************************************/ + + FLMUINT32 ftkAtomicIncrement( + FLMUINT32 * puiTarget); + + FLMUINT32 ftkAtomicDecrement( + FLMUINT32 * puiTarget); + + FLMUINT32 ftkAtomicExchange( + FLMUINT32 * puiTarget, + FLMUINT32 uiValue); + + #if defined( FLM_WIN) + + FINLINE FLMUINT32 ftkAtomicIncrement( + FLMUINT32 * puiTarget) + { + __asm + { + mov eax, 1 + mov ecx, puiTarget + lock xadd [ecx], eax + inc eax + } + } + + FINLINE FLMUINT32 ftkAtomicDecrement( + FLMUINT32 * puiTarget) + { + __asm + { + mov eax, 0ffffffffh + mov ecx, puiTarget + lock xadd [ecx], eax + dec eax + } + } + + FINLINE FLMUINT32 ftkAtomicExchange( + FLMUINT32 * puiTarget, + FLMUINT32 ui32Value) + { + __asm + { + mov eax, ui32Value + mov ecx, puiTarget + lock xchg eax, [ecx] + } + } + + #elif defined( FLM_NLM) + + extern "C" + { + LONG atomic_xchg( LONG *address, LONG value); + } + + #define ftkAtomicExchange( puiTarget, uiValue) \ + ((FLMUINT32)atomic_xchg( (LONG *)(puiTarget), (LONG)(uiValue))) + + #if defined( FLM_WATCOM_NLM) + + #pragma aux ftkAtomicIncrement parm [ecx]; + #pragma aux ftkAtomicIncrement = \ + 0xB8 0x01 0x00 0x00 0x00 /* mov eax, 1 */ \ + 0xF0 0x0F 0xC1 0x01 /* lock xadd [ecx], eax */ \ + 0x40 /* inc eax */ \ + parm [ecx] \ + modify exact [eax]; + + #pragma aux ftkAtomicDecrement parm [ecx]; + #pragma aux ftkAtomicDecrement = \ + 0xB8 0xFF 0xFF 0xFF 0xFF /* mov eax, 0ffffffffh */ \ + 0xF0 0x0F 0xC1 0x01 /* lock xadd [ecx], eax */ \ + 0x48 /* dec eax */ \ + parm [ecx] \ + modify exact [eax]; + + #endif + + #elif defined( FLM_UNIX) + + #if defined(__GNUC__) && defined(__i386__) + #define ATOMIC_INCDEC_SUPPORT 1 + #endif + + #else + #error Atomic operations are not available on the target platform + #endif + + /**************************************************************************** + Pseudo Serial Numbers + ****************************************************************************/ + + RCODE f_initSerialNumberGenerator( void); + + RCODE f_createSerialNumber( + FLMBYTE * pszGuid); + + void f_freeSerialNumberGenerator( void); + + /**************************************************************************** + CRC + ****************************************************************************/ + + RCODE f_initCRCTable( + FLMUINT32 ** ppui32CRCTbl); + + void f_updateCRC( + FLMUINT32 * pui32CRCTbl, + FLMBYTE * pucBlk, + FLMUINT uiBlkSize, + FLMUINT32 * pui32CRC); + + #define f_freeCRCTable( ppui32CRCTbl) \ + f_free( ppui32CRCTbl) + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_IStream : public IF_IStream, public XF_Base + { + public: + + F_IStream(); + + virtual ~F_IStream(); + + void lockModule( void); + + private: + + FLMBOOL m_bLockedModule; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_OStream : public IF_OStream, public XF_Base + { + public: + + F_OStream(); + + virtual ~F_OStream(); + + void lockModule( void); + + private: + + FLMBOOL m_bLockedModule; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + class F_PosIStream : public IF_PosIStream, public XF_Base + { + public: + + F_PosIStream(); + + virtual ~F_PosIStream(); + + void lockModule( void); + + private: + + FLMBOOL m_bLockedModule; + }; + + /**************************************************************************** + 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 XFLMAPI open( + const FLMBYTE * pucBuffer, + FLMUINT uiLength, + FLMBYTE ** ppucAllocatedBuffer = NULL); + + FINLINE FLMUINT64 XFLMAPI totalSize( void) + { + flmAssert( m_bIsOpen); + return( m_uiBufferLen); + } + + FINLINE FLMUINT64 XFLMAPI remainingSize( void) + { + flmAssert( m_bIsOpen); + return( m_uiBufferLen - m_uiOffset); + } + + void XFLMAPI close( void); + + FINLINE RCODE XFLMAPI positionTo( + FLMUINT64 ui64Position) + { + flmAssert( m_bIsOpen); + + if( ui64Position < m_uiBufferLen) + { + m_uiOffset = (FLMUINT)ui64Position; + } + else + { + m_uiOffset = m_uiBufferLen; + } + + return( NE_XFLM_OK); + } + + FINLINE FLMUINT64 XFLMAPI getCurrPosition( void) + { + flmAssert( m_bIsOpen); + return( m_uiOffset); + } + + RCODE XFLMAPI 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 XFLMAPI open( + const char * pszPath); + + void XFLMAPI close( void); + + RCODE XFLMAPI positionTo( + FLMUINT64 ui64Position); + + FLMUINT64 XFLMAPI totalSize( void); + + FLMUINT64 XFLMAPI remainingSize( void); + + FLMUINT64 XFLMAPI getCurrPosition( void); + + RCODE XFLMAPI 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 XFLMAPI open( + IF_IStream * pIStream, + FLMUINT uiBufferSize); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + void XFLMAPI close( void); + + FINLINE FLMUINT64 XFLMAPI totalSize( void) + { + if (!m_pIStream) + { + flmAssert( 0); + return( 0); + } + + return( m_uiBytesAvail); + } + + FINLINE FLMUINT64 XFLMAPI remainingSize( void) + { + if( !m_pIStream) + { + flmAssert( 0); + return( 0); + } + + return( m_uiBytesAvail - m_uiBufferOffset); + } + + FINLINE RCODE XFLMAPI positionTo( + FLMUINT64 ui64Position) + { + if( !m_pIStream) + { + flmAssert( 0); + return( RC_SET( NE_XFLM_ILLEGAL_OP)); + } + + if( ui64Position < m_uiBytesAvail) + { + m_uiBufferOffset = (FLMUINT)ui64Position; + } + else + { + m_uiBufferOffset = m_uiBytesAvail; + } + + return( NE_XFLM_OK); + } + + FINLINE FLMUINT64 XFLMAPI 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 XFLMAPI open( + IF_OStream * pOStream, + FLMUINT uiBufferSize); + + RCODE XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE XFLMAPI close( void); + + RCODE XFLMAPI 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 XFLMAPI open( + const char * pszFilePath, + FLMBOOL bTruncateIfExists); + + RCODE XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE XFLMAPI 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 XFLMAPI open( + const char * pszDirectory, + const char * pszBaseName); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + void XFLMAPI close( void); + + private: + + RCODE rollToNextFile( void); + + IF_IStream * m_pIStream; + FLMBOOL m_bOpen; + FLMBOOL m_bEndOfStream; + FLMUINT m_uiFileNum; + FLMUINT64 m_ui64FileOffset; + FLMBYTE m_szDirectory[ F_PATH_MAX_SIZE + 1]; + FLMBYTE 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 XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE XFLMAPI 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; + FLMBYTE m_szDirectory[ F_PATH_MAX_SIZE + 1]; + FLMBYTE 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; + } + + ~F_Base64DecoderIStream() + { + close(); + } + + RCODE XFLMAPI open( + IF_IStream * pIStream); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE void XFLMAPI close( void) + { + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + m_pIStream->close(); + } + + m_pIStream->Release(); + m_pIStream = NULL; + } + + m_uiAvailBytes = 0; + m_uiBufOffset = 0; + } + + 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; + } + + ~F_Base64EncoderIStream() + { + close(); + } + + RCODE XFLMAPI open( + IF_IStream * pIStream, + FLMBOOL bLineBreaks); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + FINLINE void XFLMAPI close( void) + { + if( m_pIStream) + { + if( m_pIStream->getRefCount() == 1) + { + m_pIStream->close(); + } + + m_pIStream->Release(); + m_pIStream = NULL; + } + } + + 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 XFLMAPI open( + IF_OStream * pOStream); + + RCODE XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten); + + RCODE XFLMAPI 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 XFLMAPI open( + IF_IStream * pIStream); + + RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead); + + void XFLMAPI 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; + }; + + /**************************************************************************** + 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 flmSprintfProcessFieldInfo( + FLMBYTE ** ppszFormat, + FLMUINT * puiWidth, + FLMUINT * puiPrecision, + FLMUINT * puiFlags, + f_va_list * args); + + void flmSprintfStringFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void flmSprintfCharFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void flmSprintfErrorFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void flmSprintfNotHandledFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + void flmSprintfNumberFormatter( + FLMBYTE ucFormatChar, + FLMUINT uiWidth, + FLMUINT uiPrecision, + FLMUINT uiFlags, + F_SPRINTF_INFO * pInfo, + f_va_list * args); + + FLMINT f_vsprintf( + char * pszDestStr, + const char * pszFormat, + f_va_list * args); + + FLMINT f_sprintf( + char * pszDestStr, + const char * pszFormat, + ...); + + /**************************************************************************** + Quick Sort + ****************************************************************************/ + + typedef FLMINT (* F_SORT_COMPARE_FUNC)( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + typedef void (* F_SORT_SWAP_FUNC)( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + FLMINT flmQSortUINTCompare( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + void flmQSortUINTSwap( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2); + + void f_qsort( + void * pvBuffer, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds, + F_SORT_COMPARE_FUNC fnCompare, + F_SORT_SWAP_FUNC fnSwap); + + /**************************************************************************** + Environment + ****************************************************************************/ + + void f_getenv( + const char * pszKey, + FLMBYTE * pszBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLen = NULL); + + /**************************************************************************** + NECESSARY INCLUDE FILES + ****************************************************************************/ + + #include "ftkmem.h" + #include "ftksem.h" + +#endif // FTK_H diff --git a/version5/src/ftkdir.cpp b/version5/src/ftkdir.cpp new file mode 100644 index 0000000..291acf0 --- /dev/null +++ b/version5/src/ftkdir.cpp @@ -0,0 +1,911 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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_XFLM_OK; + m_bFirstTime = TRUE; + m_bFindOpen = FALSE; + m_uiAttrib = 0; + m_szPattern[ 0] = '\0'; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * XFLMAPI F_DirHdl::CurrentItemName( void) +{ + const char * pszName = NULL; + + if( RC_OK( m_rc)) + { + pszName = m_szFileName; + } + + return( pszName); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL XFLMAPI F_DirHdl::CurrentItemIsDir( void) +{ + return( ((m_uiAttrib & XF_IO_FA_DIRECTORY) + ? TRUE + : FALSE)); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT64 XFLMAPI 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 XFLMAPI 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 = + XF_IO_FA_NORMAL | XF_IO_FA_RDONLY | XF_IO_FA_ARCHIVE | XF_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 XFLMAPI F_DirHdl::OpenDir( + const char * pszDirName, + const char * pszPattern) +{ + RCODE rc = NE_XFLM_OK; + + m_rc = NE_XFLM_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_XFLM_MEM); + goto Exit; + } + + f_strcpy( m_szPattern, pszPattern); + } + +Exit: + + return( rc); + +} + +/**************************************************************************** +Desc: Create a directory (and parent directories if necessary). +****************************************************************************/ +RCODE XFLMAPI F_DirHdl::CreateDir( + const char * pszDirPath) +{ + char * pszParentDir = NULL; + RCODE rc = NE_XFLM_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 gv_pFileSystem->pathReduce couldn't reduce the path at all, then an + // invalid path was supplied. + + if( f_strcmp( pszDirPath, pszParentDir) == 0) + { + rc = RC_SET( NE_XFLM_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->Exists( pszParentDir))) + { + if( !gv_pFileSystem->IsDir( pszParentDir)) + { + rc = RC_SET( NE_XFLM_IO_ACCESS_DENIED); + goto Exit; + } + } + + // Recurse on the parent directory + + else if( RC_BAD( rc = this->CreateDir( pszParentDir))) + { + goto Exit; + } + } + +#if defined( FLM_WIN) + + if( !CreateDirectory((LPTSTR)pszDirPath, NULL)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_CREATING_FILE); + } + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + + if( mkdir( (char *)pszDirPath, 0777) == -1) + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_CREATING_FILE); + } + +#endif + +Exit: + + if( pszParentDir) + { + f_free( &pszParentDir); + } + + return( rc); +} + +/**************************************************************************** +Desc: Remove a directory +Notes: The directory must be empty. +****************************************************************************/ +RCODE XFLMAPI F_DirHdl::RemoveDir( + const char * pszDirName) +{ +#if defined( FLM_WIN) + + if( !RemoveDirectory((LPTSTR)pszDirName)) + { + return( MapWinErrorToFlaim( GetLastError(), NE_XFLM_IO_DELETING_FILE)); + } + + return( NE_XFLM_OK); + +#elif defined( FLM_UNIX) || defined( FLM_NLM) + + if( rmdir( (char *)pszDirName) == -1) + { + return( MapErrnoToFlaimErr( errno, NE_XFLM_IO_DELETING_FILE)); + } + + return( NE_XFLM_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_XFLM_OK; + + f_memset( pFindData, 0, sizeof( F_IO_FIND_DATA)); + pFindData->findHandle = INVALID_HANDLE_VALUE; + pFindData->uiSearchAttrib = uiSearchAttrib; + + if( !pszSearchPath) + { + rc = RC_SET( NE_XFLM_IO_PATH_NOT_FOUND); + goto Exit; + } + + f_strcpy( pFindData->szSearchPath, pszSearchPath); + + if( uiSearchAttrib & XF_IO_FA_NORMAL ) + { + uiSearchAttrib |= XF_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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_XFLM_OK; + + if( !pszSearchPath) + { + rc = RC_SET( NE_XFLM_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 & XF_IO_FA_DIRECTORY) + { + pFindData->mode_flag |= S_IFDIR; + } + + if( uiSearchAttrib & XF_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_XFLM_IO_NO_MORE_FILES); + } + else + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_READING_FILE); + } + + goto Exit; + } + + // filter out ".." (PARENT) and "." (CURRENT) directories + + if( uiSearchAttrib & XF_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_XFLM_IO_NO_MORE_FILES); + } + else + { + rc = MapErrnoToFlaimErr( errno, NE_XFLM_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_XFLM_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_XFLM_OK; + +#ifdef FLM_WIN + + if( FindNextFile( pFindData->findHandle, + &(pFindData->findBuffer)) == FALSE) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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 = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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_XFLM_IO_NO_MORE_FILES)); + } + + return( MapErrnoToFlaimErr( errno, NE_XFLM_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_XFLM_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 & XF_IO_FA_DIRECTORY) && + (pFindData->findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)))) + { + if( (pFindData->findBuffer.dwFileAttributes & + pFindData->uiSearchAttrib) || + ((pFindData->uiSearchAttrib & XF_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 == XF_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 |= XF_IO_FA_DIRECTORY; + } + else + { + if( access( (char *)fileName, (int)(R_OK | W_OK)) == 0) + { + IOmode |= XF_IO_FA_NORMAL; + } + else if( access( (char *)fileName, (int)R_OK ) == 0) + { + IOmode |= XF_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/version5/src/ftkmem.h b/version5/src/ftkmem.h new file mode 100644 index 0000000..b9ad213 --- /dev/null +++ b/version5/src/ftkmem.h @@ -0,0 +1,207 @@ +//------------------------------------------------------------------------------ +// Desc: Contains prototypes for memory 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: ftkmem.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FTKMEM_H +#define FTKMEM_H + +extern "C" +{ +#if defined( FLM_UNIX) + + #ifdef HAVE_CONFIG_H + #include "config.h" + #endif + + #if !defined ( USE_ALT_MEM_MANAGER) + #include + #endif +#else + #include +#endif +} + +#ifdef USE_ALT_MEM_MANAGER + + // Mappings + + #define os_malloc f_alt_malloc + #define os_realloc f_alt_realloc + #define os_free f_alt_free + +#else + + #define os_malloc malloc + #define os_realloc realloc + #define os_free free + +#endif + +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) + +// Picket fence + +#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_DEBUG +RCODE f_allocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr, + FLMBOOL bAllocFromNewOp, + const char * pszFileName, + FLMINT iLineNumber); + +RCODE f_callocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber); + +RCODE f_reallocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber); + +RCODE f_recallocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr, + const char * pszFileName, + FLMINT iLineNumber); + +void f_resetStackInfoImp( // Source: flalloc.cpp + void * pvPtr, + const char * pszFileName, + FLMINT iLineNumber); + +#define f_alloc(uiSize,ppvPtr) \ + f_allocImp(uiSize,(void **)(ppvPtr),FALSE,__FILE__,__LINE__) + +#define f_calloc(uiSize,ppvPtr) \ + f_callocImp(uiSize,(void **)(ppvPtr),__FILE__,__LINE__) + +#define f_realloc(uiSize,ppvPtr) \ + f_reallocImp(uiSize,(void **)(ppvPtr),__FILE__,__LINE__) + +#define f_recalloc(uiSize,ppvPtr) \ + f_recallocImp(uiSize,(void **)(ppvPtr),__FILE__,__LINE__) + +#define f_resetStackInfo(pvPtr) \ + f_resetStackInfoImp(pvPtr,__FILE__,__LINE__) + +#else +RCODE f_allocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr); + +RCODE f_callocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr); + +RCODE f_reallocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr); + +RCODE f_recallocImp( // Source: flalloc.cpp + FLMUINT uiSize, + void ** ppvPtr); + +#define f_alloc(uiSize,ppvPtr) \ + f_allocImp(uiSize,(void **)(ppvPtr)) + +#define f_calloc(uiSize,ppvPtr) \ + f_callocImp(uiSize,(void **)(ppvPtr)) + +#define f_realloc(uiSize,ppvPtr) \ + f_reallocImp(uiSize,(void **)(ppvPtr)) + +#define f_recalloc(uiSize,ppvPtr) \ + f_recallocImp(uiSize,(void **)(ppvPtr)) + +#define f_resetStackInfo(pvPtr) + +#endif + +#define f_free(ppvPtr) f_freeImp( (void **)ppvPtr,FALSE) + +void f_freeImp( // Source: flalloc.cpp + void ** ppvPtr, + FLMBOOL bFreeFromDeleteOp); + +FINLINE FLMUINT f_msize( + void * pvPtr) +{ +#if defined( FLM_UNIX) || defined( USE_ALT_MEM_MANAGER) + 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 +} + +void f_memoryInit( void); // Source: flalloc.cpp + +void f_memoryCleanup( void); // Source: flalloc.cpp + +FLMUINT * memWalkStack( void); + +void logMemLeak( + F_MEM_HDR * pHdr); + +#ifdef FLM_DEBUG + #define f_new new( __FILE__, __LINE__) +#else + #define f_new new +#endif + +#endif // FTKMEM_H diff --git a/version5/src/ftkmisc.cpp b/version5/src/ftkmisc.cpp new file mode 100644 index 0000000..09b98c2 --- /dev/null +++ b/version5/src/ftkmisc.cpp @@ -0,0 +1,455 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" + +// Global data + +FLMUINT gv_uiSerialInitCount = 0; +F_MUTEX gv_hSerialMutex = F_MUTEX_NULL; + +#ifdef FLM_UNIX + F_RandomGenerator gv_SerialRandom; + + #if !(defined( __i386__) && defined( __GNUC__)) + static pthread_mutex_t xflm_atomic_mutex = PTHREAD_MUTEX_INITIALIZER; + #endif +#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 MapErrnoToFlaimErr( + int err, + RCODE defaultRc) +{ + /* Switch on passed in error code value */ + + switch (err) + { + case 0: + return( NE_XFLM_OK); + + case ENOENT: + return( RC_SET( NE_XFLM_IO_PATH_NOT_FOUND)); + + case EACCES: + case EEXIST: + return( RC_SET( NE_XFLM_IO_ACCESS_DENIED)); + + case EINVAL: + return( RC_SET( NE_XFLM_IO_PATH_TOO_LONG)); + + case EIO: + return( RC_SET( NE_XFLM_IO_DISK_FULL)); + + case ENOTDIR: + return( RC_SET( NE_XFLM_IO_DIRECTORY_ERR)); + +#ifdef EBADFD + case EBADFD: + return( RC_SET( NE_XFLM_IO_BAD_FILE_HANDLE)); +#endif + +#ifdef EOF + case EOF: + return( RC_SET( NE_XFLM_IO_END_OF_FILE)); +#endif + + case EMFILE: + return( RC_SET( NE_XFLM_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_XFLM_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 f_createSerialNumber( + FLMBYTE * pszSerialNum) +{ + RCODE rc = NE_XFLM_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) +{ + FLMUINT32 * pTable; + FLMUINT32 ui32Val; + FLMUINT32 ui32Loop; + FLMUINT32 ui32SubLoop; + RCODE rc = NE_XFLM_OK; + + // 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 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( pszBuffer, pszValue); + } +#else + F_UNREFERENCED_PARM( pszKey); +#endif + +Exit: + + if( puiValueLen) + { + *puiValueLen = uiValueLen; + } + + return; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) && !(defined( __i386__) && defined( __GNUC__)) +FLMUINT32 ftkAtomicIncrement( + FLMUINT32 * puiTarget) +{ + FLMUINT32 ui32Ret; + + pthread_mutex_lock( &xflm_atomic_mutex); + ui32Ret = ++(*puiTarget); + pthread_mutex_unlock( &xflm_atomic_mutex); + + return( ui32Ret); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) && !(defined( __i386__) && defined( __GNUC__)) +FLMUINT32 ftkAtomicDecrement( + FLMUINT32 * puiTarget) +{ + FLMUINT32 ui32Ret; + + pthread_mutex_lock( &xflm_atomic_mutex); + ui32Ret = --(*puiTarget); + pthread_mutex_unlock( &xflm_atomic_mutex); + + return( ui32Ret); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) && !(defined( __i386__) && defined( __GNUC__)) +FLMUINT32 ftkAtomicExchange( + FLMUINT32 * puiTarget, + FLMUINT32 uiValue) +{ + FLMUINT32 ui32Ret; + + pthread_mutex_lock( &xflm_atomic_mutex); + ui32Ret = *puiTarget; + *puiTarget = uiValue; + pthread_mutex_unlock( &xflm_atomic_mutex); + + return( ui32Ret); +} +#endif diff --git a/version5/src/ftkpath.cpp b/version5/src/ftkpath.cpp new file mode 100644 index 0000000..4550ff6 --- /dev/null +++ b/version5/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 "flaimsys.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 XFLMAPI 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; + 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 XFLMAPI F_FileSystem::pathReduce( + const char * pszPath, + char * pszDir, + char * pszPathComponent) +{ + RCODE rc = NE_XFLM_OK; + const char * pszFileNameStart; + char szLocalPath[ F_PATH_MAX_SIZE]; + FLMUINT uiLen; + + // Check for valid path pointers + + if( !pszPath || !pszDir) + { + rc = RC_SET( NE_XFLM_INVALID_PARM); + goto Exit; + } + + if ((uiLen = f_strlen( pszPath)) == 0) + { + rc = RC_SET( NE_XFLM_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_XFLM_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 XFLMAPI 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_XFLM_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_XFLM_IO_PATH_TOO_LONG); + } + } + + f_strcpy( pszEnd + 1, pszPathComponent); + } + else + { + f_strcpy( pszPath, pszPathComponent); + } + + return( NE_XFLM_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 XFLMAPI 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_XFLM_OK; +#else + + char szFile[ F_PATH_MAX_SIZE]; + char szDir[ F_PATH_MAX_SIZE]; + char * pszRealPath = NULL; + RCODE rc = NE_XFLM_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_XFLM_PARSING_FILE_NAME); + goto Exit; + } + + if (f_strlen( pszRealPath) >= F_PATH_MAX_SIZE) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI 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 XFLMAPI F_FileSystem::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( gv_pFileSystem->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/version5/src/ftkrand.cpp b/version5/src/ftkrand.cpp new file mode 100644 index 0000000..c12424d --- /dev/null +++ b/version5/src/ftkrand.cpp @@ -0,0 +1,206 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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::randomSetSeed( + FLMINT32 ui32Seed) +{ + register FLMINT32 i32Seed = (FLMINT32)ui32Seed; + + 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::randomLong( void) +{ + +#define M 2147483647 /* PMMLCG modulus == MAX_RANDOM + 1 */ +#define A 48271 /* PMMLCG multiplier */ +#define CHECK 399268537 /* produced by 10000 iterations from seed of 1 */ + + register FLMUINT32 hi; + register FLMUINT32 lo; + register FLMUINT32 ui32Seed = m_i32Seed; /* input is 31-bit number */ + + hi = (ui32Seed >> 16); /* hi = a (high-order 15 bits of x[n-1]) */ + lo = ui32Seed & 0xFFFF; /* lo = b (low-order 16 bits of x[n-1]) */ + lo *= A; /* lo = c * b = d:e (16:16 bits = 32 bits) */ + hi *= A; /* hi = c * a = f:g (15:16 bits = 31 bits) */ + + hi += (lo >> 16) & 0xFFFF; /* hi = f:g + d = h (31 bits) */ + lo &= 0xFFFF; /* lo = e (16 bits) */ + + /* + * Now, the 'longhand' product has been calculated. It is stored in + * hi:lo (31:16 bits) = h:e (31:16 bits). + * + * Now, redistribute the number h:e (31:16 bits) into x:y (16:31 bits) + */ + + lo |= (hi & 0x7FFF) << 16; /* lo = y = (low 15 bits of h spliced into e) */ + hi >>= 15; /* hi = x (high 16 bits of h) */ + lo += hi; /* lo = z = y + x (32 bits) */ + + /* + * At this point, the value has been reduced modulo M to the 32-bit + * value z, stored in lo. Reduce if the high-order bit is set. + */ + + if( lo & 0x80000000L) /* subtract 2**31 - 1 if necessary */ + { + lo &= 0x7FFFFFFF; /* equivalent to lo = lo - 2**31 */ + lo++; /* equivalent to lo = lo + 1 */ + } + + /* we don't need to worry about lo == M, because it can't happen */ + + return( m_i32Seed = lo); +} + +/************************************************************************* +Desc: return a random integer between lo and hi, inclusive. + (where lo and hi are integer arguments). +Example: + The code "RandomChoice( &r, 1, 6) + RandomChoice( &r, 1, 6)" will + simulate the roll of a standard 6-sided die. +Note: The distance (range) between lo and hi must be no greater than + MAX_RANDOM. Normally, RandomChoice computes its answer by taking + a f_randomLong modulo the desired range. If the range is large enough, + aliasing effects would cause some answers to be produced too often. + Therefore, f_randomChoice uses a better but slower algorithm if the + range is >= 1 Meg (2**20). +*************************************************************************/ +FLMINT32 F_RandomGenerator::randomChoice( + FLMINT32 lo, /* lowest allowed return value */ + FLMINT32 hi /* highest allowed return value */ + ) +{ + register FLMINT32 range = hi - lo + 1; + + if( range < (1L << 20)) + { + return( lo + randomLong() % range); + } + else + { + register FLMINT32 mask = 0; + register FLMINT32 x; + + range--; + for( x = range; x > 0; x >>= 1) + { + mask = (mask << 1) | 1; + } + + do + { + x = randomLong() & mask; + } while( x > range); + + return( lo + x); + } +} + + +/************************************************************************* +Desc: Return TRUE a certain percentage of the time +Example: + This code will decimate a population (that is, it will kill 10% of + the "life_force" group): + + for( i=0; iui32Locked = 0; +#ifdef FLM_DEBUG + (*phMutex)->uiThreadId = 0; + (*phMutex)->ui32LockedCount = 0; + (*phMutex)->ui32WaitCount = 0; +#endif + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void f_mutexDestroy( + F_MUTEX * phMutex) +{ + flmAssert( phMutex != NULL); + + if (*phMutex != F_MUTEX_NULL) + { + os_free( *phMutex); + *phMutex = F_MUTEX_NULL; + } +} + +#elif defined( FLM_UNIX) + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE f_mutexCreate( + F_MUTEX * phMutex) +{ + RCODE rc = NE_XFLM_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)os_malloc( + sizeof( pthread_mutex_t))) == F_MUTEX_NULL) + { + rc = RC_SET( NE_XFLM_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 os_malloc up above due + // to the fact that the memory subsystem uses a mutex before itis + // completely ready to go. + + os_free( *phMutex); + *phMutex = F_MUTEX_NULL; + rc = RC_SET( NE_XFLM_COULD_NOT_CREATE_MUTEX); + goto Exit; + } + +Exit: + + if( pMutexAttr) + { + pthread_mutexattr_destroy( pMutexAttr); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void 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 os_malloc up above due + // to the fact that the memory subsystem uses a mutex before it is + // completely ready to go. + + os_free( *phMutex); + *phMutex = F_MUTEX_NULL; + } +} + +/**************************************************************************** +Desc: Initializes a semaphore handle on 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); +} + +/**************************************************************************** +Desc: Frees a semaphore handle on UNIX +****************************************************************************/ +FINLINE void sema_destroy( + sema_t * pSem) +{ + pthread_mutex_destroy( &pSem->lock); + pthread_cond_destroy( &pSem->cond); +} + +/**************************************************************************** +Desc: Waits for a semaphore to be signaled on 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); +} + +/**************************************************************************** +Desc: Waits a specified number of milliseconds for a semaphore + to be signaled on 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); +} + +/**************************************************************************** +Desc: Signals a semaphore on 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); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE f_semCreate( + F_SEM * phSem) +{ + RCODE rc = NE_XFLM_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_XFLM_COULD_NOT_CREATE_SEMAPHORE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void f_semDestroy( + F_SEM * phSem) +{ + flmAssert( phSem != NULL); + + if (*phSem != F_SEM_NULL) + { + sema_destroy( *phSem); + f_free( phSem); + *phSem = F_SEM_NULL; + } +} + +/**************************************************************************** +Desc: Get the lock on a semaphore - p operation +****************************************************************************/ +RCODE f_semWait( + F_SEM hSem, + FLMUINT uiTimeout) +{ + RCODE rc = NE_XFLM_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_XFLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + else + { + if( sema_timedwait( hSem, (unsigned int)uiTimeout)) + { + rc = RC_SET( NE_XFLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + + return( rc); +} +#endif + +#if defined( FLM_WATCOM_NLM) + int gv_DummyFtksem(void) + { + return( 0); + } +#endif diff --git a/version5/src/ftksem.h b/version5/src/ftksem.h new file mode 100644 index 0000000..938bd5c --- /dev/null +++ b/version5/src/ftksem.h @@ -0,0 +1,374 @@ +//------------------------------------------------------------------------------ +// Desc: This file defines the needed prototypes, defines, macros needed +// for FLAIM's Semaphore and Mutex 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.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FTKSEM_H +#define FTKSEM_H + +/***************************************************************************** + Mutexes +*****************************************************************************/ + +#if defined( FLM_WIN) + + typedef struct + { + FLMUINT32 ui32Locked; +#ifdef FLM_DEBUG + FLMUINT uiThreadId; + FLMUINT32 ui32LockedCount; + FLMUINT32 ui32WaitCount; +#endif + } F_INTERLOCK; + + typedef F_INTERLOCK * F_MUTEX; + typedef F_INTERLOCK ** F_MUTEX_p; + #define F_MUTEX_NULL NULL + +#elif defined( FLM_NLM) + + // WARNING! The following come from portable.h + + #ifndef UINT + #define UINT unsigned int + #endif + + // WARNING! The following come from mpkapis.h + + typedef void * MUTEX; + typedef void * SEMAPHORE; + typedef unsigned long ERROR; + +extern "C" +{ + SEMAPHORE kSemaphoreAlloc(BYTE *pSemaName, UINT SemaCount); + ERROR kSemaphoreFree(SEMAPHORE SemaHandle); + ERROR kSemaphoreWait(SEMAPHORE SemaHandle); + ERROR kSemaphoreTimedWait(SEMAPHORE SemaHandle, UINT MilliSecondTimeOut); + ERROR kSemaphoreSignal(SEMAPHORE SemaHandle); + UINT kSemaphoreExamineCount(SEMAPHORE SemaHandle); + + MUTEX kMutexAlloc(BYTE *MutexName); + ERROR kMutexFree(MUTEX MutexHandle); + ERROR kMutexLock(MUTEX MutexHandle); + ERROR kMutexUnlock(MUTEX MutexHandle); +} + + typedef MUTEX F_MUTEX; + typedef MUTEX * F_MUTEX_p; + #define F_MUTEX_NULL 0 + +#elif defined( FLM_UNIX) + + #include + + typedef pthread_mutex_t * F_MUTEX; + typedef F_MUTEX * F_MUTEX_p; + #define F_MUTEX_NULL NULL +#else + + #error Unsupported platform +#endif + +/* prototypes for the mutex functions **********************************/ + +#ifndef FLM_NLM + RCODE f_mutexCreate( + F_MUTEX * phMutex); + + void f_mutexDestroy( + F_MUTEX * phMutex); +#endif + +#if defined( FLM_NLM) + + FINLINE RCODE f_mutexCreate( + F_MUTEX * phMutex) + { + if( (*phMutex = (F_MUTEX)kMutexAlloc( (BYTE *)"NOVDB")) == F_MUTEX_NULL) + { + return RC_SET( NE_XFLM_MEM); + } + + return NE_XFLM_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) + { + } + +#elif defined( FLM_WIN) + + #ifndef __STDDEF_H + #include + #endif + + FINLINE void f_mutexLock( + F_MUTEX hMutex) + { + while( ftkAtomicExchange( + &(((F_INTERLOCK *)hMutex)->ui32Locked), 1) != 0) + { +#ifdef FLM_DEBUG + ftkAtomicIncrement( &(((F_INTERLOCK *)hMutex)->ui32WaitCount)); +#endif + Sleep( 0); + } + +#ifdef FLM_DEBUG + flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == 0); + ((F_INTERLOCK *)hMutex)->uiThreadId = _threadid; + ftkAtomicIncrement( &(((F_INTERLOCK *)hMutex)->ui32LockedCount)); +#endif + } + + FINLINE void f_mutexUnlock( + F_MUTEX hMutex) + { + flmAssert( ((F_INTERLOCK *)hMutex)->ui32Locked == 1); +#ifdef FLM_DEBUG + flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == _threadid); + ((F_INTERLOCK *)hMutex)->uiThreadId = 0; +#endif + ftkAtomicExchange( &(((F_INTERLOCK *)hMutex)->ui32Locked), 0); + } + + FINLINE void f_assertMutexLocked( + F_MUTEX hMutex) + { +#ifdef FLM_DEBUG + flmAssert( ((F_INTERLOCK *)hMutex)->ui32Locked == 1); + flmAssert( ((F_INTERLOCK *)hMutex)->uiThreadId == _threadid); +#else + F_UNREFERENCED_PARM( hMutex); +#endif + } + +#elif defined( FLM_UNIX) + + 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) + { + } + +#endif + +/***************************************************************************** + Semaphores +*****************************************************************************/ + +#define F_SEM_WAITFOREVER (0xFFFFFFFF) + +#if defined( FLM_WIN) + typedef HANDLE F_SEM; + typedef HANDLE * F_SEM_p; + #define F_SEM_NULL NULL + +#elif defined( FLM_NLM) + typedef SEMAPHORE F_SEM; + typedef SEMAPHORE * F_SEM_p; + #define F_SEM_NULL 0 + +#elif defined( FLM_UNIX) + + 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 + + int sema_signal( + sema_t * sem); + +#else + #error Unsupported platform +#endif + +/* prototypes for the semaphore functions **********************************/ + +#if defined( FLM_NLM) + + FINLINE RCODE f_semCreate( + F_SEM * phSem) + { + if( (*phSem = (F_SEM)kSemaphoreAlloc( (BYTE *)"NOVDB", 0)) == F_SEM_NULL) + { + return RC_SET( NE_XFLM_MEM); + } + + return NE_XFLM_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_XFLM_OK; + + if( uiTimeout == F_SEM_WAITFOREVER) + { + if( kSemaphoreWait( (SEMAPHORE)hSem) != 0) + { + rc = RC_SET( NE_XFLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + else + { + if( kSemaphoreTimedWait( (SEMAPHORE)hSem, (UINT)uiTimeout) != 0) + { + rc = RC_SET( NE_XFLM_ERROR_WAITING_ON_SEMPAHORE); + } + } + + return( rc); + } + + FINLINE void f_semSignal( + F_SEM hSem) + { + (void)kSemaphoreSignal( (SEMAPHORE)hSem); + } + +#elif defined( FLM_WIN) + + FINLINE RCODE f_semCreate( + F_SEM * phSem) + { + if( (*phSem = CreateSemaphore( (LPSECURITY_ATTRIBUTES)NULL, + 0, 10000, NULL )) == NULL) + { + return( RC_SET( NE_XFLM_COULD_NOT_CREATE_SEMAPHORE)); + } + + return NE_XFLM_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_XFLM_OK); + } + else + { + return( RC_SET( NE_XFLM_ERROR_WAITING_ON_SEMPAHORE)); + } + } + + FINLINE void f_semSignal( + F_SEM hSem) + { + (void)ReleaseSemaphore( hSem, 1, NULL); + } + +#elif defined( FLM_UNIX) + + 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); + } + +#else + #error Platform undefined +#endif + +#endif // #ifndef FTKSEM_H diff --git a/version5/src/ftkthrd.cpp b/version5/src/ftkthrd.cpp new file mode 100644 index 0000000..6dd9dd3 --- /dev/null +++ b/version5/src/ftkthrd.cpp @@ -0,0 +1,1035 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" + +#if defined( FLM_UNIX) + #include +#endif + +#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. +****************************************************************************/ +FLMUINT32 F_Thread::AddRef( + FLMBOOL bMutexLocked) +{ + FLMUINT32 ui32RefCnt; + + if( !bMutexLocked) + { + f_mutexLock( m_hMutex); + } + + ui32RefCnt = ++m_ui32RefCnt; + + if( !bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( ui32RefCnt); +} + +/**************************************************************************** +Desc: Removes a reference to this object. +****************************************************************************/ +FLMUINT32 F_Thread::Release( + FLMBOOL bMutexLocked) +{ + FLMUINT32 ui32RefCnt; + + if( !bMutexLocked && m_hMutex != F_MUTEX_NULL) + { + f_mutexLock( m_hMutex); + } + + flmAssert( m_ui32RefCnt > 0); + ui32RefCnt = --m_ui32RefCnt; + + if( !bMutexLocked && m_hMutex != F_MUTEX_NULL) + { + f_mutexUnlock( m_hMutex); + } + + if( !ui32RefCnt) + { + delete this; + } + + return( ui32RefCnt); +} + +/**************************************************************************** +Desc: Performs various setup work and starts a new thread +****************************************************************************/ +RCODE F_Thread::startThread( + F_THREAD_FUNC fnThread, + const char * pszThreadName, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + void * pvParm1, + void * pvParm2, + FLMUINT uiStackSize) +{ + RCODE rc = NE_XFLM_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( 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_XFlmSysData.pThreadMgr->m_hMutex); + bManagerMutexLocked = TRUE; + + // Increment the active thread count + + gv_XFlmSysData.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_XFlmSysData.pThreadMgr->m_pThreadList) + { + gv_XFlmSysData.pThreadMgr->m_pThreadList->m_pPrev = this; + } + + m_pNext = gv_XFlmSysData.pThreadMgr->m_pThreadList; + gv_XFlmSysData.pThreadMgr->m_pThreadList = this; + + // Increment the reference count of the thread object now + // that it is linked into the thread manager's list. + + m_ui32RefCnt++; + + // Start the thread + +#ifdef FLM_WIN + if( _beginthreadex( + NULL, (unsigned int)m_uiStackSize, threadStub, + (void *)this, 0, &uiThreadId) == 0) // 0 indicates a failure + { + rc = RC_SET( NE_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFlmSysData.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_XFlmSysData.pThreadMgr->unlinkThread( this, bManagerMutexLocked); + + // Reset the thread object back to its initial state + + cleanupThread(); + } + + if( bManagerMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.pThreadMgr->m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Stop a running thread +****************************************************************************/ +void 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_XFlmSysData.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_XFlmSysData.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_XFlmSysData.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 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 = FLM_DEFAULT_THREAD_GROUP; + m_uiAppId = 0; + m_uiStartTime = 0; + m_exitRc = NE_XFLM_OK; +} + +/**************************************************************************** +Desc: Set the thread's status +****************************************************************************/ +void 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 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 F_Thread::setThreadStatus( + FlmThreadStatus eGenericStatus) +{ + const char * pszStatus = NULL; + + switch( eGenericStatus) + { + 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: Allocates resources needed by the thread manager +****************************************************************************/ +RCODE F_ThreadMgr::setupThreadMgr( void) +{ + RCODE rc = NE_XFLM_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( + F_Thread * pThread, + FLMBOOL bMutexIsLocked) +{ + // 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 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 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 F_ThreadMgr::getThreadInfo( + F_Pool * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads) +{ + RCODE rc = NE_XFLM_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( 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( 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 F_ThreadMgr::findThread( + F_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + FLMBOOL bOkToFindMe) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + F_Thread * pCurThread; + + *ppThread = NULL; + + f_mutexLock( m_hMutex); + bMutexLocked = TRUE; + + if( m_uiNumThreads == 0) + { + rc = RC_SET( NE_XFLM_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( TRUE); + *ppThread = pCurThread; + f_mutexUnlock( pCurThread->m_hMutex); + goto Exit; + } + } + + f_mutexUnlock( pCurThread->m_hMutex); + pCurThread = pCurThread->m_pNext; + } + + rc = RC_SET( NE_XFLM_NOT_FOUND); + +Exit: + + if( bMutexLocked) + { + f_mutexUnlock( m_hMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Finds a thread based on user-specified identifiers +****************************************************************************/ +RCODE F_ThreadMgr::getNextGroupThread( + F_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT * puiThreadId) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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 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 f_threadCreate( + F_Thread ** ppThread, + F_THREAD_FUNC fnThread, + const char * pszThreadName, + FLMUINT uiThreadGroup, + FLMUINT uiAppId, + void * pvParm1, + void * pvParm2, + FLMUINT uiStackSize) +{ + RCODE rc = NE_XFLM_OK; + F_Thread * pThread = NULL; + + if( ppThread) + { + *ppThread = NULL; + } + + if( (pThread = f_new F_Thread) == NULL) + { + rc = RC_SET( NE_XFLM_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 f_threadDestroy( + F_Thread ** ppThread) +{ + // Shutdown the thread + + if( *ppThread != NULL) + { + (*ppThread)->stopThread(); + (*ppThread)->Release(); + *ppThread = NULL; + } +} + +/**************************************************************************** +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); + } +} diff --git a/version5/src/ftkthrd.h b/version5/src/ftkthrd.h new file mode 100644 index 0000000..2ad7e81 --- /dev/null +++ b/version5/src/ftkthrd.h @@ -0,0 +1,366 @@ +//------------------------------------------------------------------------------ +// Desc: This file defines the needed prototypes, defines, macros needed +// for FLAIM's Thread 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: ftkthrd.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FTKTHRD_H +#define FTKTHRD_H + +#if defined( FLM_UNIX) + #include + #include // defines _POSIX_THREADS + #ifndef _POSIX_THREADS + #define _POSIX_THREADS + #endif +#endif + +#ifdef FLM_WIN + #ifndef __STDDEF_H + #include /* _threadid */ + #endif + #define f_threadId() (FLMUINT)_threadid +#endif + +#ifdef FLM_NLM + FINLINE FLMUINT f_threadId(void) + { + pthread_t thrd = pthread_self(); + return( (FLMUINT) thrd); + } +#endif + +#ifdef FLM_UNIX + FINLINE FLMUINT f_threadId(void) + { + #ifdef _POSIX_THREADS + pthread_t thrd = pthread_self(); + #endif + + #if defined( SCO) + return( (FLMUINT) thrd.field2); + #else + return( (FLMUINT) thrd); + #endif + } +#endif + +#define F_THREAD_MIN_STACK_SIZE (16 * 1024) +#define F_THREAD_DEFAULT_STACK_SIZE (16 * 1024) + +// Forward declarations + +class F_Thread; +class F_ThreadMgr; + +// Typedefs + +typedef enum eFlmThreadStatusType +{ + 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 +} FlmThreadStatus; + +typedef struct +{ + FLMUINT uiThreadId; + FLMUINT uiThreadGroup; + FLMUINT uiAppId; + FLMUINT uiStartTime; + char * pszThreadName; + char * pszThreadStatus; +} F_THREAD_INFO; + +// Thread types + +#define FLM_DEFAULT_THREAD_GROUP 1 +#define FLM_CHECKPOINT_THREAD_GROUP 2 +#define FLM_BACKGROUND_INDEXING_THREAD_GROUP 3 +#define FLM_DB_THREAD_GROUP 4 + +// NOTE: SMI defines thread groups starting at 0x8F000000 + +/*============================================================================ +Class: F_ThreadMgr +Desc: Class for managing a set of threads +============================================================================*/ +class F_ThreadMgr : public XF_RefCount, public XF_Base +{ +public: + + // Constructors + + F_ThreadMgr() + { + m_hMutex = F_MUTEX_NULL; + m_pThreadList = NULL; + m_uiNumThreads = 0; + } + + // Destructor + + ~F_ThreadMgr(); + + // Setup + + RCODE setupThreadMgr( void); + + // Shutdown + + void shutdownThreadGroup( + FLMUINT uiThreadGroup); + + void setThreadShutdownFlag( + FLMUINT uiThreadId); + + // Search + + RCODE findThread( + F_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT uiAppId = 0, + FLMBOOL bOkToFindMe = TRUE); + + RCODE getNextGroupThread( + F_Thread ** ppThread, + FLMUINT uiThreadGroup, + FLMUINT * puiThreadId); + + // Statistics + + RCODE getThreadInfo( + F_Pool * pPool, + F_THREAD_INFO ** ppThreadInfo, + FLMUINT * puiNumThreads); + + FLMUINT getThreadGroupCount( + FLMUINT uiThreadGroup); + + inline void lockMutex( void) + { + f_mutexLock( m_hMutex); + } + + inline void unlockMutex( void) + { + f_mutexUnlock( m_hMutex); + } + + void unlinkThread( + F_Thread * pThread, + FLMBOOL bMutexLocked); + +private: + + F_MUTEX m_hMutex; + F_Thread * m_pThreadList; + FLMUINT m_uiNumThreads; + +friend class F_Thread; +}; + +// Thread function prototype + +typedef RCODE (*F_THREAD_FUNC)(F_Thread *); + +/*============================================================================ +Class: F_Thread +Desc: Class for creating and managing a thread +============================================================================*/ +class F_Thread : public XF_RefCount, public XF_Base +{ +public: + + // Constructors + + F_Thread() + { + m_hMutex = F_MUTEX_NULL; + m_pszThreadName = NULL; + m_pszThreadStatus = NULL; + m_uiStatusBufLen = 0; + m_pPrev = NULL; + m_pNext = NULL; + cleanupThread(); + } + + // Destructor + + ~F_Thread() + { + stopThread(); + cleanupThread(); + } + + // Reference counting methods. + + FLMUINT32 AddRef( + FLMBOOL bMutexLocked); + + FLMUINT32 Release( + FLMBOOL bMutexLocked); + + // Override the AddRef and Release from XF_RefCount + + FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( AddRef( FALSE)); + } + + FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( Release( FALSE)); + } + + // Other methods + + RCODE startThread( + F_THREAD_FUNC fnThread, + const char * pszThreadName = NULL, + FLMUINT uiThreadGroup = FLM_DEFAULT_THREAD_GROUP, + FLMUINT uiAppId = 0, + void * pvParm1 = NULL, + void * pvParm2 = NULL, + FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE); + + void stopThread( void); + + FINLINE FLMUINT getThreadId( void) + { + return( m_uiThreadId); + } + + FINLINE FLMBOOL getShutdownFlag( void) + { + return( m_bShutdown); + } + + FINLINE RCODE getExitCode( void) + { + return( m_exitRc); + } + + FINLINE void * getParm1( void) + { + return( m_pvParm1); + } + + FINLINE void setParm1( + void * pvParm) + { + m_pvParm1 = pvParm; + } + + FINLINE void * getParm2( void) + { + return( m_pvParm2); + } + + FINLINE void setParm2( + void * pvParm) + { + m_pvParm2 = pvParm; + } + + FINLINE void setShutdownFlag( void) + { + m_bShutdown = TRUE; + } + + FINLINE FLMBOOL isThreadRunning( void) + { + return( m_bRunning); + } + + void setThreadStatusStr( + const char * pszStatus); + + void setThreadStatus( + const char * pszBuffer, ...); + + void setThreadStatus( + FlmThreadStatus eGenericStatus); + + FINLINE void setThreadAppId( + FLMUINT uiAppId) + { + f_mutexLock( m_hMutex); + m_uiAppId = uiAppId; + f_mutexUnlock( m_hMutex); + } + + FINLINE FLMUINT getThreadAppId( void) + { + return( m_uiAppId); + } + + FINLINE FLMUINT getThreadGroup( void) + { + return( m_uiThreadGroup); + } + + void 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; +}; + +// Misc. functions + +RCODE f_threadCreate( // Source: ftkthrd.cpp + F_Thread ** ppThread, + F_THREAD_FUNC fnThread, + const char * pszThreadName = NULL, + FLMUINT uiThreadGroup = FLM_DEFAULT_THREAD_GROUP, + FLMUINT uiAppId = 0, + void * pvParm1 = NULL, + void * pvParm2 = NULL, + FLMUINT uiStackSize = F_THREAD_DEFAULT_STACK_SIZE); + +void f_threadDestroy( // Source: ftkthrd.cpp + F_Thread ** ppThread); + +#endif // #ifndef FTKTHRD_H diff --git a/version5/src/ftktime.cpp b/version5/src/ftktime.cpp new file mode 100644 index 0000000..85505eb --- /dev/null +++ b/version5/src/ftktime.cpp @@ -0,0 +1,335 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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] = { /* days of the months */ + {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] = { + /* current total of the days in the year by mon */ + { 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, // [in] - time stamp of date + FLMUINT * puiSeconds) // [out] - seconds of time stamp +{ + 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/version5/src/funicode.cpp b/version5/src/funicode.cpp new file mode 100644 index 0000000..eb2289c --- /dev/null +++ b/version5/src/funicode.cpp @@ -0,0 +1,2693 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the Unicode conversion routines +// +// 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: funicode.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#define DEF_FLM_UNI_GLOBALS +#include "flaimsys.h" + +// Local constants + +#define UTOWP60_ENTRIES 1502 + +// Mapping table + +/**************************************************************************** +Desc: UNICODE to WP6 character mapping table +Notes: This table is used to convert a subset of Unicode characters to + their WordPerfect equivalents so that the WP collation routines + can be used for indexing. This contains characters that can be + mapped 1:1 from Unicode->WP and from WP->Unicode. There is + no ambiguity and there are no character expansions or + contractions. +****************************************************************************/ +FLMUINT16 WP_UTOWP60[ UTOWP60_ENTRIES][2] = +{ + { 0x00A1, 0x0407 }, // 7 , 4 + { 0x00A2, 0x0413 }, // 19 , 4 + { 0x00A3, 0x040b }, // 11 , 4 + { 0x00A4, 0x0418 }, // 24 , 4 + { 0x00A5, 0x040c }, // 12 , 4 + { 0x00A7, 0x0406 }, // 6 , 4 + { 0x00A9, 0x0417 }, // 23 , 4 + { 0x00AA, 0x040f }, // 15 , 4 + { 0x00AB, 0x0409 }, // 9 , 4 + { 0x00AC, 0x0614 }, // 20 , 6 + { 0x00AE, 0x0416 }, // 22 , 4 + { 0x00B0, 0x0624 }, // 36 , 6 + { 0x00B1, 0x0601 }, // 1 , 6 + { 0x00B2, 0x0414 }, // 20 , 4 + { 0x00B3, 0x041a }, // 26 , 4 + { 0x00B5, 0x0625 }, // 37 , 6 + { 0x00B6, 0x0405 }, // 5 , 4 + { 0x00B7, 0x0101 }, // 101, 1 + { 0x00B9, 0x044e }, // 78 , 4 + { 0x00BA, 0x0410 }, // 16 , 4 + { 0x00BB, 0x040a }, // 10 , 4 + { 0x00BC, 0x0412 }, // 18 , 4 + { 0x00BD, 0x0411 }, // 17 , 4 + { 0x00BE, 0x0419 }, // 25 , 4 + { 0x00BF, 0x0408 }, // 8 , 4 + { 0x00C0, 0x0120 }, // 32 , 1 + { 0x00C1, 0x011a }, // 26 , 1 + { 0x00C2, 0x011c }, // 28 , 1 + { 0x00C3, 0x014c }, // 76 , 1 + { 0x00C4, 0x011e }, // 30 , 1 + { 0x00C5, 0x0122 }, // 34 , 1 + { 0x00C6, 0x0124 }, // 36 , 1 + { 0x00C7, 0x0126 }, // 38 , 1 + { 0x00C8, 0x012e }, // 46 , 1 + { 0x00C9, 0x0128 }, // 40 , 1 + { 0x00CA, 0x012a }, // 42 , 1 + { 0x00CB, 0x012c }, // 44 , 1 + { 0x00CC, 0x0136 }, // 54 , 1 + { 0x00CD, 0x0130 }, // 48 , 1 + { 0x00CE, 0x0132 }, // 50 , 1 + { 0x00CF, 0x0134 }, // 52 , 1 + { 0x00D0, 0x0156 }, // 86 , 1 + { 0x00D1, 0x0138 }, // 56 , 1 + { 0x00D2, 0x0140 }, // 64 , 1 + { 0x00D3, 0x013a }, // 58 , 1 + { 0x00D4, 0x013c }, // 60 , 1 + { 0x00D5, 0x0152 }, // 82 , 1 + { 0x00D6, 0x013e }, // 62 , 1 + { 0x00D7, 0x0627 }, // 39 , 6 + { 0x00D8, 0x0150 }, // 80 , 1 + { 0x00D9, 0x0148 }, // 72 , 1 + { 0x00DA, 0x0142 }, // 66 , 1 + { 0x00DB, 0x0144 }, // 68 , 1 + { 0x00DC, 0x0146 }, // 70 , 1 + { 0x00DD, 0x0154 }, // 84 , 1 + { 0x00DE, 0x0158 }, // 88 , 1 + { 0x00DF, 0x0117 }, // 23 , 1 + { 0x00E0, 0x0121 }, // 33 , 1 + { 0x00E1, 0x011b }, // 27 , 1 + { 0x00E2, 0x011d }, // 29 , 1 + { 0x00E3, 0x014d }, // 77 , 1 + { 0x00E4, 0x011f }, // 31 , 1 + { 0x00E5, 0x0123 }, // 35 , 1 + { 0x00E6, 0x0125 }, // 37 , 1 + { 0x00E7, 0x0127 }, // 39 , 1 + { 0x00E8, 0x012f }, // 47 , 1 + { 0x00E9, 0x0129 }, // 41 , 1 + { 0x00EA, 0x012b }, // 43 , 1 + { 0x00EB, 0x012d }, // 45 , 1 + { 0x00EC, 0x0137 }, // 55 , 1 + { 0x00ED, 0x0131 }, // 49 , 1 + { 0x00EE, 0x0133 }, // 51 , 1 + { 0x00EF, 0x0135 }, // 53 , 1 + { 0x00F0, 0x0157 }, // 87 , 1 + { 0x00F1, 0x0139 }, // 57 , 1 + { 0x00F2, 0x0141 }, // 65 , 1 + { 0x00F3, 0x013b }, // 59 , 1 + { 0x00F4, 0x013d }, // 61 , 1 + { 0x00F5, 0x0153 }, // 83 , 1 + { 0x00F6, 0x013f }, // 63 , 1 + { 0x00F7, 0x0608 }, // 8 , 6 + { 0x00F8, 0x0151 }, // 81 , 1 + { 0x00F9, 0x0149 }, // 73 , 1 + { 0x00FA, 0x0143 }, // 67 , 1 + { 0x00FB, 0x0145 }, // 69 , 1 + { 0x00FC, 0x0147 }, // 71 , 1 + { 0x00FD, 0x0155 }, // 85 , 1 + { 0x00FE, 0x0159 }, // 89 , 1 + { 0x00FF, 0x014b }, // 75 , 1 + { 0x0100, 0x015c }, // 92 , 1 + { 0x0101, 0x015d }, // 93 , 1 + { 0x0102, 0x015a }, // 90 , 1 + { 0x0103, 0x015b }, // 91 , 1 + { 0x0104, 0x015e }, // 94 , 1 + { 0x0105, 0x015f }, // 95 , 1 + { 0x0106, 0x0160 }, // 96 , 1 + { 0x0107, 0x0161 }, // 97 , 1 + { 0x0108, 0x0164 }, // 100, 1 + { 0x0109, 0x0165 }, // 101, 1 + { 0x010A, 0x0166 }, // 102, 1 + { 0x010B, 0x0167 }, // 103, 1 + { 0x010C, 0x0162 }, // 98 , 1 + { 0x010D, 0x0163 }, // 99 , 1 + { 0x010E, 0x0168 }, // 104, 1 + { 0x010F, 0x0169 }, // 105, 1 + { 0x0110, 0x014e }, // 78 , 1 + { 0x0111, 0x014f }, // 79 , 1 + { 0x0112, 0x016e }, // 110, 1 + { 0x0113, 0x016f }, // 111, 1 + { 0x0114, 0x01ea }, // 234, 1 + { 0x0115, 0x01eb }, // 235, 1 + { 0x0116, 0x016c }, // 108, 1 + { 0x0117, 0x016d }, // 109, 1 + { 0x0118, 0x0170 }, // 112, 1 + { 0x0119, 0x0171 }, // 113, 1 + { 0x011A, 0x016a }, // 106, 1 + { 0x011B, 0x016b }, // 107, 1 + { 0x011C, 0x017a }, // 122, 1 + { 0x011D, 0x017b }, // 123, 1 + { 0x011E, 0x0174 }, // 116, 1 + { 0x011F, 0x0175 }, // 117, 1 + { 0x0120, 0x017c }, // 124, 1 + { 0x0121, 0x017d }, // 125, 1 + { 0x0122, 0x0178 }, // 120, 1 + { 0x0123, 0x0179 }, // 121, 1 + { 0x0124, 0x017e }, // 126, 1 + { 0x0125, 0x017f }, // 127, 1 + { 0x0126, 0x0180 }, // 128, 1 + { 0x0127, 0x0181 }, // 129, 1 + { 0x0128, 0x0188 }, // 136, 1 + { 0x0129, 0x0189 }, // 137, 1 + { 0x012A, 0x0184 }, // 132, 1 + { 0x012B, 0x0185 }, // 133, 1 + { 0x012C, 0x01ec }, // 236, 1 + { 0x012D, 0x01ed }, // 237, 1 + { 0x012E, 0x0186 }, // 134, 1 + { 0x012F, 0x0187 }, // 135, 1 + { 0x0130, 0x0182 }, // 130, 1 + { 0x0131, 0x01ef }, // 239, 1 + { 0x0132, 0x018a }, // 138, 1 + { 0x0133, 0x018b }, // 139, 1 + { 0x0134, 0x018c }, // 140, 1 + { 0x0135, 0x018d }, // 141, 1 + { 0x0136, 0x018e }, // 142, 1 + { 0x0137, 0x018f }, // 143, 1 + { 0x0138, 0x0118 }, // 24 , 1 + { 0x0139, 0x0190 }, // 144, 1 + { 0x013A, 0x0191 }, // 145, 1 + { 0x013B, 0x0194 }, // 148, 1 + { 0x013C, 0x0195 }, // 149, 1 + { 0x013D, 0x0192 }, // 146, 1 + { 0x013E, 0x0193 }, // 147, 1 + { 0x013F, 0x0196 }, // 150, 1 + { 0x0140, 0x0197 }, // 151, 1 + { 0x0141, 0x0198 }, // 152, 1 + { 0x0142, 0x0199 }, // 153, 1 + { 0x0143, 0x019a }, // 154, 1 + { 0x0144, 0x019b }, // 155, 1 + { 0x0145, 0x01a0 }, // 160, 1 + { 0x0146, 0x01a1 }, // 161, 1 + { 0x0147, 0x019e }, // 158, 1 + { 0x0148, 0x019f }, // 159, 1 + { 0x0149, 0x019d }, // 157, 1 + { 0x014A, 0x01d2 }, // 210, 1 + { 0x014B, 0x01d3 }, // 211, 1 + { 0x014C, 0x01a4 }, // 164, 1 + { 0x014D, 0x01a5 }, // 165, 1 + { 0x014E, 0x01f0 }, // 240, 1 + { 0x014F, 0x01f1 }, // 241, 1 + { 0x0150, 0x01a2 }, // 162, 1 + { 0x0151, 0x01a3 }, // 163, 1 + { 0x0152, 0x01a6 }, // 166, 1 + { 0x0153, 0x01a7 }, // 167, 1 + { 0x0154, 0x01a8 }, // 168, 1 + { 0x0155, 0x01a9 }, // 169, 1 + { 0x0156, 0x01ac }, // 172, 1 + { 0x0157, 0x01ad }, // 173, 1 + { 0x0158, 0x01aa }, // 170, 1 + { 0x0159, 0x01ab }, // 171, 1 + { 0x015A, 0x01ae }, // 174, 1 + { 0x015B, 0x01af }, // 175, 1 + { 0x015C, 0x01b4 }, // 180, 1 + { 0x015D, 0x01b5 }, // 181, 1 + { 0x015E, 0x01b2 }, // 178, 1 + { 0x015F, 0x01b3 }, // 179, 1 + { 0x0160, 0x01b0 }, // 176, 1 + { 0x0161, 0x01b1 }, // 177, 1 + { 0x0162, 0x01b8 }, // 184, 1 + { 0x0163, 0x01b9 }, // 185, 1 + { 0x0164, 0x01b6 }, // 182, 1 + { 0x0165, 0x01b7 }, // 183, 1 + { 0x0166, 0x01ba }, // 186, 1 + { 0x0167, 0x01bb }, // 187, 1 + { 0x0168, 0x01c6 }, // 198, 1 + { 0x0169, 0x01c7 }, // 199, 1 + { 0x016A, 0x01c0 }, // 192, 1 + { 0x016B, 0x01c1 }, // 193, 1 + { 0x016C, 0x01bc }, // 188, 1 + { 0x016D, 0x01bd }, // 189, 1 + { 0x016E, 0x01c4 }, // 196, 1 + { 0x016F, 0x01c5 }, // 197, 1 + { 0x0170, 0x01be }, // 190, 1 + { 0x0171, 0x01bf }, // 191, 1 + { 0x0172, 0x01c2 }, // 194, 1 + { 0x0173, 0x01c3 }, // 195, 1 + { 0x0174, 0x01c8 }, // 200, 1 + { 0x0175, 0x01c9 }, // 201, 1 + { 0x0176, 0x01ca }, // 202, 1 + { 0x0177, 0x01cb }, // 203, 1 + { 0x0178, 0x014a }, // 74 , 1 + { 0x0179, 0x01cc }, // 204, 1 + { 0x017A, 0x01cd }, // 205, 1 + { 0x017B, 0x01d0 }, // 208, 1 + { 0x017C, 0x01d1 }, // 209, 1 + { 0x017D, 0x01ce }, // 206, 1 + { 0x017E, 0x01cf }, // 207, 1 + { 0x0192, 0x040e }, // 14 , 4 + { 0x0194, 0x0a7c }, // 124, 10 + { 0x01A0, 0x01e6 }, // 230, 1 + { 0x01A1, 0x01e7 }, // 231, 1 + { 0x01AF, 0x01e8 }, // 232, 1 + { 0x01B0, 0x01e9 }, // 233, 1 + { 0x01C0, 0x0605 }, // 5 , 6 + { 0x0250, 0x0237 }, // 55 , 2 + { 0x0251, 0x0238 }, // 56 , 2 + { 0x0252, 0x0239 }, // 57 , 2 + { 0x0253, 0x023a }, // 58 , 2 + { 0x0254, 0x023c }, // 60 , 2 + { 0x0255, 0x023d }, // 61 , 2 + { 0x0256, 0x023f }, // 63 , 2 + { 0x0257, 0x0240 }, // 64 , 2 + { 0x0258, 0x0241 }, // 65 , 2 + { 0x0259, 0x0242 }, // 66 , 2 + { 0x025A, 0x0243 }, // 67 , 2 + { 0x025B, 0x0244 }, // 68 , 2 + { 0x025C, 0x0245 }, // 69 , 2 + { 0x025D, 0x0246 }, // 70 , 2 + { 0x025E, 0x0248 }, // 72 , 2 + { 0x025F, 0x0249 }, // 73 , 2 + { 0x0260, 0x024c }, // 76 , 2 + { 0x0261, 0x024b }, // 75 , 2 + { 0x0262, 0x024d }, // 77 , 2 + { 0x0263, 0x024f }, // 79 , 2 + { 0x0264, 0x0250 }, // 80 , 2 + { 0x0265, 0x0251 }, // 81 , 2 + { 0x0266, 0x0252 }, // 82 , 2 + { 0x0267, 0x0253 }, // 83 , 2 + { 0x0268, 0x0255 }, // 85 , 2 + { 0x0269, 0x0257 }, // 87 , 2 + { 0x026A, 0x0256 }, // 86 , 2 + { 0x026B, 0x025a }, // 90 , 2 + { 0x026C, 0x025b }, // 91 , 2 + { 0x026D, 0x025c }, // 92 , 2 + { 0x026E, 0x025e }, // 94 , 2 + { 0x026F, 0x0260 }, // 96 , 2 + { 0x0270, 0x0261 }, // 97 , 2 + { 0x0271, 0x0262 }, // 98 , 2 + { 0x0272, 0x0263 }, // 99 , 2 + { 0x0273, 0x0264 }, // 100, 2 + { 0x0274, 0x0265 }, // 101, 2 + { 0x0275, 0x0279 }, // 121, 2 + { 0x0276, 0x0266 }, // 102, 2 + { 0x0277, 0x0267 }, // 103, 2 + { 0x0278, 0x024a }, // 74 , 2 + { 0x0279, 0x0269 }, // 105, 2 + { 0x027A, 0x026a }, // 106, 2 + { 0x027B, 0x026b }, // 107, 2 + { 0x027C, 0x026c }, // 108, 2 + { 0x027D, 0x026d }, // 109, 2 + { 0x027E, 0x026e }, // 110, 2 + { 0x027F, 0x026f }, // 111, 2 + { 0x0280, 0x0270 }, // 112, 2 + { 0x0281, 0x0271 }, // 113, 2 + { 0x0282, 0x0272 }, // 114, 2 + { 0x0283, 0x0273 }, // 115, 2 + { 0x0284, 0x0274 }, // 116, 2 + { 0x0285, 0x0275 }, // 117, 2 + { 0x0286, 0x0276 }, // 118, 2 + { 0x0287, 0x0277 }, // 119, 2 + { 0x0288, 0x0278 }, // 120, 2 + { 0x0289, 0x027a }, // 122, 2 + { 0x028A, 0x027b }, // 123, 2 + { 0x028B, 0x027d }, // 125, 2 + { 0x028C, 0x027c }, // 124, 2 + { 0x028D, 0x027e }, // 126, 2 + { 0x028E, 0x025f }, // 95 , 2 + { 0x028F, 0x0280 }, // 128, 2 + { 0x0290, 0x0281 }, // 129, 2 + { 0x0291, 0x0282 }, // 130, 2 + { 0x0292, 0x0283 }, // 131, 2 + { 0x0293, 0x0284 }, // 132, 2 + { 0x0294, 0x0285 }, // 133, 2 + { 0x0295, 0x0286 }, // 134, 2 + { 0x0296, 0x0287 }, // 135, 2 + { 0x0297, 0x023e }, // 62 , 2 + { 0x0298, 0x028a }, // 138, 2 + { 0x0299, 0x023b }, // 59 , 2 + { 0x029A, 0x0247 }, // 71 , 2 + { 0x029B, 0x024e }, // 78 , 2 + { 0x029C, 0x0254 }, // 84 , 2 + { 0x029D, 0x0258 }, // 88 , 2 + { 0x029E, 0x0259 }, // 89 , 2 + { 0x029F, 0x025d }, // 93 , 2 + { 0x02A0, 0x0268 }, // 104, 2 + { 0x02A1, 0x0288 }, // 136, 2 + { 0x02A2, 0x0289 }, // 137, 2 + { 0x02A3, 0x028b }, // 139, 2 + { 0x02A4, 0x028c }, // 140, 2 + { 0x02A5, 0x028d }, // 141, 2 + { 0x02A6, 0x028e }, // 142, 2 + { 0x02A7, 0x028f }, // 143, 2 + { 0x02A8, 0x0290 }, // 144, 2 + { 0x02B0, 0x0235 }, // 53 , 2 + { 0x02B6, 0x0236 }, // 54 , 2 + { 0x02B9, 0x0200 }, // 0 , 2 + { 0x02BA, 0x0201 }, // 1 , 2 + { 0x02BB, 0x0202 }, // 2 , 2 + { 0x02BC, 0x0205 }, // 5 , 2 + { 0x02BD, 0x0204 }, // 4 , 2 + { 0x02BE, 0x0207 }, // 7 , 2 + { 0x02BF, 0x0208 }, // 8 , 2 + { 0x02C6, 0x0217 }, // 23 , 2 + { 0x02C7, 0x0218 }, // 24 , 2 + { 0x02C8, 0x020f }, // 15 , 2 + { 0x02C9, 0x0211 }, // 17 , 2 + { 0x02CA, 0x0212 }, // 18 , 2 + { 0x02CB, 0x0213 }, // 19 , 2 + { 0x02CC, 0x0210 }, // 16 , 2 + { 0x02CD, 0x0214 }, // 20 , 2 + { 0x02CE, 0x0215 }, // 21 , 2 + { 0x02CF, 0x0216 }, // 22 , 2 + { 0x02D0, 0x020a }, // 10 , 2 + { 0x02D1, 0x020b }, // 11 , 2 + { 0x02D2, 0x022a }, // 42 , 2 + { 0x02D3, 0x022b }, // 43 , 2 + { 0x02DA, 0x021b }, // 27 , 2 + { 0x02DB, 0x0231 }, // 49 , 2 + { 0x02DC, 0x0219 }, // 25 , 2 + { 0x02DE, 0x0233 }, // 51 , 2 + { 0x0300, 0x0100 }, // 0 , 1 + { 0x0301, 0x0106 }, // 6 , 1 + { 0x0302, 0x0103 }, // 3 , 1 + { 0x0303, 0x0102 }, // 2 , 1 + { 0x0304, 0x0108 }, // 8 , 1 + { 0x0305, 0x0115 }, // 21 , 1 + { 0x0306, 0x0116 }, // 22 , 1 + { 0x0307, 0x010f }, // 15 , 1 + { 0x0308, 0x0107 }, // 7 , 1 + { 0x030A, 0x010e }, // 14 , 1 + { 0x030B, 0x0110 }, // 16 , 1 + { 0x030C, 0x0113 }, // 19 , 1 + { 0x0310, 0x0209 }, // 9 , 2 + { 0x0311, 0x0858 }, // 88 , 8 + { 0x0313, 0x0109 }, // 9 , 1 + { 0x0314, 0x085a }, // 90 , 8 + { 0x0315, 0x010a }, // 10 , 1 + { 0x031C, 0x0221 }, // 33 , 2 + { 0x031D, 0x0222 }, // 34 , 2 + { 0x031E, 0x0223 }, // 35 , 2 + { 0x031F, 0x0224 }, // 36 , 2 + { 0x0320, 0x0225 }, // 37 , 2 + { 0x0321, 0x0226 }, // 38 , 2 + { 0x0322, 0x0227 }, // 39 , 2 + { 0x0323, 0x021e }, // 30 , 2 + { 0x0324, 0x0220 }, // 32 , 2 + { 0x0325, 0x021a }, // 26 , 2 + { 0x0326, 0x010c }, // 12 , 1 + { 0x0327, 0x0111 }, // 17 , 1 + { 0x0328, 0x0112 }, // 18 , 1 + { 0x0329, 0x020e }, // 14 , 2 + { 0x032A, 0x0228 }, // 40 , 2 + { 0x032B, 0x0229 }, // 41 , 2 + { 0x032C, 0x021d }, // 29 , 2 + { 0x032D, 0x021c }, // 28 , 2 + { 0x032E, 0x020d }, // 13 , 2 + { 0x0335, 0x0104 }, // 4 , 1 + { 0x0337, 0x0114 }, // 20 , 1 + { 0x0338, 0x0105 }, // 5 , 1 + { 0x033E, 0x0230 }, // 48 , 2 + { 0x0345, 0x085b }, // 91 , 8 + { 0x0374, 0x0851 }, // 81 , 8 + { 0x0375, 0x0852 }, // 82 , 8 + { 0x0391, 0x0800 }, // 0 , 8 + { 0x0392, 0x0802 }, // 2 , 8 + { 0x0393, 0x0806 }, // 6 , 8 + { 0x0394, 0x0808 }, // 8 , 8 + { 0x0395, 0x080a }, // 10 , 8 + { 0x0396, 0x080c }, // 12 , 8 + { 0x0397, 0x080e }, // 14 , 8 + { 0x0398, 0x0810 }, // 16 , 8 + { 0x0399, 0x0812 }, // 18 , 8 + { 0x039A, 0x0814 }, // 20 , 8 + { 0x039B, 0x0816 }, // 22 , 8 + { 0x039C, 0x0818 }, // 24 , 8 + { 0x039D, 0x081a }, // 26 , 8 + { 0x039E, 0x081c }, // 28 , 8 + { 0x039F, 0x081e }, // 30 , 8 + { 0x03A0, 0x0820 }, // 32 , 8 + { 0x03A1, 0x0822 }, // 34 , 8 + { 0x03A3, 0x0824 }, // 36 , 8 + { 0x03A4, 0x0828 }, // 40 , 8 + { 0x03A5, 0x082a }, // 42 , 8 + { 0x03A6, 0x082c }, // 44 , 8 + { 0x03A7, 0x082e }, // 46 , 8 + { 0x03A8, 0x0830 }, // 48 , 8 + { 0x03A9, 0x0832 }, // 50 , 8 + { 0x03AA, 0x083c }, // 60 , 8 + { 0x03AB, 0x0842 }, // 66 , 8 + { 0x03AC, 0x0835 }, // 53 , 8 + { 0x03AD, 0x0837 }, // 55 , 8 + { 0x03AE, 0x0839 }, // 57 , 8 + { 0x03AF, 0x083b }, // 59 , 8 + { 0x03B1, 0x0801 }, // 1 , 8 + { 0x03B2, 0x0803 }, // 3 , 8 + { 0x03B3, 0x0807 }, // 7 , 8 + { 0x03B4, 0x0809 }, // 9 , 8 + { 0x03B5, 0x080b }, // 11 , 8 + { 0x03B6, 0x080d }, // 13 , 8 + { 0x03B7, 0x080f }, // 15 , 8 + { 0x03B8, 0x0811 }, // 17 , 8 + { 0x03B9, 0x0813 }, // 19 , 8 + { 0x03BA, 0x0815 }, // 21 , 8 + { 0x03BB, 0x0817 }, // 23 , 8 + { 0x03BC, 0x0819 }, // 25 , 8 + { 0x03BD, 0x081b }, // 27 , 8 + { 0x03BE, 0x081d }, // 29 , 8 + { 0x03BF, 0x081f }, // 31 , 8 + { 0x03C0, 0x0821 }, // 33 , 8 + { 0x03C1, 0x0823 }, // 35 , 8 + { 0x03C2, 0x0827 }, // 39 , 8 + { 0x03C3, 0x0825 }, // 37 , 8 + { 0x03C4, 0x0829 }, // 41 , 8 + { 0x03C5, 0x082b }, // 43 , 8 + { 0x03C6, 0x082d }, // 45 , 8 + { 0x03C7, 0x082f }, // 47 , 8 + { 0x03C8, 0x0831 }, // 49 , 8 + { 0x03C9, 0x0833 }, // 51 , 8 + { 0x03CA, 0x083d }, // 61 , 8 + { 0x03CB, 0x0843 }, // 67 , 8 + { 0x03CC, 0x083f }, // 63 , 8 + { 0x03CD, 0x0841 }, // 65 , 8 + { 0x03CE, 0x0845 }, // 69 , 8 + { 0x03D0, 0x0805 }, // 5 , 8 + { 0x03D1, 0x0847 }, // 71 , 8 + { 0x03D2, 0x084c }, // 76 , 8 + { 0x03D5, 0x084d }, // 77 , 8 + { 0x03D6, 0x0849 }, // 73 , 8 + { 0x03D7, 0x084f }, // 79 , 8 + { 0x03DA, 0x08d7 }, // 215, 8 + { 0x03DB, 0x084B }, // 75 , 8 + { 0x03DC, 0x08d8 }, // 216, 8 + { 0x03DE, 0x08d9 }, // 217, 8 + { 0x03E0, 0x08da }, // 218, 8 + { 0x03F0, 0x0848 }, // 72 , 8 + { 0x03F1, 0x084a }, // 74 , 8 + { 0x0401, 0x0a0c }, // 12 , 10 + { 0x0402, 0x0a4a }, // 74 , 10 + { 0x0403, 0x0a44 }, // 68 , 10 + { 0x0404, 0x0a4e }, // 78 , 10 + { 0x0405, 0x0a52 }, // 82 , 10 + { 0x0406, 0x0a58 }, // 88 , 10 + { 0x0407, 0x0a5a }, // 90 , 10 + { 0x0408, 0x0a5e }, // 94 , 10 + { 0x0409, 0x0a68 }, // 104, 10 + { 0x040A, 0x0a6c }, // 108, 10 + { 0x040B, 0x0a72 }, // 114, 10 + { 0x040C, 0x0a60 }, // 96 , 10 + { 0x040E, 0x0a74 }, // 116, 10 + { 0x040F, 0x0a86 }, // 134, 10 + { 0x0410, 0x0a00 }, // 0 , 10 + { 0x0411, 0x0a02 }, // 2 , 10 + { 0x0412, 0x0a04 }, // 4 , 10 + { 0x0413, 0x0a06 }, // 6 , 10 + { 0x0414, 0x0a08 }, // 8 , 10 + { 0x0415, 0x0a0a }, // 10 , 10 + { 0x0416, 0x0a0e }, // 14 , 10 + { 0x0417, 0x0a10 }, // 16 , 10 + { 0x0418, 0x0a12 }, // 18 , 10 + { 0x0419, 0x0a14 }, // 20 , 10 + { 0x041A, 0x0a16 }, // 22 , 10 + { 0x041B, 0x0a18 }, // 24 , 10 + { 0x041C, 0x0a1a }, // 26 , 10 + { 0x041D, 0x0a1c }, // 28 , 10 + { 0x041E, 0x0a1e }, // 30 , 10 + { 0x041F, 0x0a20 }, // 32 , 10 + { 0x0420, 0x0a22 }, // 34 , 10 + { 0x0421, 0x0a24 }, // 36 , 10 + { 0x0422, 0x0a26 }, // 38 , 10 + { 0x0423, 0x0a28 }, // 40 , 10 + { 0x0424, 0x0a2a }, // 42 , 10 + { 0x0425, 0x0a2c }, // 44 , 10 + { 0x0426, 0x0a2e }, // 46 , 10 + { 0x0427, 0x0a30 }, // 48 , 10 + { 0x0428, 0x0a32 }, // 50 , 10 + { 0x0429, 0x0a34 }, // 52 , 10 + { 0x042A, 0x0a36 }, // 54 , 10 + { 0x042B, 0x0a38 }, // 56 , 10 + { 0x042C, 0x0a3a }, // 58 , 10 + { 0x042D, 0x0a3c }, // 60 , 10 + { 0x042E, 0x0a3e }, // 62 , 10 + { 0x042F, 0x0a40 }, // 64 , 10 + { 0x0430, 0x0a01 }, // 1 , 10 + { 0x0431, 0x0a03 }, // 3 , 10 + { 0x0432, 0x0a05 }, // 5 , 10 + { 0x0433, 0x0a07 }, // 7 , 10 + { 0x0434, 0x0a09 }, // 9 , 10 + { 0x0435, 0x0a0b }, // 11 , 10 + { 0x0436, 0x0a0f }, // 15 , 10 + { 0x0437, 0x0a11 }, // 17 , 10 + { 0x0438, 0x0a13 }, // 19 , 10 + { 0x0439, 0x0a15 }, // 21 , 10 + { 0x043A, 0x0a17 }, // 23 , 10 + { 0x043B, 0x0a19 }, // 25 , 10 + { 0x043C, 0x0a1b }, // 27 , 10 + { 0x043D, 0x0a1d }, // 29 , 10 + { 0x043E, 0x0a1f }, // 31 , 10 + { 0x043F, 0x0a21 }, // 33 , 10 + { 0x0440, 0x0a23 }, // 35 , 10 + { 0x0441, 0x0a25 }, // 37 , 10 + { 0x0442, 0x0a27 }, // 39 , 10 + { 0x0443, 0x0a29 }, // 41 , 10 + { 0x0444, 0x0a2b }, // 43 , 10 + { 0x0445, 0x0a2d }, // 45 , 10 + { 0x0446, 0x0a2f }, // 47 , 10 + { 0x0447, 0x0a31 }, // 49 , 10 + { 0x0448, 0x0a33 }, // 51 , 10 + { 0x0449, 0x0a35 }, // 53 , 10 + { 0x044A, 0x0a37 }, // 55 , 10 + { 0x044B, 0x0a39 }, // 57 , 10 + { 0x044C, 0x0a3b }, // 59 , 10 + { 0x044D, 0x0a3d }, // 61 , 10 + { 0x044E, 0x0a3f }, // 63 , 10 + { 0x044F, 0x0a41 }, // 65 , 10 + { 0x0451, 0x0a0d }, // 13 , 10 + { 0x0452, 0x0a4b }, // 75 , 10 + { 0x0453, 0x0a45 }, // 69 , 10 + { 0x0454, 0x0a4f }, // 79 , 10 + { 0x0455, 0x0a53 }, // 83 , 10 + { 0x0456, 0x0a59 }, // 89 , 10 + { 0x0457, 0x0a5b }, // 91 , 10 + { 0x0458, 0x0a5f }, // 95 , 10 + { 0x0459, 0x0a69 }, // 105, 10 + { 0x045A, 0x0a6d }, // 109, 10 + { 0x045B, 0x0a73 }, // 115, 10 + { 0x045C, 0x0a61 }, // 97 , 10 + { 0x045E, 0x0a75 }, // 117, 10 + { 0x045F, 0x0a87 }, // 135, 10 + { 0x0460, 0x0a70 }, // 112, 10 + { 0x0461, 0x0a71 }, // 113, 10 + { 0x0462, 0x0a8e }, // 142, 10 + { 0x0463, 0x0a8f }, // 143, 10 + { 0x0466, 0x0a90 }, // 144, 10 + { 0x0467, 0x0a91 }, // 145, 10 + { 0x046A, 0x0a92 }, // 146, 10 + { 0x046B, 0x0a93 }, // 147, 10 + { 0x046E, 0x0a94 }, // 148, 10 + { 0x046F, 0x0a95 }, // 149, 10 + { 0x0470, 0x0a96 }, // 150, 10 + { 0x0471, 0x0a97 }, // 151, 10 + { 0x0472, 0x0a98 }, // 152, 10 + { 0x0473, 0x0a99 }, // 153, 10 + { 0x0474, 0x0a9a }, // 154, 10 + { 0x0475, 0x0a9b }, // 155, 10 + { 0x047A, 0x0a6e }, // 110, 10 + { 0x047B, 0x0a6f }, // 111, 10 + { 0x047E, 0x0a84 }, // 132, 10 + { 0x047F, 0x0a85 }, // 133, 10 + { 0x0490, 0x0a46 }, // 70 , 10 + { 0x0491, 0x0a47 }, // 71 , 10 + { 0x0492, 0x0a48 }, // 72 , 10 + { 0x0493, 0x0a49 }, // 73 , 10 + { 0x0496, 0x0a50 }, // 80 , 10 + { 0x0497, 0x0a51 }, // 81 , 10 + { 0x049A, 0x0a62 }, // 98 , 10 + { 0x049B, 0x0a63 }, // 99 , 10 + { 0x049C, 0x0a66 }, // 102, 10 + { 0x049D, 0x0a67 }, // 103, 10 + { 0x04A2, 0x0a6a }, // 106, 10 + { 0x04A3, 0x0a6b }, // 107, 10 + { 0x04AE, 0x0a78 }, // 120, 10 + { 0x04AF, 0x0a79 }, // 121, 10 + { 0x04B0, 0x0a7a }, // 122, 10 + { 0x04B1, 0x0a7b }, // 123, 10 + { 0x04B2, 0x0a7e }, // 126, 10 + { 0x04B3, 0x0a7f }, // 127, 10 + { 0x04B6, 0x0a88 }, // 136, 10 + { 0x04B7, 0x0a89 }, // 137, 10 + { 0x04B8, 0x0a8a }, // 138, 10 + { 0x04B9, 0x0a8b }, // 139, 10 + { 0x04BA, 0x0a82 }, // 130, 10 + { 0x04BB, 0x0a83 }, // 131, 10 + { 0x04D8, 0x0a42 }, // 66 , 10 + { 0x04D9, 0x0a43 }, // 67 , 10 + { 0x04EE, 0x0a76 }, // 118, 10 + { 0x04EF, 0x0a77 }, // 119, 10 + { 0x05B0, 0x0920 }, // 32 , 9 + { 0x05B1, 0x0921 }, // 33 , 9 + { 0x05B2, 0x0922 }, // 34 , 9 + { 0x05B3, 0x0923 }, // 35 , 9 + { 0x05B4, 0x0924 }, // 36 , 9 + { 0x05B5, 0x0925 }, // 37 , 9 + { 0x05B6, 0x0926 }, // 38 , 9 + { 0x05B7, 0x0927 }, // 39 , 9 + { 0x05B8, 0x0928 }, // 40 , 9 + { 0x05B9, 0x0929 }, // 41 , 9 + { 0x05BB, 0x092b }, // 43 , 9 + { 0x05BC, 0x092c }, // 44 , 9 + { 0x05BD, 0x092d }, // 45 , 9 + { 0x05BF, 0x092e }, // 46 , 9 + { 0x05C0, 0x091c }, // 28 , 9 + { 0x05C3, 0x091d }, // 29 , 9 + { 0x05D0, 0x0900 }, // 0 , 9 + { 0x05D1, 0x0901 }, // 1 , 9 + { 0x05D2, 0x0902 }, // 2 , 9 + { 0x05D3, 0x0903 }, // 3 , 9 + { 0x05D4, 0x0904 }, // 4 , 9 + { 0x05D5, 0x0905 }, // 5 , 9 + { 0x05D6, 0x0906 }, // 6 , 9 + { 0x05D7, 0x0907 }, // 7 , 9 + { 0x05D8, 0x0908 }, // 8 , 9 + { 0x05D9, 0x0909 }, // 9 , 9 + { 0x05DA, 0x090a }, // 10 , 9 + { 0x05DB, 0x090b }, // 11 , 9 + { 0x05DC, 0x090c }, // 12 , 9 + { 0x05DD, 0x090d }, // 13 , 9 + { 0x05DE, 0x090e }, // 14 , 9 + { 0x05DF, 0x090f }, // 15 , 9 + { 0x05E0, 0x0910 }, // 16 , 9 + { 0x05E1, 0x0911 }, // 17 , 9 + { 0x05E2, 0x0912 }, // 18 , 9 + { 0x05E3, 0x0913 }, // 19 , 9 + { 0x05E4, 0x0914 }, // 20 , 9 + { 0x05E5, 0x0915 }, // 21 , 9 + { 0x05E6, 0x0916 }, // 22 , 9 + { 0x05E7, 0x0917 }, // 23 , 9 + { 0x05E8, 0x0918 }, // 24 , 9 + { 0x05E9, 0x0919 }, // 25 , 9 + { 0x05EA, 0x091a }, // 26 , 9 + { 0x05F0, 0x0931 }, // 49 , 9 + { 0x05F1, 0x0932 }, // 50 , 9 + { 0x05F2, 0x0933 }, // 51 , 9 + { 0x05F3, 0x091e }, // 30 , 9 + { 0x05F4, 0x091f }, // 31 , 9 + { 0x060C, 0x0d26 }, // 38 , 13 + { 0x061B, 0x0d27 }, // 39 , 13 + { 0x061F, 0x0d28 }, // 40 , 13 + { 0x0621, 0x0da4 }, // 164, 13 + { 0x0622, 0x0db1 }, // 177, 13 + { 0x0623, 0x0da5 }, // 165, 13 + { 0x0624, 0x0da9 }, // 169, 13 + { 0x0625, 0x0da7 }, // 167, 13 + { 0x0626, 0x0dab }, // 171, 13 + { 0x0627, 0x0d3a }, // 58 , 13 + { 0x0628, 0x0d3c }, // 60 , 13 + { 0x0629, 0x0d98 }, // 152, 13 + { 0x062A, 0x0d40 }, // 64 , 13 + { 0x062B, 0x0d44 }, // 68 , 13 + { 0x062C, 0x0d48 }, // 72 , 13 + { 0x062D, 0x0d4c }, // 76 , 13 + { 0x062E, 0x0d50 }, // 80 , 13 + { 0x062F, 0x0d54 }, // 84 , 13 + { 0x0630, 0x0d56 }, // 86 , 13 + { 0x0631, 0x0d58 }, // 88 , 13 + { 0x0632, 0x0d5a }, // 90 , 13 + { 0x0633, 0x0d5c }, // 92 , 13 + { 0x0634, 0x0d60 }, // 96 , 13 + { 0x0635, 0x0d64 }, // 100, 13 + { 0x0636, 0x0d68 }, // 104, 13 + { 0x0637, 0x0d6c }, // 108, 13 + { 0x0638, 0x0d70 }, // 112, 13 + { 0x0639, 0x0d74 }, // 116, 13 + { 0x063A, 0x0d78 }, // 120, 13 + { 0x0640, 0x0dc2 }, // 194, 13 + { 0x0641, 0x0d7c }, // 124, 13 + { 0x0642, 0x0d80 }, // 128, 13 + { 0x0643, 0x0d84 }, // 132, 13 + { 0x0644, 0x0d88 }, // 136, 13 + { 0x0645, 0x0d8c }, // 140, 13 + { 0x0646, 0x0d90 }, // 144, 13 + { 0x0647, 0x0d94 }, // 148, 13 + { 0x0648, 0x0d9a }, // 154, 13 + { 0x0649, 0x0da0 }, // 160, 13 + { 0x064A, 0x0d9c }, // 156, 13 + { 0x064B, 0x0d10 }, // 16 , 13 + { 0x064C, 0x0d11 }, // 17 , 13 + { 0x064E, 0x0d0a }, // 10 , 13 + { 0x064F, 0x0d0c }, // 12 , 13 + { 0x0650, 0x0d0e }, // 14 , 13 + { 0x0651, 0x0d16 }, // 22 , 13 + { 0x0652, 0x0d14 }, // 20 , 13 + { 0x0660, 0x0d38 }, // 56 , 13 + { 0x0661, 0x0d2f }, // 47 , 13 + { 0x0662, 0x0d30 }, // 48 , 13 + { 0x0663, 0x0d31 }, // 49 , 13 + { 0x0664, 0x0d32 }, // 50 , 13 + { 0x0665, 0x0d33 }, // 51 , 13 + { 0x0666, 0x0d34 }, // 52 , 13 + { 0x0667, 0x0d35 }, // 53 , 13 + { 0x0668, 0x0d36 }, // 54 , 13 + { 0x0669, 0x0d37 }, // 55 , 13 + { 0x066A, 0x0d2a }, // 42 , 13 + { 0x0671, 0x0db3 }, // 179, 13 + { 0x0674, 0x0d24 }, // 36 , 13 + { 0x0679, 0x0e3c }, // 60 , 14 + { 0x067A, 0x0e4c }, // 76 , 14 + { 0x067B, 0x0e30 }, // 48 , 14 + { 0x067C, 0x0e40 }, // 64 , 14 + { 0x067D, 0x0e48 }, // 72 , 14 + { 0x067E, 0x0e38 }, // 56 , 14 + { 0x067F, 0x0e44 }, // 68 , 14 + { 0x0680, 0x0e34 }, // 52 , 14 + { 0x0681, 0x0e64 }, // 100, 14 + { 0x0683, 0x0e54 }, // 84 , 14 + { 0x0684, 0x0e50 }, // 80 , 14 + { 0x0685, 0x0e60 }, // 96 , 14 + { 0x0686, 0x0e58 }, // 88 , 14 + { 0x0687, 0x0e5c }, // 92 , 14 + { 0x0688, 0x0e68 }, // 104, 14 + { 0x0689, 0x0e6a }, // 106, 14 + { 0x068A, 0x0e70 }, // 112, 14 + { 0x068C, 0x0e6c }, // 108, 14 + { 0x068D, 0x0e72 }, // 114, 14 + { 0x068E, 0x0e6e }, // 110, 14 + { 0x0691, 0x0e76 }, // 118, 14 + { 0x0692, 0x0e7C }, // 124, 14 + { 0x0693, 0x0e74 }, // 116, 14 + { 0x0695, 0x0e7a }, // 122, 14 + { 0x0696, 0x0e80 }, // 128, 14 + { 0x0698, 0x0e7e }, // 126, 14 + { 0x0699, 0x0e78 }, // 120, 14 + { 0x069A, 0x0e84 }, // 132, 14 + { 0x06A0, 0x0e88 }, // 136, 14 + { 0x06A4, 0x0e8c }, // 140, 14 + { 0x06A6, 0x0e90 }, // 144, 14 + { 0x06A9, 0x0e94 }, // 148, 14 + { 0x06AA, 0x0e9c }, // 156, 14 + { 0x06AB, 0x0ea8 }, // 168, 14 + { 0x06AF, 0x0ea0 }, // 160, 14 + { 0x06B1, 0x0eac }, // 172, 14 + { 0x06B3, 0x0eb0 }, // 176, 14 + { 0x06B5, 0x0eb4 }, // 180, 14 + { 0x06BA, 0x0eba }, // 186, 14 + { 0x06BB, 0x0ec2 }, // 194, 14 + { 0x06BC, 0x0ebe }, // 190, 14 + { 0x06C0, 0x0eda }, // 218, 14 + { 0x06C6, 0x0ec6 }, // 198, 14 + { 0x06CA, 0x0ec8 }, // 200, 14 + { 0x06CE, 0x0ed0 }, // 208, 14 + { 0x06D1, 0x0ed6 }, // 214, 14 + { 0x06D2, 0x0ed4 }, // 212, 14 + { 0x06D6, 0x0d25 }, // 37 , 13 + { 0x06E4, 0x0d22 }, // 34 , 13 + { 0x06F4, 0x0e29 }, // 41 , 14 + { 0x06F5, 0x0e2b }, // 43 , 14 + { 0x06F6, 0x0e2c }, // 44 , 14 + { 0x06F7, 0x0e2e }, // 46 , 14 + { 0x06F8, 0x0e2f }, // 47 , 14 + { 0x10D0, 0x0ad2 }, // 210, 10 + { 0x10D1, 0x0ad3 }, // 211, 10 + { 0x10D2, 0x0ad4 }, // 212, 10 + { 0x10D3, 0x0ad5 }, // 213, 10 + { 0x10D4, 0x0ad6 }, // 214, 10 + { 0x10D5, 0x0ad7 }, // 215, 10 + { 0x10D6, 0x0ad8 }, // 216, 10 + { 0x10D7, 0x0ada }, // 218, 10 + { 0x10D8, 0x0adb }, // 219, 10 + { 0x10D9, 0x0adc }, // 220, 10 + { 0x10DA, 0x0add }, // 221, 10 + { 0x10DB, 0x0ade }, // 222, 10 + { 0x10DC, 0x0adf }, // 223, 10 + { 0x10DD, 0x0ae1 }, // 225, 10 + { 0x10DE, 0x0ae2 }, // 226, 10 + { 0x10DF, 0x0ae3 }, // 227, 10 + { 0x10E0, 0x0ae4 }, // 228, 10 + { 0x10E1, 0x0ae5 }, // 229, 10 + { 0x10E2, 0x0ae6 }, // 230, 10 + { 0x10E3, 0x0ae7 }, // 231, 10 + { 0x10E4, 0x0ae9 }, // 233, 10 + { 0x10E5, 0x0aea }, // 234, 10 + { 0x10E6, 0x0aeb }, // 235, 10 + { 0x10E7, 0x0aec }, // 236, 10 + { 0x10E8, 0x0aed }, // 237, 10 + { 0x10E9, 0x0aee }, // 238, 10 + { 0x10EA, 0x0aef }, // 239, 10 + { 0x10EB, 0x0af0 }, // 240, 10 + { 0x10EC, 0x0af1 }, // 241, 10 + { 0x10ED, 0x0af2 }, // 242, 10 + { 0x10EE, 0x0af3 }, // 243, 10 + { 0x10EF, 0x0af5 }, // 245, 10 + { 0x10F0, 0x0af6 }, // 246, 10 + { 0x10F1, 0x0ad9 }, // 217, 10 + { 0x10F2, 0x0ae0 }, // 224, 10 + { 0x10F3, 0x0ae8 }, // 232, 10 + { 0x10F4, 0x0af4 }, // 244, 10 + { 0x10F5, 0x0af7 }, // 247, 10 + { 0x10F6, 0x0af8 }, // 248, 10 + { 0x1F00, 0x0873 }, // 115, 8 + { 0x1F01, 0x087b }, // 123, 8 + { 0x1F02, 0x0875 }, // 117, 8 + { 0x1F03, 0x087d }, // 125, 8 + { 0x1F04, 0x0874 }, // 116, 8 + { 0x1F05, 0x087c }, // 124, 8 + { 0x1F10, 0x0884 }, // 132, 8 + { 0x1F11, 0x0887 }, // 135, 8 + { 0x1F12, 0x0886 }, // 134, 8 + { 0x1F13, 0x0889 }, // 137, 8 + { 0x1F14, 0x0885 }, // 133, 8 + { 0x1F15, 0x0888 }, // 136, 8 + { 0x1F20, 0x0890 }, // 144, 8 + { 0x1F21, 0x0898 }, // 152, 8 + { 0x1F22, 0x0892 }, // 146, 8 + { 0x1F23, 0x089a }, // 154, 8 + { 0x1F24, 0x0891 }, // 145, 8 + { 0x1F25, 0x0899 }, // 153, 8 + { 0x1F30, 0x08a4 }, // 164, 8 + { 0x1F31, 0x08a8 }, // 168, 8 + { 0x1F32, 0x08a6 }, // 166, 8 + { 0x1F33, 0x08aa }, // 170, 8 + { 0x1F34, 0x08a5 }, // 165, 8 + { 0x1F35, 0x08a9 }, // 169, 8 + { 0x1F40, 0x08ad }, // 173, 8 + { 0x1F41, 0x08b0 }, // 176, 8 + { 0x1F42, 0x08af }, // 175, 8 + { 0x1F43, 0x08b2 }, // 178, 8 + { 0x1F44, 0x08ae }, // 174, 8 + { 0x1F45, 0x08b1 }, // 177, 8 + { 0x1F50, 0x08b9 }, // 185, 8 + { 0x1F51, 0x08bd }, // 189, 8 + { 0x1F52, 0x08bb }, // 187, 8 + { 0x1F53, 0x08bf }, // 191, 8 + { 0x1F54, 0x08ba }, // 186, 8 + { 0x1F55, 0x08be }, // 190, 8 + { 0x1F60, 0x08c7 }, // 199, 8 + { 0x1F61, 0x08cf }, // 207, 8 + { 0x1F62, 0x08c9 }, // 201, 8 + { 0x1F63, 0x08d1 }, // 209, 8 + { 0x1F64, 0x08c8 }, // 200, 8 + { 0x1F65, 0x08d0 }, // 208, 8 + { 0x1F70, 0x086d }, // 109, 8 + { 0x1F72, 0x0883 }, // 131, 8 + { 0x1F74, 0x088a }, // 138, 8 + { 0x1F76, 0x08a0 }, // 160, 8 + { 0x1F78, 0x08ac }, // 172, 8 + { 0x1F7A, 0x08b5 }, // 181, 8 + { 0x1F7C, 0x08c1 }, // 193, 8 + { 0x1F80, 0x0877 }, // 119, 8 + { 0x1F81, 0x087f }, // 127, 8 + { 0x1F82, 0x0879 }, // 121, 8 + { 0x1F83, 0x0881 }, // 129, 8 + { 0x1F84, 0x0878 }, // 120, 8 + { 0x1F85, 0x0880 }, // 128, 8 + { 0x1F90, 0x0894 }, // 148, 8 + { 0x1F91, 0x089c }, // 156, 8 + { 0x1F92, 0x0896 }, // 150, 8 + { 0x1F93, 0x089e }, // 158, 8 + { 0x1F94, 0x0895 }, // 149, 8 + { 0x1F95, 0x089d }, // 157, 8 + { 0x1FA0, 0x08cb }, // 203, 8 + { 0x1FA1, 0x08d3 }, // 211, 8 + { 0x1FA2, 0x08cd }, // 205, 8 + { 0x1FA3, 0x08d5 }, // 213, 8 + { 0x1FA4, 0x08cc }, // 204, 8 + { 0x1FA5, 0x08d4 }, // 212, 8 + { 0x1FB2, 0x0871 }, // 113, 8 + { 0x1FB3, 0x086f }, // 111, 8 + { 0x1FB4, 0x0870 }, // 112, 8 + { 0x1FC2, 0x088e }, // 142, 8 + { 0x1FC3, 0x088c }, // 140, 8 + { 0x1FC4, 0x088d }, // 141, 8 + { 0x1FCD, 0x085e }, // 94 , 8 + { 0x1FCE, 0x085c }, // 92 , 8 + { 0x1FDD, 0x085f }, // 95 , 8 + { 0x1FDE, 0x085d }, // 93 , 8 + { 0x1FE4, 0x08B4 }, // 180, 8 + { 0x1FE5, 0x08B3 }, // 179, 8 + { 0x1FF2, 0x08c5 }, // 197, 8 + { 0x1FF3, 0x08c3 }, // 195, 8 + { 0x1FF4, 0x08c4 }, // 196, 8 + { 0x2007, 0x0517 }, // 23 , 5 + { 0x2012, 0x0432 }, // 50 , 4 + { 0x2013, 0x0421 }, // 33 , 4 + { 0x2014, 0x0422 }, // 34 , 4 + { 0x2017, 0x022f }, // 47 , 2 + { 0x2018, 0x041d }, // 29 , 4 + { 0x2019, 0x041c }, // 28 , 4 + { 0x201A, 0x043e }, // 62 , 4 + { 0x201B, 0x041b }, // 27 , 4 + { 0x201C, 0x0420 }, // 32 , 4 + { 0x201D, 0x041f }, // 31 , 4 + { 0x201E, 0x043f }, // 63 , 4 + { 0x201F, 0x041e }, // 30 , 4 + { 0x2020, 0x0427 }, // 39 , 4 + { 0x2021, 0x0428 }, // 40 , 4 + { 0x2022, 0x0403 }, // 3 , 4 + { 0x2026, 0x0438 }, // 56 , 4 + { 0x2030, 0x044b }, // 75 , 4 + { 0x2033, 0x0580 }, // 128, 5 + { 0x2034, 0x0671 }, // 113, 6 + { 0x2036, 0x057f }, // 127, 5 + { 0x2039, 0x0423 }, // 35 , 4 + { 0x203A, 0x0424 }, // 36 , 4 + { 0x203C, 0x050d }, // 13 , 5 + { 0x203E, 0x0626 }, // 38 , 6 + { 0x207F, 0x0415 }, // 21 , 4 + { 0x20A0, 0x043c }, // 60 , 4 + { 0x20A2, 0x043b }, // 59 , 4 + { 0x20A3, 0x043a }, // 58 , 4 + { 0x20A4, 0x043d }, // 61 , 4 + { 0x20A6, 0x0457 }, // 87 , 4 + { 0x20A7, 0x040d }, // 13 , 4 + { 0x20A8, 0x0458 }, // 88 , 4 + { 0x20A9, 0x0456 }, // 86 , 4 + { 0x20AA, 0x097A }, // 122, 9 + { 0x20AC, 0x0466 }, // 102, 4, Euro Sign - GW assigned x448 [4,72] + { 0x20DD, 0x066d }, // 109, 6 + { 0x20E1, 0x06e1 }, // 225, 6 + { 0x2102, 0x06d5 }, // 213, 6 + { 0x2104, 0x0515 }, // 21 , 5 + { 0x2105, 0x0449 }, // 73 , 4 + { 0x2106, 0x044a }, // 74 , 4 + { 0x210C, 0x06e9 }, // 233, 6 + { 0x210F, 0x0632 }, // 50 , 6 + { 0x2111, 0x0633 }, // 51 , 6 + { 0x2112, 0x0669 }, // 105, 6 + { 0x2113, 0x0631 }, // 49 , 6 + { 0x2115, 0x06d7 }, // 215, 6 + { 0x2116, 0x044c }, // 76 , 4 + { 0x2118, 0x0635 }, // 53 , 6 + { 0x211C, 0x0634 }, // 52 , 6 + { 0x211D, 0x06d8 }, // 216, 6 + { 0x211E, 0x042b }, // 43 , 4 + { 0x2120, 0x042a }, // 42 , 4 + { 0x2122, 0x0429 }, // 41 , 4 + { 0x2127, 0x06a7 }, // 167, 6 + { 0x2128, 0x066b }, // 107, 6 + { 0x212B, 0x0623 }, // 35 , 6 + { 0x212D, 0x066a }, // 106, 6 + { 0x212F, 0x0630 }, // 48 , 6 + { 0x2130, 0x06d3 }, // 211, 6 + { 0x2131, 0x06d4 }, // 212, 6 + { 0x2153, 0x0440 }, // 64 , 4 + { 0x2154, 0x0441 }, // 65 , 4 + { 0x215B, 0x0442 }, // 66 , 4 + { 0x215C, 0x0443 }, // 67 , 4 + { 0x215D, 0x0444 }, // 68 , 4 + { 0x215E, 0x0445 }, // 69 , 4 + { 0x2190, 0x0590 }, // 144, 5 + { 0x2191, 0x0617 }, // 23 , 6 + { 0x2192, 0x05d5 }, // 213, 5 + { 0x2193, 0x0618 }, // 24 , 6 + { 0x2194, 0x05d6 }, // 214, 5 + { 0x2195, 0x05d7 }, // 215, 5 + { 0x2196, 0x0640 }, // 64 , 6 + { 0x2197, 0x063e }, // 62 , 6 + { 0x2198, 0x063f }, // 63 , 6 + { 0x2199, 0x0641 }, // 65 , 6 + { 0x219D, 0x0690 }, // 144, 6 + { 0x21A3, 0x0693 }, // 147, 6 + { 0x21A8, 0x050f }, // 15 , 5 + { 0x21A9, 0x0691 }, // 145, 6 + { 0x21AA, 0x0692 }, // 146, 6 + { 0x21B5, 0x0514 }, // 20 , 5 + { 0x21BC, 0x0694 }, // 148, 6 + { 0x21BD, 0x0695 }, // 149, 6 + { 0x21BE, 0x069b }, // 155, 6 + { 0x21BF, 0x069a }, // 154, 6 + { 0x21C0, 0x0696 }, // 150, 6 + { 0x21C1, 0x0697 }, // 151, 6 + { 0x21C2, 0x069d }, // 157, 6 + { 0x21C3, 0x069c }, // 156, 6 + { 0x21C4, 0x0636 }, // 54 , 6 + { 0x21C6, 0x0637 }, // 55 , 6 + { 0x21C7, 0x069f }, // 159, 6 + { 0x21C9, 0x069e }, // 158, 6 + { 0x21CB, 0x0699 }, // 153, 6 + { 0x21CC, 0x0698 }, // 152, 6 + { 0x21D0, 0x0639 }, // 57 , 6 + { 0x21D1, 0x063a }, // 58 , 6 + { 0x21D2, 0x0638 }, // 56 , 6 + { 0x21D3, 0x063b }, // 59 , 6 + { 0x21D4, 0x063c }, // 60 , 6 + { 0x21D5, 0x063d }, // 61 , 6 + { 0x21E6, 0x0597 }, // 151, 5 + { 0x21E8, 0x0596 }, // 150, 5 + { 0x2200, 0x067a }, // 122, 6 + { 0x2202, 0x062c }, // 44 , 6 + { 0x2203, 0x0679 }, // 121, 6 + { 0x2204, 0x06d0 }, // 208, 6 + { 0x2205, 0x0648 }, // 72 , 6 + { 0x2207, 0x062b }, // 43 , 6 + { 0x2208, 0x060f }, // 15 , 6 + { 0x2209, 0x06d1 }, // 209, 6 + { 0x220B, 0x06db }, // 219, 6 + { 0x220D, 0x0647 }, // 71 , 6 + { 0x220F, 0x0629 }, // 41 , 6 + { 0x2210, 0x0672 }, // 114, 6 + { 0x2211, 0x0612 }, // 18 , 6 + { 0x2212, 0x0600 }, // 0 , 6 + { 0x2213, 0x062a }, // 42 , 6 + { 0x2214, 0x06ae }, // 174, 6 + { 0x2215, 0x0606 }, // 6 , 6 + { 0x2216, 0x0607 }, // 7 , 6 + { 0x2218, 0x0621 }, // 33 , 6 + { 0x2219, 0x0622 }, // 34 , 6 + { 0x221A, 0x0704 }, // 4 , 7 + { 0x221D, 0x0604 }, // 4 , 6 + { 0x221E, 0x0613 }, // 19 , 6 + { 0x221F, 0x06da }, // 218, 6 + { 0x2220, 0x064f }, // 79 , 6 + { 0x2221, 0x06a8 }, // 168, 6 + { 0x2222, 0x06a9 }, // 169, 6 + { 0x2223, 0x0609 }, // 9 , 6 + { 0x2224, 0x06ce }, // 206, 6 + { 0x2225, 0x0611 }, // 17 , 6 + { 0x2226, 0x06cd }, // 205, 6 + { 0x2227, 0x0655 }, // 85 , 6 + { 0x2228, 0x0656 }, // 86 , 6 + { 0x2229, 0x0610 }, // 16 , 6 + { 0x222A, 0x0642 }, // 66 , 6 + { 0x222B, 0x0628 }, // 40 , 6 + { 0x222E, 0x0668 }, // 104, 6 + { 0x2234, 0x0666 }, // 102, 6 + { 0x2235, 0x0665 }, // 101, 6 + { 0x2237, 0x0667 }, // 103, 6 + { 0x223C, 0x060c }, // 12 , 6 + { 0x2241, 0x06bd }, // 189, 6 + { 0x2243, 0x0673 }, // 115, 6 + { 0x2244, 0x06be }, // 190, 6 + { 0x2245, 0x0674 }, // 116, 6 + { 0x2247, 0x06bf }, // 191, 6 + { 0x2248, 0x060d }, // 13 , 6 + { 0x2249, 0x06c0 }, // 192, 6 + { 0x224D, 0x06b3 }, // 179, 6 + { 0x224E, 0x06b2 }, // 178, 6 + { 0x2250, 0x06af }, // 175, 6 + { 0x2252, 0x06b0 }, // 176, 6 + { 0x2253, 0x06b1 }, // 177, 6 + { 0x225F, 0x06d9 }, // 217, 6 + { 0x2260, 0x0663 }, // 99 , 6 + { 0x2261, 0x060e }, // 14 , 6 + { 0x2262, 0x0664 }, // 100, 6 + { 0x2264, 0x0602 }, // 2 , 6 + { 0x2265, 0x0603 }, // 3 , 6 + { 0x226A, 0x064d }, // 77 , 6 + { 0x226B, 0x064e }, // 78 , 6 + { 0x226C, 0x06b6 }, // 182, 6 + { 0x226D, 0x06cf }, // 207, 6 + { 0x226E, 0x06b9 }, // 185, 6 + { 0x226F, 0x06bb }, // 187, 6 + { 0x2270, 0x06ba }, // 186, 6 + { 0x2271, 0x06bc }, // 188, 6 + { 0x2272, 0x06eb }, // 235, 6 + { 0x2273, 0x06ec }, // 236, 6 + { 0x227A, 0x0675 }, // 117, 6 + { 0x227B, 0x0677 }, // 119, 6 + { 0x227C, 0x0676 }, // 118, 6 + { 0x227D, 0x0678 }, // 120, 6 + { 0x2280, 0x06c1 }, // 193, 6 + { 0x2281, 0x06c3 }, // 195, 6 + { 0x2282, 0x0643 }, // 67 , 6 + { 0x2283, 0x0644 }, // 68 , 6 + { 0x2284, 0x06c5 }, // 197, 6 + { 0x2285, 0x06c6 }, // 198, 6 + { 0x2286, 0x0645 }, // 69 , 6 + { 0x2287, 0x0646 }, // 70 , 6 + { 0x2288, 0x06c7 }, // 199, 6 + { 0x2289, 0x06c8 }, // 200, 6 + { 0x228A, 0x067e }, // 126, 6 + { 0x228B, 0x067f }, // 127, 6 + { 0x228E, 0x067d }, // 125, 6 + { 0x228F, 0x0682 }, // 130, 6 + { 0x2290, 0x0685 }, // 133, 6 + { 0x2291, 0x0683 }, // 131, 6 + { 0x2292, 0x0686 }, // 134, 6 + { 0x2293, 0x0680 }, // 128, 6 + { 0x2294, 0x0681 }, // 129, 6 + { 0x2295, 0x0651 }, // 81 , 6 + { 0x2296, 0x0652 }, // 82 , 6 + { 0x2297, 0x0650 }, // 80 , 6 + { 0x2299, 0x0654 }, // 84 , 6 + { 0x229A, 0x06a4 }, // 164, 6 + { 0x229B, 0x06a5 }, // 165, 6 + { 0x229D, 0x06a6 }, // 166, 6 + { 0x22A2, 0x065b }, // 91 , 6 + { 0x22A3, 0x065c }, // 92 , 6 + { 0x22A4, 0x0658 }, // 88 , 6 + { 0x22A5, 0x0659 }, // 89 , 6 + { 0x22A8, 0x06b4 }, // 180, 6 + { 0x22BB, 0x0657 }, // 87 , 6 + { 0x22C5, 0x061f }, // 31 , 6 + { 0x22C6, 0x0670 }, // 112, 6 + { 0x22C8, 0x068c }, // 140, 6 + { 0x22D0, 0x06a2 }, // 162, 6 + { 0x22D1, 0x06a3 }, // 163, 6 + { 0x22D2, 0x06a1 }, // 161, 6 + { 0x22D3, 0x06a0 }, // 160, 6 + { 0x22D8, 0x067b }, // 123, 6 + { 0x22D9, 0x067c }, // 124, 6 + { 0x22E0, 0x06c2 }, // 194, 6 + { 0x22E1, 0x06c4 }, // 196, 6 + { 0x22E2, 0x06cb }, // 203, 6 + { 0x22E3, 0x06cc }, // 204, 6 + { 0x22E4, 0x0684 }, // 132, 6 + { 0x22E5, 0x0687 }, // 135, 6 + { 0x22EE, 0x06de }, // 222, 6 + { 0x22EF, 0x06dc }, // 220, 6 + { 0x22F1, 0x06df }, // 223, 6 + { 0x2302, 0x050c }, // 12 , 5 + { 0x2308, 0x0649 }, // 73 , 6 + { 0x2309, 0x064a }, // 74 , 6 + { 0x230A, 0x064b }, // 75 , 6 + { 0x230B, 0x064c }, // 76 , 6 + { 0x2310, 0x0510 }, // 16 , 5 + { 0x2312, 0x065a }, // 90 , 6 + { 0x2319, 0x0511 }, // 17 , 5 + { 0x231A, 0x051f }, // 31 , 5 + { 0x231B, 0x0520 }, // 32 , 5 + { 0x2320, 0x0700 }, // 0 , 7 + { 0x2321, 0x0701 }, // 1 , 7 + { 0x2322, 0x068e }, // 142, 6 + { 0x2323, 0x068d }, // 141, 6 + { 0x2329, 0x060a }, // 10 , 6 + { 0x232A, 0x060b }, // 11 , 6 + { 0x2409, 0x044f }, // 79 , 4 + { 0x240A, 0x0452 }, // 82 , 4 + { 0x240B, 0x0454 }, // 84 , 4 + { 0x240C, 0x0450 }, // 80 , 4 + { 0x240D, 0x0451 }, // 81 , 4 + { 0x2424, 0x0453 }, // 83 , 4 + { 0x24C2, 0x0446 }, // 70 , 4 + { 0x24C5, 0x0447 }, // 71 , 4 + { 0x24CA, 0x0448 }, // 72 , 4, - circled U + { 0x2500, 0x0308 }, // 8 , 3 + { 0x2502, 0x0309 }, // 9 , 3 + { 0x250C, 0x030a }, // 10 , 3 + { 0x2510, 0x030b }, // 11 , 3 + { 0x2514, 0x030d }, // 13 , 3 + { 0x2518, 0x030c }, // 12 , 3 + { 0x251C, 0x030e }, // 14 , 3 + { 0x251E, 0x033e }, // 62 , 3 + { 0x251F, 0x033c }, // 60 , 3 + { 0x2521, 0x033f }, // 63 , 3 + { 0x2522, 0x033d }, // 61 , 3 + { 0x2524, 0x0310 }, // 16 , 3 + { 0x2526, 0x0345 }, // 69 , 3 + { 0x2527, 0x0344 }, // 68 , 3 + { 0x2529, 0x0347 }, // 71 , 3 + { 0x252A, 0x0346 }, // 70 , 3 + { 0x252C, 0x030f }, // 15 , 3 + { 0x252D, 0x0342 }, // 66 , 3 + { 0x252E, 0x0340 }, // 64 , 3 + { 0x2531, 0x0343 }, // 67 , 3 + { 0x2532, 0x0341 }, // 65 , 3 + { 0x2534, 0x0311 }, // 17 , 3 + { 0x2535, 0x034a }, // 74 , 3 + { 0x2536, 0x0348 }, // 72 , 3 + { 0x2539, 0x034b }, // 75 , 3 + { 0x253A, 0x0349 }, // 73 , 3 + { 0x253C, 0x0312 }, // 18 , 3 + { 0x253D, 0x0352 }, // 82 , 3 + { 0x253E, 0x034e }, // 78 , 3 + { 0x2540, 0x034f }, // 79 , 3 + { 0x2541, 0x034c }, // 76 , 3 + { 0x2543, 0x0355 }, // 85 , 3 + { 0x2544, 0x0350 }, // 80 , 3 + { 0x2545, 0x0353 }, // 83 , 3 + { 0x2546, 0x034d }, // 77 , 3 + { 0x2547, 0x0357 }, // 87 , 3 + { 0x2548, 0x0354 }, // 84 , 3 + { 0x2549, 0x0356 }, // 86 , 3 + { 0x254A, 0x0351 }, // 81 , 3 + { 0x2550, 0x0313 }, // 19 , 3 + { 0x2551, 0x0314 }, // 20 , 3 + { 0x2552, 0x031e }, // 30 , 3 + { 0x2553, 0x0322 }, // 34 , 3 + { 0x2554, 0x0315 }, // 21 , 3 + { 0x2555, 0x031f }, // 31 , 3 + { 0x2556, 0x0323 }, // 35 , 3 + { 0x2557, 0x0316 }, // 22 , 3 + { 0x2558, 0x0321 }, // 33 , 3 + { 0x2559, 0x0325 }, // 37 , 3 + { 0x255A, 0x0318 }, // 24 , 3 + { 0x255B, 0x0320 }, // 32 , 3 + { 0x255C, 0x0324 }, // 36 , 3 + { 0x255D, 0x0317 }, // 23 , 3 + { 0x255E, 0x0326 }, // 38 , 3 + { 0x255F, 0x032a }, // 42 , 3 + { 0x2560, 0x0319 }, // 25 , 3 + { 0x2561, 0x0328 }, // 40 , 3 + { 0x2562, 0x032c }, // 44 , 3 + { 0x2563, 0x031b }, // 27 , 3 + { 0x2564, 0x032b }, // 43 , 3 + { 0x2565, 0x0327 }, // 39 , 3 + { 0x2566, 0x031a }, // 26 , 3 + { 0x2567, 0x032d }, // 45 , 3 + { 0x2568, 0x0329 }, // 41 , 3 + { 0x2569, 0x031c }, // 28 , 3 + { 0x256A, 0x032f }, // 47 , 3 + { 0x256B, 0x032e }, // 46 , 3 + { 0x256C, 0x031d }, // 29 , 3 + { 0x2574, 0x0330 }, // 48 , 3 + { 0x2575, 0x0331 }, // 49 , 3 + { 0x2576, 0x0332 }, // 50 , 3 + { 0x2577, 0x0333 }, // 51 , 3 + { 0x2578, 0x0334 }, // 52 , 3 + { 0x2579, 0x0335 }, // 53 , 3 + { 0x257A, 0x0336 }, // 54 , 3 + { 0x257B, 0x0337 }, // 55 , 3 + { 0x257C, 0x0338 }, // 56 , 3 + { 0x257D, 0x033a }, // 58 , 3 + { 0x257E, 0x0339 }, // 57 , 3 + { 0x257F, 0x033b }, // 59 , 3 + { 0x2580, 0x0305 }, // 5 , 3 + { 0x2584, 0x0307 }, // 7 , 3 + { 0x2588, 0x0303 }, // 3 , 3 + { 0x258C, 0x0304 }, // 4 , 3 + { 0x2590, 0x0306 }, // 6 , 3 + { 0x2591, 0x0300 }, // 0 , 3 + { 0x2592, 0x0301 }, // 1 , 3 + { 0x2593, 0x0302 }, // 2 , 3 + { 0x25A0, 0x0402 }, // 2 , 4 + { 0x25A1, 0x0426 }, // 38 , 4 + { 0x25AA, 0x042f }, // 47 , 4 + { 0x25AB, 0x0431 }, // 49 , 4 + { 0x25AC, 0x050b }, // 11 , 5 + { 0x25B2, 0x0573 }, // 115, 5 + { 0x25B3, 0x0688 }, // 136, 6 + { 0x25B4, 0x061d }, // 29 , 6 + { 0x25B5, 0x06ac }, // 172, 6 + { 0x25B8, 0x061b }, // 27 , 6 + { 0x25B9, 0x068b }, // 139, 6 + { 0x25BC, 0x0574 }, // 116, 5 + { 0x25BD, 0x0689 }, // 137, 6 + { 0x25BE, 0x061e }, // 30 , 6 + { 0x25BF, 0x06ad }, // 173, 6 + { 0x25C2, 0x061c }, // 28 , 6 + { 0x25C3, 0x068a }, // 138, 6 + { 0x25C6, 0x0575 }, // 117, 5 + { 0x25C7, 0x066f }, // 111, 6 + { 0x25CA, 0x065f }, // 95 , 6 + { 0x25CB, 0x0401 }, // 1 , 4 + { 0x25CF, 0x0400 }, // 0 , 4 + { 0x25D6, 0x059e }, // 158, 5 + { 0x25D7, 0x0577 }, // 119, 5 + { 0x25D8, 0x0512 }, // 18 , 5 + { 0x25D9, 0x0513 }, // 19 , 5 + { 0x25E6, 0x042d }, // 45 , 4 + { 0x2605, 0x0548 }, // 72, 5 + { 0x260E, 0x051e }, // 30 , 5 + { 0x2610, 0x0518 }, // 24 , 5 + { 0x2612, 0x0519 }, // 25 , 5 + { 0x261B, 0x052a }, // 42 , 5 + { 0x261C, 0x0516 }, // 22 , 5 + { 0x261E, 0x052b }, // 43 , 5 + { 0x2639, 0x051a }, // 26 , 5 + { 0x263A, 0x0507 }, // 7 , 5 + { 0x263B, 0x0508 }, // 8 , 5 + { 0x263C, 0x0506 }, // 6 , 5 + { 0x2640, 0x0505 }, // 5 , 5 + { 0x2642, 0x0504 }, // 4 , 5 + { 0x2660, 0x05ab }, // 171, 5 + { 0x2661, 0x0500 }, // 0 , 5 + { 0x2662, 0x0501 }, // 1 , 5 + { 0x2663, 0x05a8 }, // 168, 5 + { 0x2664, 0x0503 }, // 3 , 5 + { 0x2665, 0x05aa }, // 170, 5 + { 0x2666, 0x05a9 }, // 169, 5 + { 0x2667, 0x0502 }, // 2 , 5 + { 0x266A, 0x0509 }, // 9 , 5 + { 0x266C, 0x050a }, // 10 , 5 + { 0x266D, 0x051c }, // 28 , 5 + { 0x266E, 0x051d }, // 29 , 5 + { 0x266F, 0x051b }, // 27 , 5 + { 0x2701, 0x0521 }, // 33 , 5 + { 0x2702, 0x0522 }, // 34 , 5 + { 0x2703, 0x0523 }, // 35 , 5 + { 0x2704, 0x0524 }, // 36 , 5 + { 0x2706, 0x0526 }, // 38 , 5 + { 0x2707, 0x0527 }, // 39 , 5 + { 0x2708, 0x0528 }, // 40 , 5 + { 0x2709, 0x0529 }, // 41 , 5 + { 0x270C, 0x052c }, // 44 , 5 + { 0x270D, 0x052d }, // 45 , 5 + { 0x270E, 0x052e }, // 46 , 5 + { 0x270F, 0x052f }, // 47 , 5 + { 0x2710, 0x0530 }, // 48 , 5 + { 0x2711, 0x0531 }, // 49 , 5 + { 0x2712, 0x0532 }, // 50 , 5 + { 0x2713, 0x0533 }, // 51 , 5 + { 0x2714, 0x0534 }, // 52 , 5 + { 0x2715, 0x0535 }, // 53 , 5 + { 0x2716, 0x0536 }, // 54 , 5 + { 0x2717, 0x0537 }, // 55 , 5 + { 0x2718, 0x0538 }, // 56 , 5 + { 0x2719, 0x0539 }, // 57 , 5 + { 0x271A, 0x053a }, // 58 , 5 + { 0x271B, 0x053b }, // 59 , 5 + { 0x271C, 0x053c }, // 60 , 5 + { 0x271D, 0x053d }, // 61 , 5 + { 0x271E, 0x053e }, // 62 , 5 + { 0x271F, 0x053f }, // 63 , 5 + { 0x2720, 0x0540 }, // 64 , 5 + { 0x2721, 0x0541 }, // 65 , 5 + { 0x2722, 0x0542 }, // 66 , 5 + { 0x2723, 0x0543 }, // 67 , 5 + { 0x2724, 0x0544 }, // 68 , 5 + { 0x2725, 0x0545 }, // 69 , 5 + { 0x2726, 0x0546 }, // 70 , 5 + { 0x2727, 0x0547 }, // 71 , 5 + { 0x2729, 0x0549 }, // 73 , 5 + { 0x272A, 0x054a }, // 74 , 5 + { 0x272B, 0x054b }, // 75 , 5 + { 0x272C, 0x054c }, // 76 , 5 + { 0x272D, 0x054d }, // 77 , 5 + { 0x272E, 0x054e }, // 78 , 5 + { 0x272F, 0x054f }, // 79 , 5 + { 0x2730, 0x0550 }, // 80 , 5 + { 0x2731, 0x0551 }, // 81 , 5 + { 0x2732, 0x0552 }, // 82 , 5 + { 0x2733, 0x0553 }, // 83 , 5 + { 0x2734, 0x0554 }, // 84 , 5 + { 0x2735, 0x0555 }, // 85 , 5 + { 0x2736, 0x0556 }, // 86 , 5 + { 0x2737, 0x0557 }, // 87 , 5 + { 0x2738, 0x0558 }, // 88 , 5 + { 0x2739, 0x0559 }, // 89 , 5 + { 0x273A, 0x055a }, // 90 , 5 + { 0x273B, 0x055b }, // 91 , 5 + { 0x273C, 0x055c }, // 92 , 5 + { 0x273D, 0x055d }, // 93 , 5 + { 0x273E, 0x055e }, // 94 , 5 + { 0x273F, 0x055f }, // 95 , 5 + { 0x2740, 0x0560 }, // 96 , 5 + { 0x2741, 0x0561 }, // 97 , 5 + { 0x2742, 0x0562 }, // 98 , 5 + { 0x2743, 0x0563 }, // 99 , 5 + { 0x2744, 0x0564 }, // 100, 5 + { 0x2745, 0x0565 }, // 101, 5 + { 0x2746, 0x0566 }, // 102, 5 + { 0x2747, 0x0567 }, // 103, 5 + { 0x2748, 0x0568 }, // 104, 5 + { 0x2749, 0x0569 }, // 105, 5 + { 0x274A, 0x056a }, // 106, 5 + { 0x274B, 0x056b }, // 107, 5 + { 0x274D, 0x056d }, // 109, 5 + { 0x274F, 0x056f }, // 111, 5 + { 0x2750, 0x0570 }, // 112, 5 + { 0x2751, 0x0571 }, // 113, 5 + { 0x2752, 0x0572 }, // 114, 5 + { 0x2756, 0x0576 }, // 118, 5 + { 0x2758, 0x0578 }, // 120, 5 + { 0x2759, 0x0579 }, // 121, 5 + { 0x275A, 0x057a }, // 122, 5 + { 0x275B, 0x057b }, // 123, 5 + { 0x275C, 0x057c }, // 124, 5 + { 0x275D, 0x057d }, // 125, 5 + { 0x275E, 0x057e }, // 126, 5 + { 0x2761, 0x05a1 }, // 161, 5 + { 0x2762, 0x05a2 }, // 162, 5 + { 0x2763, 0x05a3 }, // 163, 5 + { 0x2764, 0x05a4 }, // 164, 5 + { 0x2765, 0x05a5 }, // 165, 5 + { 0x2766, 0x05a6 }, // 166, 5 + { 0x2767, 0x05a7 }, // 167, 5 + { 0x2776, 0x05b6 }, // 182, 5 + { 0x2777, 0x05b7 }, // 183, 5 + { 0x2778, 0x05b8 }, // 184, 5 + { 0x2779, 0x05b9 }, // 185, 5 + { 0x277A, 0x05ba }, // 186, 5 + { 0x277B, 0x05bb }, // 187, 5 + { 0x277C, 0x05bc }, // 188, 5 + { 0x277D, 0x05bd }, // 189, 5 + { 0x277E, 0x05be }, // 190, 5 + { 0x277F, 0x05bf }, // 191, 5 + { 0x2780, 0x05c0 }, // 192, 5 + { 0x2781, 0x05c1 }, // 193, 5 + { 0x2782, 0x05c2 }, // 194, 5 + { 0x2783, 0x05c3 }, // 195, 5 + { 0x2784, 0x05c4 }, // 196, 5 + { 0x2785, 0x05c5 }, // 197, 5 + { 0x2786, 0x05c6 }, // 198, 5 + { 0x2787, 0x05c7 }, // 199, 5 + { 0x2788, 0x05c8 }, // 200, 5 + { 0x2789, 0x05c9 }, // 201, 5 + { 0x278A, 0x05ca }, // 202, 5 + { 0x278B, 0x05cb }, // 203, 5 + { 0x278C, 0x05cc }, // 204, 5 + { 0x278D, 0x05cd }, // 205, 5 + { 0x278E, 0x05ce }, // 206, 5 + { 0x278F, 0x05cf }, // 207, 5 + { 0x2790, 0x05d0 }, // 208, 5 + { 0x2791, 0x05d1 }, // 209, 5 + { 0x2792, 0x05d2 }, // 210, 5 + { 0x2793, 0x05d3 }, // 211, 5 + { 0x2794, 0x05d4 }, // 212, 5 + { 0x2798, 0x05d8 }, // 216, 5 + { 0x2799, 0x05d9 }, // 217, 5 + { 0x279A, 0x05da }, // 218, 5 + { 0x279B, 0x05db }, // 219, 5 + { 0x279C, 0x05dc }, // 220, 5 + { 0x279D, 0x05dd }, // 221, 5 + { 0x279E, 0x05de }, // 222, 5 + { 0x279F, 0x05df }, // 223, 5 + { 0x27A0, 0x05e0 }, // 224, 5 + { 0x27A1, 0x05e1 }, // 225, 5 + { 0x27A2, 0x05e2 }, // 226, 5 + { 0x27A3, 0x05e3 }, // 227, 5 + { 0x27A4, 0x05e4 }, // 228, 5 + { 0x27A5, 0x05e5 }, // 229, 5 + { 0x27A6, 0x05e6 }, // 230, 5 + { 0x27A7, 0x05e7 }, // 231, 5 + { 0x27A8, 0x05e8 }, // 232, 5 + { 0x27A9, 0x05e9 }, // 233, 5 + { 0x27AA, 0x05ea }, // 234, 5 + { 0x27AB, 0x05eb }, // 235, 5 + { 0x27AC, 0x05ec }, // 236, 5 + { 0x27AD, 0x05ed }, // 237, 5 + { 0x27AE, 0x05ee }, // 238, 5 + { 0x27AF, 0x05ef }, // 239, 5 + { 0x27B1, 0x05f1 }, // 241, 5 + { 0x27B2, 0x05f2 }, // 242, 5 + { 0x27B3, 0x05f3 }, // 243, 5 + { 0x27B4, 0x05f4 }, // 244, 5 + { 0x27B5, 0x05f5 }, // 245, 5 + { 0x27B6, 0x05f6 }, // 246, 5 + { 0x27B7, 0x05f7 }, // 247, 5 + { 0x27B8, 0x05f8 }, // 248, 5 + { 0x27B9, 0x05f9 }, // 249, 5 + { 0x27BA, 0x05fa }, // 250, 5 + { 0x27BB, 0x05fb }, // 251, 5 + { 0x27BC, 0x05fc }, // 252, 5 + { 0x27BD, 0x05fd }, // 253, 5 + { 0x27BE, 0x05fe }, // 254, 5 + + // Range 0xE000 through 0xF8FF is reserved for private use. + // We cannot try to interpret characters in this range nor + // assign any default collation or meaning. + + { 0xFB00, 0x0433 }, // 51 , 4 + { 0xFB01, 0x0436 }, // 54 , 4 + { 0xFB02, 0x0437 }, // 55 , 4 + { 0xFB03, 0x0434 }, // 52 , 4 + { 0xFB04, 0x0435 }, // 53 , 4 + { 0xFB1E, 0x0930 }, // 48 , 9 + { 0xFF61, 0x0b00 }, // 0 , 11 + { 0xFF62, 0x0b01 }, // 1 , 11 + { 0xFF63, 0x0b02 }, // 2 , 11 + { 0xFF64, 0x0b03 }, // 3 , 11 + { 0xFF65, 0x0b04 }, // 4 , 11 + { 0xFF66, 0x0b05 }, // 5 , 11 + { 0xFF67, 0x0b06 }, // 6 , 11 + { 0xFF68, 0x0b07 }, // 7 , 11 + { 0xFF69, 0x0b08 }, // 8 , 11 + { 0xFF6A, 0x0b09 }, // 9 , 11 + { 0xFF6B, 0x0b0a }, // 10 , 11 + { 0xFF6C, 0x0b0b }, // 11 , 11 + { 0xFF6D, 0x0b0c }, // 12 , 11 + { 0xFF6E, 0x0b0d }, // 13 , 11 + { 0xFF6F, 0x0b0e }, // 14 , 11 + { 0xFF70, 0x0b0f }, // 15 , 11 + { 0xFF71, 0x0b10 }, // 16 , 11 + { 0xFF72, 0x0b11 }, // 17 , 11 + { 0xFF73, 0x0b12 }, // 18 , 11 + { 0xFF74, 0x0b13 }, // 19 , 11 + { 0xFF75, 0x0b14 }, // 20 , 11 + { 0xFF76, 0x0b15 }, // 21 , 11 + { 0xFF77, 0x0b16 }, // 22 , 11 + { 0xFF78, 0x0b17 }, // 23 , 11 + { 0xFF79, 0x0b18 }, // 24 , 11 + { 0xFF7A, 0x0b19 }, // 25 , 11 + { 0xFF7B, 0x0b1a }, // 26 , 11 + { 0xFF7C, 0x0b1b }, // 27 , 11 + { 0xFF7D, 0x0b1c }, // 28 , 11 + { 0xFF7E, 0x0b1d }, // 29 , 11 + { 0xFF7F, 0x0b1e }, // 30 , 11 + { 0xFF80, 0x0b1f }, // 31 , 11 + { 0xFF81, 0x0b20 }, // 32 , 11 + { 0xFF82, 0x0b21 }, // 33 , 11 + { 0xFF83, 0x0b22 }, // 34 , 11 + { 0xFF84, 0x0b23 }, // 35 , 11 + { 0xFF85, 0x0b24 }, // 36 , 11 + { 0xFF86, 0x0b25 }, // 37 , 11 + { 0xFF87, 0x0b26 }, // 38 , 11 + { 0xFF88, 0x0b27 }, // 39 , 11 + { 0xFF89, 0x0b28 }, // 40 , 11 + { 0xFF8A, 0x0b29 }, // 41 , 11 + { 0xFF8B, 0x0b2a }, // 42 , 11 + { 0xFF8C, 0x0b2b }, // 43 , 11 + { 0xFF8D, 0x0b2c }, // 44 , 11 + { 0xFF8E, 0x0b2d }, // 45 , 11 + { 0xFF8F, 0x0b2e }, // 46 , 11 + { 0xFF90, 0x0b2f }, // 47 , 11 + { 0xFF91, 0x0b30 }, // 48 , 11 + { 0xFF92, 0x0b31 }, // 49 , 11 + { 0xFF93, 0x0b32 }, // 50 , 11 + { 0xFF94, 0x0b33 }, // 51 , 11 + { 0xFF95, 0x0b34 }, // 52 , 11 + { 0xFF96, 0x0b35 }, // 53 , 11 + { 0xFF97, 0x0b36 }, // 54 , 11 + { 0xFF98, 0x0b37 }, // 55 , 11 + { 0xFF99, 0x0b38 }, // 56 , 11 + { 0xFF9A, 0x0b39 }, // 57 , 11 + { 0xFF9B, 0x0b3a }, // 58 , 11 + { 0xFF9C, 0x0b3b }, // 59 , 11 + { 0xFF9D, 0x0b3c }, // 60 , 11 + { 0xFF9E, 0x0b3d }, // 61 , 11 + { 0xFF9F, 0x0b3e } // 62 , 11 +}; + +/**************************************************************************** +Desc: Encode a string into FLAIM's internal text format + (SEN-prefixed, null-terminated UTF8). The string is prefixed with + a SEN that indicates the number of characters, not including the + terminating NULL. + + This routine can be called with a NULL input buffer. If it is + called in this way, the length of the encoded string will be + returned in *puiBufLength, but no encoding will actually be + performed. +****************************************************************************/ +RCODE flmUnicode2Storage( + const FLMUNICODE * puzStr, // UNICODE string to encode + FLMUINT uiStrLen, // 0 = Unknown + FLMBYTE * pucBuf, // Destination buffer + FLMUINT * puiBufLength, // [IN ] Size of pucBuf, + // [OUT] Amount of pucBuf used + FLMUINT * puiCharCount) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEncPtr; + const FLMUNICODE * puzPtr = NULL; + FLMUINT uiMaxLen; + FLMUINT uiCharsEncoded = 0; + FLMUINT uiEncodedLen = 0; + FLMUINT uiTmp; + FLMUNICODE uChar; + FLMBYTE ucTmpSen[ 5]; + FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; + + if( !pucBuf) + { + uiMaxLen = (~(FLMUINT)0); + } + else + { + uiMaxLen = *puiBufLength; + } + + // If uiStrLen is 0, determine the number of characters. + + if( !uiStrLen) + { + uiStrLen = f_unilen( puzStr); + } + else if( puzStr[ uiStrLen] != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + if( puiCharCount) + { + *puiCharCount = uiStrLen; + } + + if( !uiStrLen) + { + // Nothing to encode + + *puiBufLength = 0; + goto Exit; + } + + pucEncPtr = pucBuf; + + // Encode the number of characters as a SEN. If pucEncPtr is + // NULL, the caller is only interested in the length of the encoded + // string, so a temporary buffer is used to call flmEncodeSEN. + + uiTmp = flmEncodeSEN( uiStrLen, &pucTmpSen); + + if( pucEncPtr) + { + if( (uiEncodedLen + uiTmp) >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( uiTmp == 1) + { + *pucEncPtr++ = ucTmpSen[ 0]; + } + else + { + f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); + pucEncPtr += uiTmp; + } + } + uiEncodedLen += uiTmp; + + // Encode the string using UTF-8 + + puzPtr = puzStr; + if( uiStrLen) + { + while( (uChar = *puzPtr) != 0) + { + if( (uiTmp = uiMaxLen - uiEncodedLen) == 0) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( uChar <= 127) + { + if( pucEncPtr) + { + *pucEncPtr++ = (FLMBYTE)uChar; + } + + uiEncodedLen++; + } + else + { + if( RC_BAD( rc = flmUni2UTF8( uChar, pucEncPtr, &uiTmp))) + { + goto Exit; + } + + if( pucEncPtr) + { + pucEncPtr += uiTmp; + } + + uiEncodedLen += uiTmp; + } + + puzPtr++; + uiCharsEncoded++; + } + + // Make sure the string length (which may have been provided by + // the caller) was correct. + + if( uiCharsEncoded != uiStrLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + } + + // Terminate the string with a 0 byte + + if( (uiMaxLen - uiEncodedLen) < 1) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( pucEncPtr) + { + *pucEncPtr++ = 0; + } + uiEncodedLen++; + + // Return the length of the encoded string + + *puiBufLength = uiEncodedLen; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Encode a string into FLAIM's internal text format + (SEN-prefixed, null-terminated UTF8). The string is prefixed with + a SEN that indicates the number of characters, not including the + terminating NULL. + + This routine can be called with a NULL input buffer. If it is + called in this way, the length of the encoded string will be + returned in *puiBufLength, but no encoding will actually be + performed. +****************************************************************************/ +RCODE flmNative2Storage( + char * pszStr, // Native string to encode + FLMUINT uiStrLen, // 0 = Unknown + FLMBYTE * pucBuf, // Destination buffer + FLMUINT * puiBufLength, // [IN ] Size of pucBuf, + // [OUT] Amount of pucBuf used + FLMUINT * puiCharCount) +{ + FLMBYTE * pucEncPtr; + char * pszPtr = NULL; + FLMUINT uiMaxLen; + FLMUINT uiCharsEncoded = 0; + FLMUINT uiEncodedLen = 0; + FLMUINT uiTmp; + FLMBYTE ucTmpSen[ 5]; + FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; + FLMBOOL bDirectCopy = FALSE; + RCODE rc = NE_XFLM_OK; + + if( !pucBuf) + { + uiMaxLen = (~(FLMUINT)0); + } + else + { + uiMaxLen = *puiBufLength; + } + + // If uiStrLen is 0, determine the number of characters. + + if( !uiStrLen) + { +#ifdef FLM_ASCII_PLATFORM + bDirectCopy = TRUE; + for( pszPtr = pszStr; *pszPtr; pszPtr++, uiStrLen++) + { + if( (FLMBYTE)*pszPtr > 0x7F) + { + bDirectCopy = FALSE; + } + } +#else + uiStrLen = f_strlen( pszStr); +#endif + } + else if( pszStr[ uiStrLen] != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + + if( puiCharCount) + { + *puiCharCount = uiStrLen; + } + + if( !uiStrLen) + { + // Nothing to encode + + *puiBufLength = 0; + goto Exit; + } + + pucEncPtr = pucBuf; + + // Encode the number of characters as a SEN. If pucEncPtr is + // NULL, the caller is only interested in the length of the encoded + // string, so a temporary buffer is used to call flmEncodeSEN. + + uiTmp = flmEncodeSEN( uiStrLen, &pucTmpSen); + if( pucEncPtr) + { + if( (uiEncodedLen + uiTmp) >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); + pucEncPtr += uiTmp; + } + uiEncodedLen += uiTmp; + + // Encode the string using UTF-8 + + if( uiStrLen) + { + if( bDirectCopy) + { + // Since all of the characters are ASCII and have + // values <= 0x7F, the string can be copied directly + // into the destination buffer buffer. + + if( uiEncodedLen + uiStrLen >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( pucEncPtr) + { + f_memcpy( pucEncPtr, pszStr, uiStrLen); + pucEncPtr += uiStrLen; + } + uiEncodedLen += uiStrLen; + } + else + { + pszPtr = pszStr; + while( *pszPtr) + { + if( (uiTmp = uiMaxLen - uiEncodedLen) == 0) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + // Convert the native character to ASCII before + // mapping it to Unicode. + + if( RC_BAD( rc = flmUni2UTF8( (FLMUNICODE)f_toascii( *pszPtr), + pucEncPtr, &uiTmp))) + { + goto Exit; + } + + uiEncodedLen += uiTmp; + + if( pucEncPtr) + { + pucEncPtr += uiTmp; + } + + pszPtr++; + uiCharsEncoded++; + } + + // Make sure the string length (which may have been provided by + // the caller) was correct. + + if( uiCharsEncoded != uiStrLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_PARM); + goto Exit; + } + } + } + + // Terminate the string with a 0 byte + + if( (uiMaxLen - uiEncodedLen) == 0) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( pucEncPtr) + { + *pucEncPtr++ = 0; + } + uiEncodedLen++; + + // Return the length of the encoded string + + *puiBufLength = uiEncodedLen; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Encode a string into FLAIM's internal text format + (SEN-prefixed, null-terminated UTF8). The string is prefixed with + a SEN that indicates the number of characters, not including the + terminating NULL. + + This routine can be called with a NULL input buffer. If it is + called in this way, the length of the encoded string will be + returned in *puiBufLength, but no encoding will actually be + performed. +****************************************************************************/ +RCODE flmUTF8ToStorage( + const FLMBYTE * pucUTF8, // UTF8 string to encode + FLMUINT uiBytesInBuffer, // Maximum bytes to process from source + FLMBYTE * pucBuf, // Destination buffer + FLMUINT * puiBufLength) // [IN ] Size of pucBuf, + // [OUT] Amount of pucBuf used +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucEncPtr; + const FLMBYTE * pucPtr = NULL; + FLMUINT uiMaxLen; + FLMUINT uiEncodedLen = 0; + FLMUINT uiCharCount; + FLMUINT uiByteCount; + FLMUINT uiTmp; + FLMUNICODE uChar; + FLMBYTE ucTmpSen[ 5]; + FLMBYTE * pucTmpSen = &ucTmpSen[ 0]; + const FLMBYTE * pucEnd = NULL; + + if( !pucBuf) + { + uiMaxLen = (~(FLMUINT)0); + } + else + { + uiMaxLen = *puiBufLength; + } + + if( uiBytesInBuffer) + { + pucEnd = &pucUTF8[ uiBytesInBuffer]; + } + + // Determine the number of bytes and characters in the + // string + + uiCharCount = 0; + pucPtr = pucUTF8; + for( ;;) + { + if( RC_BAD( rc = flmGetCharFromUTF8Buf( &pucPtr, pucEnd, &uChar))) + { + goto Exit; + } + + if( !uChar) + { + break; + } + + uiCharCount++; + } + + if( !uiCharCount) + { + *puiBufLength = 0; + goto Exit; + } + + pucEncPtr = pucBuf; + + // Encode the number of characters as a SEN. If pucEncPtr is + // NULL, the caller is only interested in the length of the encoded + // string, so a temporary buffer is used to call flmEncodeSEN. + + uiTmp = flmEncodeSEN( uiCharCount, &pucTmpSen); + if( pucEncPtr) + { + if( (uiEncodedLen + uiTmp) >= uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucEncPtr, &ucTmpSen[ 0], uiTmp); + pucEncPtr += uiTmp; + } + uiEncodedLen += uiTmp; + + // Copy the UTF8 characters into the destination buffer + + uiByteCount = (FLMUINT)(pucPtr - pucUTF8); + if( pucEncPtr) + { + if( (uiMaxLen - uiEncodedLen) < uiByteCount) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucEncPtr, pucUTF8, uiByteCount); + pucEncPtr += uiByteCount; + } + uiEncodedLen += uiByteCount; + + // Terminate the string with a 0 byte + + if( uiEncodedLen == uiMaxLen) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + if( pucEncPtr) + { + *pucEncPtr++ = 0; + } + uiEncodedLen++; + + // Return the length of the encoded string + + *puiBufLength = uiEncodedLen; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Convert text storage string to Unicode. +Notes: If puzOutBuf is NULL, only a count is returned + in puiOutBufLen to indicate the number of bytes needed to + contain the data. Two (unicode) bytes must be + added to this value to account for null termination. +****************************************************************************/ +RCODE flmStorage2Unicode( + FLMUINT uiType, + FLMUINT uiBufLength, + const FLMBYTE * pucBuffer, + FLMUINT * puiOutBufLen, + // [IN] Number of bytes available in buffer + // [OUT] Returns the number of bytes that are needed to + // represent the data. The null termination byte(s) are + // not included in this value. + void * pOutBuf) + // [IN/OUT] Buffer to hold the data. +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + FLMUINT uiOffset = 0; + FLMUINT uiSenLen; + FLMUINT uiNumChars; + FLMUINT uiMaxOutChars; + FLMBYTE ucTempBuf[ 64]; + FLMUNICODE * puzOutBuf = NULL; + FLMBYTE * pszOutBuf = NULL; + const FLMBYTE * pucEnd; + FLMUINT uiDecodeCount; + + if( !pucBuffer || !uiBufLength) + { + ucTempBuf[ 0] = 0; // SEN encoding of 0 + ucTempBuf[ 1] = 0; // String terminator + pucBuffer = &ucTempBuf[ 0]; + uiBufLength = 2; + } + else if( uiType != XFLM_TEXT_TYPE) + { + // If the value is a number, convert to text + + if( uiType == XFLM_NUMBER_TYPE) + { + FLMUINT uiTmp; + + uiTmp = sizeof( ucTempBuf); + if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength, + ucTempBuf, &uiTmp))) + { + goto Exit; + } + pucBuffer = &ucTempBuf[ 0]; + uiBufLength = uiTmp; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + if( !uiBufLength) + { + if( puiOutBufLen) + { + if( *puiOutBufLen >= 2) + { + *((FLMUNICODE *)pOutBuf) = 0; + } + + *puiOutBufLen = 0; + } + goto Exit; + } + + pucEnd = &pucBuffer[ uiBufLength]; + + uiSenLen = flmGetSENLength( *pucBuffer); + if( pucBuffer + uiSenLen >= pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucBuffer, pucEnd, &uiNumChars))) + { + goto Exit; + } + + // If only a length is needed (number of bytes), we can + // return that without parsing the string + + if( !pOutBuf) + { + uiOffset = uiNumChars; + goto Exit; + } + + flmAssert( puiOutBufLen); + uiMaxOutChars = (*puiOutBufLen) / sizeof( FLMUNICODE); + puzOutBuf = (FLMUNICODE *)pOutBuf; + + // If we have a zero-length string, jump to exit. + + if( !uiNumChars) + { + if( (pucBuffer + 1) != pucEnd || *pucBuffer != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( *pucBuffer != 0) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else if (!uiMaxOutChars) + { + goto Overflow_Error; + } + + if( puzOutBuf) + { + *puzOutBuf = 0; + } + else + { + *pszOutBuf = 0; + } + + goto Exit; + } + + // Parse through the string, outputting data to the buffer as we go. + + uChar = 0; + uiDecodeCount = 0; + for( ;;) + { + // Decode the bytes. + + if( RC_BAD( rc = flmGetCharFromUTF8Buf( &pucBuffer, pucEnd, &uChar))) + { + goto Exit; + } + + if( !uChar) + { + break; + } + + if( uiOffset == uiMaxOutChars) + { + goto Overflow_Error; + } + + if( puzOutBuf) + { + puzOutBuf[ uiOffset++] = uChar; + } + else + { + if ( uChar <= 0xFF) + { + uChar = (FLMUNICODE)f_tonative( (FLMBYTE)uChar); + pszOutBuf[ uiOffset++] = (FLMBYTE)uChar; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + uiDecodeCount++; + } + + if( uChar || uiDecodeCount != uiNumChars) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // There is room for the 0 terminating character, but we + // will not increment return length. + + if( uiOffset < uiMaxOutChars) + { + if( puzOutBuf) + { + puzOutBuf[ uiOffset] = 0; + } + else + { + pszOutBuf[ uiOffset] = 0; + } + } + else + { +Overflow_Error: + flmAssert( uiOffset == uiMaxOutChars); + + // If uiOffset is zero, so is uiMaxOutChars, which means + // that we can't even put out the zero terminator. + + if (uiOffset) + { + uiOffset--; + if( puzOutBuf) + { + puzOutBuf[ uiOffset] = 0; + } + else + { + pszOutBuf[ uiOffset] = 0; + } + } + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + +Exit: + + if( puiOutBufLen) + { + *puiOutBufLen = uiOffset + uiOffset; + } + + return( rc); +} + +/**************************************************************************** +Desc: Converts storage formats to UNICODE +****************************************************************************/ +RCODE flmStorage2Unicode( + FLMUINT uiType, + FLMUINT uiStorageLength, + const FLMBYTE * pucStorageBuffer, + IF_DynaBuf * pBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucTempBuf[ 80]; + const FLMBYTE * pucEnd; + FLMUINT uiSenLen; + FLMUINT uiNumChars; + FLMUNICODE * puzDestBuffer; + + pBuffer->truncateData( 0); + + if( uiType != XFLM_TEXT_TYPE) + { + // If the value is a number, convert to text + + if( uiType == XFLM_NUMBER_TYPE) + { + FLMUINT uiTmp; + + uiStorageLength = sizeof( ucTempBuf); + if( RC_BAD( rc = flmStorageNum2StorageText( pucStorageBuffer, + uiStorageLength, ucTempBuf, &uiTmp))) + { + goto Exit; + } + pucStorageBuffer = &ucTempBuf[ 0]; + uiStorageLength = uiTmp; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + pucEnd = &pucStorageBuffer[ uiStorageLength]; + uiSenLen = flmGetSENLength( *pucStorageBuffer); + + if( pucStorageBuffer + uiSenLen >= pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucStorageBuffer, pucEnd, &uiNumChars))) + { + goto Exit; + } + + if( RC_BAD( rc = pBuffer->allocSpace( + (uiNumChars + 1) * sizeof( FLMUNICODE), (void **)&puzDestBuffer))) + { + goto Exit; + } + + // Parse through the string outputting data to the buffer as we go + + for( ;;) + { + if( RC_BAD( rc = flmGetCharFromUTF8Buf( + &pucStorageBuffer, pucEnd, puzDestBuffer))) + { + goto Exit; + } + + if( !(*puzDestBuffer)) + { + break; + } + + puzDestBuffer++; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Converts a storage buffer to UTF-8 (null-terminated) text +****************************************************************************/ +RCODE flmStorage2UTF8( + FLMUINT uiType, + FLMUINT uiBufLength, + const FLMBYTE * pucBuffer, + FLMUINT * puiOutBufLen, + // [IN] Number of bytes available in buffer + // [OUT] Returns the number of bytes that are needed to + // represent the data. The null termination byte is not + // included in this value. + FLMBYTE * pucOutBuf) + // [OUT] Buffer to hold the data. +{ + RCODE rc = NE_XFLM_OK; + const FLMBYTE * pucEnd; + FLMBYTE ucTempBuf[ 64]; + FLMUINT uiSenLen; + + if( !pucBuffer) + { + ucTempBuf[ 0] = 0; // SEN encoding of 0 + ucTempBuf[ 1] = 0; // String terminator + pucBuffer = &ucTempBuf[ 0]; + uiBufLength = 2; + } + else if( uiType != XFLM_TEXT_TYPE) + { + // If the value is a number, convert to text + + if( uiType == XFLM_NUMBER_TYPE) + { + FLMUINT uiTmp; + + uiTmp = sizeof( ucTempBuf); + if( RC_BAD( rc = flmStorageNum2StorageText( pucBuffer, uiBufLength, + ucTempBuf, &uiTmp))) + { + goto Exit; + } + pucBuffer = &ucTempBuf[ 0]; + uiBufLength = uiTmp; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + + if( !uiBufLength) + { + if( *puiOutBufLen && pucOutBuf) + { + *pucOutBuf = 0; + } + *puiOutBufLen = 0; + goto Exit; + } + + pucEnd = &pucBuffer[ uiBufLength]; + + uiSenLen = flmGetSENLength( *pucBuffer); + if( pucBuffer + uiSenLen >= pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucBuffer, pucEnd, NULL))) + { + goto Exit; + } + + if( pucOutBuf) + { + if( *puiOutBufLen >= uiBufLength - uiSenLen) + { + f_memcpy( pucOutBuf, pucBuffer, uiBufLength - uiSenLen); + } + } + + *puiOutBufLen = (uiBufLength - uiSenLen) - 1; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads and returns the value of the SEN indicating the number of + characters encoded into the storage (UTF-8) string +****************************************************************************/ +RCODE flmGetCharCountFromStorageBuf( + const FLMBYTE ** ppucBuf, + FLMUINT uiBufSize, + FLMUINT * puiNumChars, + FLMUINT * puiSenLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSenLen; + FLMUINT uiNumChars; + + if( !uiBufSize) + { + if( puiNumChars) + { + *puiNumChars = 0; + } + + if( puiSenLen) + { + *puiSenLen = 0; + } + goto Exit; + } + + if( (uiSenLen = flmGetSENLength( (*ppucBuf)[ 0])) >= uiBufSize) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( ppucBuf, *ppucBuf + uiSenLen, &uiNumChars))) + { + goto Exit; + } + + if( puiNumChars) + { + *puiNumChars = uiNumChars; + } + + if( puiSenLen) + { + *puiSenLen = uiSenLen; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine converts an internal number to internal (ASCII) text. +Notes: If the buffer pointer is NULL, the routine just determines how + much buffer space is needed to store the number in a text string. +****************************************************************************/ +RCODE flmStorageNum2StorageText( + const FLMBYTE * pucNum, + FLMUINT uiNumLen, + FLMBYTE * pucBuffer, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num; + FLMINT64 i64Num; + FLMUINT uiOffset = 0; + FLMBOOL bNeg = FALSE; + char szTmpBuf[ 64]; + + if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen, + pucNum, &ui64Num, NULL))) + { + if( rc == NE_XFLM_CONV_NUM_UNDERFLOW) + { + if( RC_BAD( rc = flmStorage2Number64( XFLM_NUMBER_TYPE, uiNumLen, + pucNum, NULL, &i64Num))) + { + goto Exit; + } + + ui64Num = (FLMUINT64)-i64Num; + bNeg = TRUE; + } + else + { + goto Exit; + } + } + + if( bNeg) + { + szTmpBuf[ uiOffset++] = '-'; + } + + uiOffset += f_sprintf( &szTmpBuf[ uiOffset], "%I64u", ui64Num); + + if( RC_BAD( rc = flmNative2Storage( szTmpBuf, uiOffset, + pucBuffer, puiBufLen, NULL))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Called by FlmStartup, this routine initializes the Unicode to + WP and WP to Unicode mapping tables. +****************************************************************************/ +RCODE F_DbSystem::initCharMappingTables( void) +{ + FLMUINT16 * puStaticPtr; + FLMUINT uiLoop; + FLMUINT uiEntries; + FLMUINT uiOffset; + RCODE rc = NE_XFLM_OK; + + if( gv_pUnicodeToWP60 || gv_pWP60ToUnicode) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + gv_uiMinUniChar = 0; + gv_uiMaxUniChar = 0; + + gv_uiMinWPChar = 0; + gv_uiMaxWPChar = 0; + + // Make an initial pass over the table to determine + // what our allocation sizes will need to be. + + for( uiLoop = 0, puStaticPtr = (FLMUINT16 *)WP_UTOWP60; + uiLoop < UTOWP60_ENTRIES; + uiLoop++, puStaticPtr += 2) + { + // Unicode + + if( (FLMUINT)puStaticPtr[ 0] < gv_uiMinUniChar || + !gv_uiMinUniChar) + { + flmAssert( puStaticPtr[ 0] != 0); + gv_uiMinUniChar = (FLMUINT)puStaticPtr[ 0]; + } + + if( (FLMUINT)puStaticPtr[ 0] > gv_uiMaxUniChar) + { + gv_uiMaxUniChar = (FLMUINT)puStaticPtr[ 0]; + } + + // WordPerfect + + if( (FLMUINT)puStaticPtr[ 1] < gv_uiMinWPChar || + !gv_uiMinWPChar) + { + flmAssert( puStaticPtr[ 1] != 0); + gv_uiMinWPChar = (FLMUINT)puStaticPtr[ 1]; + } + + if( (FLMUINT)puStaticPtr[ 1] > gv_uiMaxWPChar) + { + gv_uiMaxWPChar = (FLMUINT)puStaticPtr[ 1]; + } + } + + // Allocate the Unicode table + + uiEntries = (gv_uiMaxUniChar - gv_uiMinUniChar) + 1; + if (RC_BAD( rc = f_calloc( uiEntries * sizeof( FLMUINT16), + &gv_pUnicodeToWP60))) + { + goto Exit; + } + + // Populate the Unicode table + + for( uiLoop = 0, puStaticPtr = (FLMUINT16 *)WP_UTOWP60; + uiLoop < UTOWP60_ENTRIES; uiLoop++, puStaticPtr += 2) + { + uiOffset = (FLMUINT)puStaticPtr[ 0] - gv_uiMinUniChar; + + flmAssert( gv_pUnicodeToWP60[ uiOffset] == 0); + gv_pUnicodeToWP60[ uiOffset] = puStaticPtr[ 1]; + } + + // Allocate the WordPerfect table + + uiEntries = (gv_uiMaxWPChar - gv_uiMinWPChar) + 1; + if (RC_BAD( rc = f_calloc( uiEntries * sizeof( FLMUINT16), + &gv_pWP60ToUnicode))) + { + goto Exit; + } + + // Populate the WordPerfect table + + for( uiLoop = 0, puStaticPtr = (FLMUINT16 *)WP_UTOWP60; + uiLoop < UTOWP60_ENTRIES; uiLoop++, puStaticPtr += 2) + { + uiOffset = (FLMUINT)puStaticPtr[ 1] - gv_uiMinWPChar; + + flmAssert( gv_pWP60ToUnicode[ uiOffset] == 0); + gv_pWP60ToUnicode[ uiOffset] = puStaticPtr[ 0]; + } + +Exit: + + if( RC_BAD( rc)) + { + if( gv_pUnicodeToWP60) + { + f_free( &gv_pUnicodeToWP60); + } + + if( gv_pWP60ToUnicode) + { + f_free( &gv_pWP60ToUnicode); + } + + gv_uiMinUniChar = 0; + gv_uiMaxUniChar = 0; + + gv_uiMinWPChar = 0; + gv_uiMaxWPChar = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: Called by FlmShutdown, this routine frees the Unicode to WP and + WP to Unicode mapping tables. +****************************************************************************/ +void F_DbSystem::freeCharMappingTables( void) +{ + if( gv_pUnicodeToWP60) + { + f_free( &gv_pUnicodeToWP60); + } + + if( gv_pWP60ToUnicode) + { + f_free( &gv_pWP60ToUnicode); + } + + gv_uiMinUniChar = 0; + gv_uiMaxUniChar = 0; + + gv_uiMinWPChar = 0; + gv_uiMaxWPChar = 0; +} diff --git a/version5/src/fvector.cpp b/version5/src/fvector.cpp new file mode 100644 index 0000000..a0a0451 --- /dev/null +++ b/version5/src/fvector.cpp @@ -0,0 +1,1765 @@ +//------------------------------------------------------------------------------ +// Desc: Contains the code for the F_DataVector class. +// +// 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: fvector.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#if defined( FLM_WATCOM_NLM) + // Disable "Warning! W549: col(XX) 'sizeof' operand contains + // compiler generated information" + #pragma warning 549 9 +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +F_DataVector::F_DataVector() +{ + m_pVectorElements = &m_VectorArray [0]; + m_uiVectorArraySize = MIN_VECTOR_ELEMENTS; + m_pucDataBuf = m_ucIntDataBuf; + m_uiDataBufLength = sizeof( m_ucIntDataBuf); + reset(); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_DataVector::~F_DataVector() +{ + if (m_pVectorElements != &m_VectorArray [0]) + { + f_free( &m_pVectorElements); + } + if (m_pucDataBuf && m_pucDataBuf != m_ucIntDataBuf) + { + f_free( &m_pucDataBuf); + } + reset(); +} + + +/**************************************************************************** +Desc: Clear the data vector, but don't free any buffers that have been + allocated. That will only happen in the destructor. The reset() + method is so that we can get efficient re-use of the vector. So, if + it has allocated buffers, etc. we don't want to free them. +****************************************************************************/ +void XFLMAPI F_DataVector::reset( void) +{ + m_ui64DocumentID = 0; + m_uiNumElements = 0; + m_uiDataBufOffset = 0; +} + +/**************************************************************************** +Desc: Make sure the vector array is allocated at least up to the element + number that is passed in. +****************************************************************************/ +RCODE F_DataVector::allocVectorArray( + FLMUINT uiElementNumber) +{ + RCODE rc = NE_XFLM_OK; + + if (uiElementNumber >= m_uiNumElements) + { + + // May need to allocate a new vector array + + if (uiElementNumber >= m_uiVectorArraySize) + { + FLMUINT uiNewArraySize = uiElementNumber + 32; + F_VECTOR_ELEMENT * pNewVector; + + if (m_pVectorElements == &m_VectorArray [0]) + { + if (RC_BAD( rc = f_alloc( uiNewArraySize * sizeof( F_VECTOR_ELEMENT), + &pNewVector))) + { + goto Exit; + } + if (m_uiNumElements) + { + f_memcpy( pNewVector, m_pVectorElements, + m_uiNumElements * sizeof( F_VECTOR_ELEMENT)); + } + } + else + { + pNewVector = m_pVectorElements; + + if (RC_BAD( rc = f_realloc( uiNewArraySize * sizeof( F_VECTOR_ELEMENT), + &pNewVector))) + { + goto Exit; + } + + } + m_pVectorElements = pNewVector; + m_uiVectorArraySize = uiNewArraySize; + } + + // Initialized everything between the old last element and + // the new element, including the new element, to zeroes. + + f_memset( &m_pVectorElements [m_uiNumElements], 0, + sizeof( F_VECTOR_ELEMENT) * + (uiElementNumber - m_uiNumElements + 1)); + + m_uiNumElements = uiElementNumber + 1; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Store a data value into its vector element. +****************************************************************************/ +RCODE F_DataVector::storeValue( + FLMINT uiElementNumber, + FLMUINT uiDataType, + const FLMBYTE * pucData, + FLMUINT uiDataLen, + FLMBYTE ** ppucDataPtr) +{ + RCODE rc = NE_XFLM_OK; + F_VECTOR_ELEMENT * pVector; + FLMBYTE * pucDataPtr; + FLMUINT uiTemp; + + // Find or allocate space for the vector + + if (RC_BAD( rc = allocVectorArray( uiElementNumber))) + { + goto Exit; + } + + pVector = &m_pVectorElements [uiElementNumber]; + + // Will the data fit inside uiDataOffset? + + if (uiDataLen <= sizeof( FLMUINT)) + { + pucDataPtr = (FLMBYTE *)&pVector->uiDataOffset; + } + else if (uiDataLen <= pVector->uiDataLength) + { + + // New data will fit in original space. Simply reuse it. + + pucDataPtr = m_pucDataBuf + pVector->uiDataOffset; + } + else + { + + // New data will not fit in originally allocated space. + // Must allocate new space. + + // Always align the new allocation so that if it gets + // reused later for binary data it will be properly aligned. + + if ((m_uiDataBufOffset & FLM_ALLOC_ALIGN) != 0) + { + uiTemp = (FLM_ALLOC_ALIGN + 1) - (m_uiDataBufOffset & FLM_ALLOC_ALIGN); + m_uiDataBufOffset += uiTemp; + } + + if (uiDataLen + m_uiDataBufOffset > m_uiDataBufLength) + { + // Re-allocate the data buffer. + + if( m_pucDataBuf == m_ucIntDataBuf) + { + if (RC_BAD( rc = f_alloc( + m_uiDataBufOffset + uiDataLen + 512, + &m_pucDataBuf))) + { + goto Exit; + } + + f_memcpy( m_pucDataBuf, m_ucIntDataBuf, m_uiDataBufOffset); + } + else + { + if (RC_BAD( rc = f_realloc( + m_uiDataBufOffset + uiDataLen + 512, + &m_pucDataBuf))) + { + goto Exit; + } + } + + m_uiDataBufLength = m_uiDataBufOffset + uiDataLen + 512; + } + pucDataPtr = m_pucDataBuf + m_uiDataBufOffset; + pVector->uiDataOffset = m_uiDataBufOffset; + m_uiDataBufOffset += uiDataLen; + } + + // Store the data - may be zero length. + + if( pucData) + { + if( uiDataLen > 1) + { + f_memcpy( pucDataPtr, pucData, uiDataLen); + } + else if( uiDataLen) + { + *pucDataPtr = *pucData; + } + } + + pVector->uiFlags |= VECT_SLOT_HAS_DATA; + pVector->uiDataLength = uiDataLen; + pVector->uiDataType = uiDataType; + + if( ppucDataPtr) + { + *ppucDataPtr = pucDataPtr; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set the id for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setID( + FLMUINT uiElementNumber, + FLMUINT64 ui64ID) +{ + RCODE rc = NE_XFLM_OK; + F_VECTOR_ELEMENT * pVector; + + // Find or allocate space for the vector element + + if (RC_BAD( rc = allocVectorArray( uiElementNumber))) + { + goto Exit; + } + pVector = &m_pVectorElements [uiElementNumber]; + + pVector->uiFlags |= VECT_SLOT_HAS_ID; + pVector->ui64ID = ui64ID; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set the name id for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setNameId( + FLMUINT uiElementNumber, + FLMUINT uiNameId, + FLMBOOL bIsAttr, + FLMBOOL bIsData) +{ + RCODE rc = NE_XFLM_OK; + F_VECTOR_ELEMENT * pVector; + + // Find or allocate space for the vector element + + if (RC_BAD( rc = allocVectorArray( uiElementNumber))) + { + goto Exit; + } + pVector = &m_pVectorElements [uiElementNumber]; + + pVector->uiFlags |= VECT_SLOT_HAS_NAME_ID; + if (bIsAttr) + { + pVector->uiFlags |= VECT_SLOT_IS_ATTR; + } + else + { + pVector->uiFlags &= (~(VECT_SLOT_IS_ATTR)); + } + if (bIsData) + { + pVector->uiFlags |= VECT_SLOT_IS_DATA; + } + else + { + pVector->uiFlags &= (~(VECT_SLOT_IS_DATA)); + } + pVector->uiNameId = uiNameId; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a FLMINT value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setINT( + FLMUINT uiElementNumber, + FLMINT iNum) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + FLMBOOL bNeg = FALSE; + + if (iNum < 0) + { + bNeg = TRUE; + iNum = -iNum; + } + + uiStorageLen = sizeof( ucStorageBuf); + if( ((FLMUINT)iNum) <= gv_uiMaxUInt32Val) + { + if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, + &uiStorageLen, ucStorageBuf, bNeg, FALSE))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmNumber64ToStorage( (FLMUINT64)iNum, + &uiStorageLen, ucStorageBuf, bNeg, FALSE))) + { + goto Exit; + } + } + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a FLMINT64 value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setINT64( + FLMUINT uiElementNumber, + FLMINT64 i64Num) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + FLMBOOL bNeg = FALSE; + + if (i64Num < 0) + { + bNeg = TRUE; + i64Num = -i64Num; + } + + uiStorageLen = sizeof( ucStorageBuf); + if (RC_BAD( rc = flmNumber64ToStorage( i64Num, &uiStorageLen, + ucStorageBuf, bNeg, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a FLMUINT value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setUINT( + FLMUINT uiElementNumber, + FLMUINT uiNum) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + + uiStorageLen = sizeof( ucStorageBuf); + if (uiNum <= gv_uiMaxUInt32Val) + { + if (RC_BAD( rc = flmNumber64ToStorage( uiNum, + &uiStorageLen, ucStorageBuf, FALSE, FALSE))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = flmNumber64ToStorage( uiNum, + &uiStorageLen, ucStorageBuf, FALSE, FALSE))) + { + goto Exit; + } + } + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a FLMUINT64 value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setUINT64( + FLMUINT uiElementNumber, + FLMUINT64 ui64Num) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucStorageBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + + uiStorageLen = sizeof( ucStorageBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Num, + &uiStorageLen, ucStorageBuf, FALSE, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_NUMBER_TYPE, ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a FLMUNICODE value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setUnicode( + FLMUINT uiElementNumber, + const FLMUNICODE * puzUnicode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucDataPtr; + FLMUINT uiLen; + FLMUINT uiCharCount; + FLMBYTE ucTmpBuf [64]; + + // A NULL or empty puzUnicode string is allowed - on those cases + // just set the data type. + + if (puzUnicode == NULL || *puzUnicode == 0) + { + rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, 0); + goto Exit; + } + + // See if it will fit in our temporary buffer on the stack. + + uiLen = sizeof( ucTmpBuf); + if (RC_OK( rc = flmUnicode2Storage( puzUnicode, 0, ucTmpBuf, + &uiLen, &uiCharCount))) + { + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_TEXT_TYPE, ucTmpBuf, uiLen))) + { + goto Exit; + } + } + else if (rc != NE_XFLM_CONV_DEST_OVERFLOW) + { + goto Exit; + } + else + { + + // Determine the length needed. + + if (RC_BAD( rc = flmUnicode2Storage( puzUnicode, 0, NULL, + &uiLen, &uiCharCount))) + { + goto Exit; + } + + // Allocate space for it in the vector and get a pointer + // back so we can then store it. + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_TEXT_TYPE, NULL, uiLen, &pucDataPtr))) + { + goto Exit; + } + + // Store it out to the space we just allocated. + + if (RC_BAD( rc = flmUnicode2Storage( puzUnicode, uiCharCount, + pucDataPtr, &uiLen, NULL))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Set a UTF8 value for a vector element. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::setUTF8( + FLMUINT uiElementNumber, + const FLMBYTE * pszUTF8, + FLMUINT uiBytesInBuffer) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucDataPtr; + FLMUINT uiLen; + FLMBYTE ucTmpBuf [64]; + + // A NULL or empty pszNative string is allowed - on those cases + // just set the data type. + + if (pszUTF8 == NULL || *pszUTF8 == 0) + { + rc = storeValue( uiElementNumber, XFLM_TEXT_TYPE, NULL, 0); + goto Exit; + } + + // See if it will fit in our temporary buffer on the stack. + + uiLen = sizeof( ucTmpBuf); + if (RC_OK( rc = flmUTF8ToStorage( + pszUTF8, uiBytesInBuffer, ucTmpBuf, &uiLen))) + { + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_TEXT_TYPE, ucTmpBuf, uiLen))) + { + goto Exit; + } + } + else if (rc != NE_XFLM_CONV_DEST_OVERFLOW) + { + goto Exit; + } + else + { + // Determine the length needed. + + if (RC_BAD( rc = flmUTF8ToStorage( + pszUTF8, uiBytesInBuffer, NULL, &uiLen))) + { + goto Exit; + } + + // Allocate space for it in the vector and get a pointer + // back so we can then store it. + + if (RC_BAD( rc = storeValue( uiElementNumber, + XFLM_TEXT_TYPE, NULL, uiLen, &pucDataPtr))) + { + goto Exit; + } + + // Store it out to the space we just allocated. + + if (RC_BAD( rc = flmUTF8ToStorage( + pszUTF8, uiBytesInBuffer, pucDataPtr, &uiLen))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Get a pointer to the UTF8 - no conversions are done. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::getUTF8Ptr( + FLMUINT uiElementNumber, + const FLMBYTE ** ppszUTF8, + FLMUINT * puiBufLen) +{ + RCODE rc = NE_XFLM_OK; + F_VECTOR_ELEMENT * pVector = getVector( uiElementNumber, + VECT_SLOT_HAS_DATA); + void * pvValue; + FLMUINT uiStorageLen; + FLMUINT uiSenLen; + + if (!pVector) + { + *ppszUTF8 = NULL; + if (puiBufLen) + { + *puiBufLen = 0; + } + goto Exit; + } + if (pVector->uiDataType != XFLM_TEXT_TYPE) + { + rc = RC_SET( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + + if ((pvValue = getDataPtr( pVector)) != NULL) + { + *ppszUTF8 = (FLMBYTE *)pvValue; + uiStorageLen = pVector->uiDataLength; + if( RC_BAD( rc = flmGetCharCountFromStorageBuf( ppszUTF8, + uiStorageLen, NULL, &uiSenLen))) + { + goto Exit; + } + + flmAssert( uiStorageLen > uiSenLen); + uiStorageLen -= uiSenLen; + } + else + { + *ppszUTF8 = NULL; + uiStorageLen = 0; + } + + if (puiBufLen) + { + *puiBufLen = uiStorageLen; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allocate data for a unicode element and retrieve it. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::getUnicode( + FLMUINT uiElementNumber, + FLMUNICODE ** ppuzUnicode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen; + + // Get the unicode length (does not include NULL terminator) + + if (RC_BAD( rc = getUnicode( uiElementNumber, NULL, &uiLen))) + { + goto Exit; + } + + if (uiLen) + { + + // Account for NULL character. + + uiLen += sizeof( FLMUNICODE); + + if( RC_BAD( rc = f_alloc( uiLen, ppuzUnicode))) + { + goto Exit; + } + + if (RC_BAD( rc = getUnicode( uiElementNumber, *ppuzUnicode, + &uiLen))) + { + goto Exit; + } + } + else + { + *ppuzUnicode = NULL; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compose a key buffer from the vector's components. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::outputKey( + IF_Db * ifpDb, + FLMUINT uiIndexNum, + FLMUINT, // uiMatchFlags, //VISIT: Need to remove this from the interface. + FLMBYTE * pucKeyBuf, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + + if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = outputKey( pIxd, XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID, pucKeyBuf, + uiKeyBufSize, puiKeyLen, 0))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compose a key buffer from the vector's components. +****************************************************************************/ +RCODE F_DataVector::outputKey( + IXD * pIxd, + FLMUINT uiMatchFlags, + FLMBYTE * pucKeyBuf, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen, + FLMUINT uiSearchKeyFlag) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd; + FLMBYTE * pucToKey; + FLMBYTE * pucKeyLenPos; + FLMUINT uiToKeyLen; + FLMUINT uiKeyLen; + FLMUINT uiKeyComponent; + FLMUINT uiDataComponent; + FLMUINT uiContextComponent; + FLMBOOL bDataTruncated; + FLMUINT uiDataType; + FLMUINT uiLanguage; + F_VECTOR_ELEMENT * pVector = NULL; + FLMBYTE ucIDBuf [256]; + FLMUINT uiIDLen = 0; + FLMUINT64 ui64Id; + FLMUINT uiMaxKeySize; + FLMUINT uiDataLen; + const FLMBYTE * pucDataPtr; + FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE * pucTmpSen; + FLMUINT uiSenLen; + FLMUINT uiIDMatchFlags = uiMatchFlags & (XFLM_MATCH_IDS | XFLM_MATCH_DOC_ID); + + if (uiIDMatchFlags) + { + pucToKey = &ucIDBuf [0]; + uiIDLen = 0; + + // Put document ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (sizeof( ucIDBuf) - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( m_ui64DocumentID, &pucToKey); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( m_ui64DocumentID, &pucTmpSen); + if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucToKey, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucToKey += uiSenLen; + } + + if (uiIDMatchFlags & XFLM_MATCH_IDS) + { + // Append the Key component NODE IDs to the key + + for (uiKeyComponent = 0; + uiKeyComponent < pIxd->uiNumKeyComponents; + uiKeyComponent++) + { + ui64Id = getID( uiKeyComponent); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (sizeof( ucIDBuf) - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucToKey); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucToKey, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucToKey += uiSenLen; + } + } + + // Append the Data NODE IDs to the key + + for (uiDataComponent = 0; + uiDataComponent < pIxd->uiNumDataComponents; + uiDataComponent++) + { + ui64Id = getID( uiDataComponent + + pIxd->uiNumKeyComponents); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (sizeof( ucIDBuf) - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucToKey); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucToKey, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucToKey += uiSenLen; + } + } + + // Append the Context NODE IDs to the key + + for (uiContextComponent = 0; + uiContextComponent < pIxd->uiNumContextComponents; + uiContextComponent++) + { + ui64Id = getID( uiContextComponent + + pIxd->uiNumKeyComponents + + pIxd->uiNumDataComponents); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (sizeof( ucIDBuf) - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucToKey); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > sizeof( ucIDBuf)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucToKey, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucToKey += uiSenLen; + } + } + } + + if (uiIDLen >= uiKeyBufSize) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + + // Output the key components + + uiMaxKeySize = uiKeyBufSize - uiIDLen; + uiLanguage = pIxd->uiLanguage; + uiKeyLen = 0; + pucToKey = pucKeyBuf; + pIcd = pIxd->pFirstKey; + for (uiKeyComponent = 0;;pIcd = pIcd->pNextKeyComponent, uiKeyComponent++) + { + pucKeyLenPos = pucToKey; + pucToKey += 2; + uiKeyLen += 2; + + uiDataType = icdGetDataType( pIcd); + + // Find matching node in the tree - if not found skip and continue. + + if ((pVector = getVector( uiKeyComponent, VECT_SLOT_HAS_DATA)) == NULL) + { + UW2FBA( 0, pucKeyLenPos); + } + else + { + uiToKeyLen = 0; + bDataTruncated = FALSE; + + // Take the dictionary number and make it the key + + if (pIcd->uiFlags & ICD_PRESENCE) + { + FLMUINT uiNum; + + // Component better be a number - and better match the + // tag number of the ICD + + if (RC_BAD( rc = getUINT( uiKeyComponent, &uiNum))) + { + goto Exit; + } + + flmAssert( uiNum == pIcd->uiDictNum || pIcd->uiDictNum == ELM_ROOT_TAG); + + // Output the tag number + + if (uiKeyLen + 4 > uiMaxKeySize) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + longToByte( (FLMUINT32)uiNum, pucToKey); + uiToKeyLen = 4; + } + else if (pIcd->uiFlags & ICD_METAPHONE) + { + FLMUINT uiMeta; + FLMBYTE ucStorageBuf[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + F_BufferIStream bufferStream; + + if (uiDataType != XFLM_TEXT_TYPE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_SYNTAX); + goto Exit; + } + + if (pVector->uiDataType == XFLM_TEXT_TYPE) + { + if (RC_BAD( rc = getUTF8Ptr( uiKeyComponent, + &pucDataPtr, &uiDataLen))) + { + goto Exit; + } + + if (RC_BAD( rc = bufferStream.open( pucDataPtr, uiDataLen))) + { + goto Exit; + } + + if (RC_BAD( rc = flmGetNextMetaphone( &bufferStream, &uiMeta))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_SYNTAX); + } + goto Exit; + } + + bufferStream.close(); + } + else if (pVector->uiDataType == XFLM_NUMBER_TYPE) + { + if( RC_BAD( rc = getUINT( uiKeyComponent, &uiMeta))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_SYNTAX); + goto Exit; + } + + if ( uiMeta) + { + uiStorageLen = FLM_MAX_NUM_BUF_SIZE; + if( RC_BAD( rc = flmNumber64ToStorage( uiMeta, + &uiStorageLen, ucStorageBuf, FALSE, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = bufferStream.open( ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + + // Output the metaphone key piece + + uiToKeyLen = uiMaxKeySize - uiKeyLen; + if( RC_BAD( rc = KYCollateValue( pucToKey, &uiToKeyLen, + &bufferStream, XFLM_NUMBER_TYPE, + pIcd->uiFlags, pIcd->uiCompareRules, pIcd->uiLimit, + NULL, NULL, uiLanguage, + FALSE, FALSE, &bDataTruncated, NULL))) + { + goto Exit; + } + } + } + else + { + F_BufferIStream bufferStream; + + if (uiDataType == XFLM_TEXT_TYPE) + { + if (RC_BAD( rc = getUTF8Ptr( uiKeyComponent, + &pucDataPtr, &uiDataLen))) + { + goto Exit; + } + } + else + { + pucDataPtr = (FLMBYTE *)getDataPtr( pVector); + uiDataLen = pVector->uiDataLength; + } + if (uiDataLen) + { + if (RC_BAD( rc = bufferStream.open( pucDataPtr, uiDataLen))) + { + goto Exit; + } + + uiToKeyLen = uiMaxKeySize - uiKeyLen; + if( RC_BAD( rc = KYCollateValue( pucToKey, &uiToKeyLen, + &bufferStream, uiDataType, + pIcd->uiFlags, pIcd->uiCompareRules, pIcd->uiLimit, + NULL, NULL, uiLanguage, + (FLMBOOL) ((pIcd->uiFlags & ICD_SUBSTRING) + ? (isLeftTruncated( pVector) + ? FALSE : TRUE) + : FALSE), + isRightTruncated( pVector), + &bDataTruncated, NULL))) + { + goto Exit; + } + } + } + + if (uiToKeyLen) + { + + // Increment total key length + + pucToKey += uiToKeyLen; + uiKeyLen += uiToKeyLen; + } + if (!bDataTruncated) + { + UW2FBA( (FLMUINT16)(uiToKeyLen | uiSearchKeyFlag), + pucKeyLenPos); + } + else + { + UW2FBA( (FLMUINT16)(uiToKeyLen | TRUNCATED_FLAG | + uiSearchKeyFlag), pucKeyLenPos); + } + } + + // Check if done. + + if (!pIcd->pNextKeyComponent) + { + break; + } + } + + // Output the node IDs, if requested. + + if (uiIDMatchFlags) + { + + // There will always be room at this point for the + // IDs - because it was subtracted out above. + + f_memcpy( pucToKey, ucIDBuf, uiIDLen); + } + *puiKeyLen = uiKeyLen + uiIDLen; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Populate a vector's components from the key part of an index key. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::inputKey( + IF_Db * ifpDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + + if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = inputKey( pIxd, pucKey, uiKeyLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Populate a vector's components from the key part of an index key. +****************************************************************************/ +RCODE F_DataVector::inputKey( + IXD * pIxd, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_XFLM_OK; + const FLMBYTE * pucKeyEnd = pucKey + uiKeyLen; + FLMBYTE ucDataBuf [MAX_KEY_SIZ]; + FLMUINT uiDataLen; + ICD * pIcd; + FLMUINT uiLanguage = pIxd->uiLanguage; + FLMUINT uiComponentLen; + FLMUINT uiDataType; + FLMBOOL bDataRightTruncated; + FLMBOOL bFirstSubstring; + FLMBOOL bIsText; + FLMUINT uiComponent; + FLMUINT64 ui64Id; + FLMUINT uiDictNumber; + + flmAssert( uiKeyLen); + + // Loop for each compound piece of key + + uiComponent = 0; + pIcd = pIxd->pFirstKey; + while (pIcd) + { + if (uiKeyLen < 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + uiComponentLen = getKeyComponentLength( pucKey); + bDataRightTruncated = isKeyComponentTruncated( pucKey); + uiKeyLen -= 2; + pucKey += 2; + + if (uiComponentLen > uiKeyLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + bFirstSubstring = FALSE; + bIsText = (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && + !(pIcd->uiFlags & (ICD_PRESENCE | ICD_METAPHONE))) + ? TRUE + : FALSE; + + uiDataType = icdGetDataType( pIcd); + uiDictNumber = pIcd->uiDictNum; + if (uiComponentLen) + { + if (pIcd->uiFlags & ICD_PRESENCE) + { + FLMUINT uiNum; + + if (uiComponentLen != 4 || bDataRightTruncated) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + uiNum = (FLMUINT)byteToLong( pucKey); + + // What is stored in the key better match the dictionary + // number of the ICD. + + if (pIcd->uiDictNum != ELM_ROOT_TAG) + { + if (uiNum != uiDictNumber) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + } + else + { + uiDictNumber = uiNum; + } + if (RC_BAD( rc = setUINT( uiComponent, uiNum))) + { + goto Exit; + } + } + else if (pIcd->uiFlags & ICD_METAPHONE) + { + uiDataLen = sizeof( ucDataBuf); + + if ( uiComponentLen) + { + if( RC_BAD( rc = flmCollationNum2StorageNum( pucKey, + uiComponentLen, ucDataBuf, &uiDataLen))) + { + goto Exit; + } + } + else + { + uiDataLen = 0; + } + + if (bDataRightTruncated) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + + // Allocate and copy value into the component. NOTE: + // storeValue handles zero length data. + + if (RC_BAD( rc = storeValue( uiComponent, XFLM_NUMBER_TYPE, + ucDataBuf, uiDataLen))) + { + goto Exit; + } + } + else + { + + // Grab only the Nth section of key if compound key + + switch (uiDataType) + { + case XFLM_TEXT_TYPE: + { + FLMBOOL bTmpTruncated = FALSE; + + if (uiComponentLen) + { + uiDataLen = sizeof( ucDataBuf); + if (RC_BAD( rc = flmColText2StorageText( pucKey, + uiComponentLen, + ucDataBuf, &uiDataLen, uiLanguage, + &bTmpTruncated, &bFirstSubstring))) + { + goto Exit; + } + + if (bTmpTruncated != bDataRightTruncated) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + } + else + { + uiDataLen = 0; + } + break; + } + + case XFLM_NUMBER_TYPE: + { + if (uiComponentLen) + { + uiDataLen = sizeof( ucDataBuf); + if( RC_BAD( rc = flmCollationNum2StorageNum( pucKey, + uiComponentLen, ucDataBuf, &uiDataLen))) + { + goto Exit; + } + } + else + { + uiDataLen = 0; + } + + if (bDataRightTruncated) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BTREE_ERROR); + goto Exit; + } + break; + } + + case XFLM_BINARY_TYPE: + { + uiDataLen = uiComponentLen; + if (uiComponentLen > sizeof( ucDataBuf)) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + if (uiComponentLen) + { + f_memcpy( ucDataBuf, pucKey, uiComponentLen); + } + break; + } + + default: + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Allocate and copy value into the component. NOTE: + // storeValue handles zero length data. + + if (RC_BAD( rc = storeValue( uiComponent, uiDataType, ucDataBuf, + uiDataLen))) + { + goto Exit; + } + + // Set first sub-string and truncated flags. + + if ((pIcd->uiFlags & ICD_SUBSTRING) && !bFirstSubstring) + { + setLeftTruncated( uiComponent); + } + if (bDataRightTruncated) + { + setRightTruncated( uiComponent); + } + } + } + else + { + if ((pIcd->uiFlags & ICD_PRESENCE) && uiDictNumber == ELM_ROOT_TAG) + { + uiDictNumber = 0; + } + } + + // Store the name ID + + if (RC_BAD( rc = setNameId( uiComponent, uiDictNumber, + (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? TRUE + : FALSE), FALSE))) + { + goto Exit; + } + + // Position to the end of this component + + flmAssert( uiKeyLen >= uiComponentLen); + pucKey += uiComponentLen; + uiKeyLen -= uiComponentLen; + uiComponent++; + + pIcd = pIcd->pNextKeyComponent; + } + + // See if we have a document ID. + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, &m_ui64DocumentID))) + { + goto Exit; + } + + // Get the node IDs for the key components, if any + + pIcd = pIxd->pFirstKey; + uiComponent = 0; + while (pIcd) + { + + // Extract the component node ID + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) + { + goto Exit; + } + + // No need to store if it is zero + + if (ui64Id) + { + if (RC_BAD( rc = setID( uiComponent, ui64Id))) + { + goto Exit; + } + } + uiComponent++; + pIcd = pIcd->pNextKeyComponent; + } + + // Get the node IDs for the data components, if any + + pIcd = pIxd->pFirstData; + while (pIcd) + { + + // Extract the component node ID + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) + { + goto Exit; + } + + // No need to store if it is zero + + if (ui64Id) + { + if (RC_BAD( rc = setID( uiComponent, ui64Id))) + { + goto Exit; + } + } + + // Store the name ID + + if (RC_BAD( rc = setNameId( uiComponent, pIcd->uiDictNum, + (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? TRUE + : FALSE), TRUE))) + { + goto Exit; + } + uiComponent++; + pIcd = pIcd->pNextDataComponent; + } + + // Get the node IDs for the context components, if any + + pIcd = pIxd->pFirstContext; + while (pIcd) + { + + // Extract the component node ID + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, &ui64Id))) + { + goto Exit; + } + + // No need to store if it is zero + + if (ui64Id) + { + if (RC_BAD( rc = setID( uiComponent, ui64Id))) + { + goto Exit; + } + } + + // Store the name ID + + if (RC_BAD( rc = setNameId( uiComponent, pIcd->uiDictNum, + (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? TRUE + : FALSE), TRUE))) + { + goto Exit; + } + uiComponent++; + pIcd = pIcd->pNextKeyComponent; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compose a data buffer from the vector's components. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::outputData( + IF_Db * ifpDb, + FLMUINT uiIndexNum, + FLMBYTE * pucDataBuf, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + + if (RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = outputData( pIxd, pucDataBuf, uiDataBufSize, puiDataLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compose a data buffer from the vector's components. +****************************************************************************/ +RCODE F_DataVector::outputData( + IXD * pIxd, + FLMBYTE * pucDataBuf, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd = pIxd->pFirstData; + FLMUINT uiDataComponent = 0; + F_VECTOR_ELEMENT * pVector; + FLMBYTE * pucData; + FLMUINT uiDataLength; + FLMUINT uiTotalLength = 0; + FLMBYTE ucTmpSen [32]; + FLMBYTE * pucTmpSen = &ucTmpSen [0]; + FLMUINT uiSENLen; + FLMUINT uiLastDataLen = 0; + + while (pIcd) + { + if ((pVector = getVector( uiDataComponent + pIxd->uiNumKeyComponents, + VECT_SLOT_HAS_DATA)) != NULL) + { + + // Cannot do data conversions right now. + + flmAssert( pVector->uiDataType == icdGetDataType( pIcd)); + uiDataLength = pVector->uiDataLength; + pucData = (FLMBYTE *)getDataPtr( pVector); + } + else + { + uiDataLength = 0; + pucData = NULL; + } + + // Output the length of the data as a SEN value + + uiSENLen = flmEncodeSEN( uiDataLength, &pucTmpSen); + if (uiTotalLength + uiSENLen > uiDataBufSize) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucDataBuf, ucTmpSen, uiSENLen); + pucDataBuf += uiSENLen; + uiTotalLength += uiSENLen; + + // Output the data + + if (uiDataLength) + { + if (uiTotalLength + uiDataLength > uiDataBufSize) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + f_memcpy( pucDataBuf, pucData, uiDataLength); + pucDataBuf += uiDataLength; + uiTotalLength += uiDataLength; + uiLastDataLen = uiTotalLength; + } + pIcd = pIcd->pNextDataComponent; + uiDataComponent++; + } + +Exit: + + // Even if rc == NE_XFLM_CONV_DEST_OVERFLOW, return a length + + *puiDataLen = uiLastDataLen; + + return( rc); +} + +/**************************************************************************** +Desc: Populate a vector's data components from the data part of a key. +****************************************************************************/ +RCODE XFLMAPI F_DataVector::inputData( + IF_Db * ifpDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucData, + FLMUINT uiInputLen) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + + if( RC_BAD( rc = ((F_Db *)ifpDb)->m_pDict->getIndex( uiIndexNum, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + if (RC_BAD( rc = inputData( pIxd, pucData, uiInputLen))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Populate a vector's data components from the data part of a key. +****************************************************************************/ +RCODE F_DataVector::inputData( + IXD * pIxd, + const FLMBYTE * pucData, + FLMUINT uiInputLen) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd = pIxd->pFirstData; + FLMUINT uiDataComponent = 0; + FLMUINT uiDataLength; + FLMUINT uiSENLen; + + while (pIcd) + { + if (!uiInputLen) + { + break; + } + + // Get the data length - it is stored as a SEN + + uiSENLen = flmGetSENLength( *pucData); + if (uiSENLen > uiInputLen) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucData, + &pucData[ uiSENLen], &uiDataLength))) + { + goto Exit; + } + + uiInputLen -= uiSENLen; + if (uiDataLength > uiInputLen) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Store the name ID + + if (RC_BAD( rc = setNameId( uiDataComponent + pIxd->uiNumKeyComponents, + pIcd->uiDictNum, + (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? TRUE + : FALSE), TRUE))) + { + goto Exit; + } + + // Store the data into the vector. + + if (RC_BAD( rc = storeValue( uiDataComponent + pIxd->uiNumKeyComponents, + icdGetDataType( pIcd), + pucData, uiDataLength, NULL))) + { + goto Exit; + } + pucData += uiDataLength; + uiInputLen -= uiDataLength; + pIcd = pIcd->pNextDataComponent; + uiDataComponent++; + } + + // Output the remaining name IDs, even if the data is missing. + + while (pIcd) + { + + // Store the name ID + + if (RC_BAD( rc = setNameId( uiDataComponent + pIxd->uiNumKeyComponents, + pIcd->uiDictNum, + (FLMBOOL)((pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? TRUE + : FALSE), TRUE))) + { + goto Exit; + } + + pIcd = pIcd->pNextDataComponent; + uiDataComponent++; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Create and empty data vector and return it's interface... +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::createIFDataVector( + IF_DataVector ** ifppDV) +{ + RCODE rc = NE_XFLM_OK; + IF_DataVector * ifpDV; + + if( (ifpDV = f_new F_DataVector) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + *ifppDV = ifpDV; + +Exit: + + return( rc); +} diff --git a/version5/src/fwin.cpp b/version5/src/fwin.cpp new file mode 100644 index 0000000..62cb493 --- /dev/null +++ b/version5/src/fwin.cpp @@ -0,0 +1,1674 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.h" +#include "ffilehdl.h" + +#if defined( FLM_WIN) + +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_XFLM_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_XFLM_IO_PATH_NOT_FOUND)); + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: + return( RC_SET( NE_XFLM_IO_ACCESS_DENIED)); + + case ERROR_BUFFER_OVERFLOW: + case ERROR_FILENAME_EXCED_RANGE: + return( RC_SET( NE_XFLM_IO_PATH_TOO_LONG)); + + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + return( RC_SET( NE_XFLM_IO_DISK_FULL)); + + case ERROR_CURRENT_DIRECTORY: + case ERROR_DIR_NOT_EMPTY: + return( RC_SET( NE_XFLM_IO_DIRECTORY_ERR)); + + case ERROR_DIRECT_ACCESS_HANDLE: + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_TARGET_HANDLE: + return( RC_SET( NE_XFLM_IO_BAD_FILE_HANDLE)); + + case ERROR_HANDLE_EOF: + return( RC_SET( NE_XFLM_IO_END_OF_FILE)); + + case ERROR_OPEN_FAILED: + return( RC_SET( NE_XFLM_IO_OPEN_ERR)); + + case ERROR_CANNOT_MAKE: + return( RC_SET( NE_XFLM_IO_PATH_CREATE_FAILURE)); + + case ERROR_LOCK_FAILED: + case ERROR_LOCK_VIOLATION: + return( RC_SET( NE_XFLM_IO_FILE_LOCK_ERR)); + + case ERROR_NEGATIVE_SEEK: + case ERROR_SEEK: + case ERROR_SEEK_ON_DEVICE: + return( RC_SET( NE_XFLM_IO_SEEK_ERR)); + + case ERROR_NO_MORE_FILES: + case ERROR_NO_MORE_SEARCH_HANDLES: + return( RC_SET( NE_XFLM_IO_NO_MORE_FILES)); + + case ERROR_TOO_MANY_OPEN_FILES: + return( RC_SET( NE_XFLM_IO_TOO_MANY_OPEN_FILES)); + + case NO_ERROR: + return( NE_XFLM_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; // Change to TRUE when we want to do async writes. + m_Overlapped.hEvent = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_FileHdl::~F_FileHdl() +{ + if( m_bFileOpened) + { + (void)Close(); + } + + if (m_pucAlignedBuff) + { + gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( m_uiAlignedBuffSize); + + (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) +{ + char szSaveFileName[ F_PATH_MAX_SIZE]; + RCODE rc = NE_XFLM_OK; + DWORD udAccessMode = 0; + DWORD udShareMode = 0; + DWORD udCreateMode = 0; + DWORD udAttrFlags = 0; + DWORD udErrCode; + + m_bDoDirectIO = (uiAccess & XFLM_IO_DIRECT) ? TRUE : FALSE; + + /* Save the file name in case we have to create the directory. */ + + if ((bCreateFlag) && (uiAccess & XFLM_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 = gv_XFlmSysData.bOkToDoAsyncWrites; + } + + // Set up the file characteristics requested by caller. + + if (uiAccess & XFLM_IO_SH_DENYRW) + { + udShareMode = 0; + uiAccess &= ~XFLM_IO_SH_DENYRW; + } + else if (uiAccess & XFLM_IO_SH_DENYWR) + { + udShareMode = FILE_SHARE_READ; + uiAccess &= ~XFLM_IO_SH_DENYWR; + } + else if (uiAccess & XFLM_IO_SH_DENYNONE) + { + udShareMode = (FILE_SHARE_READ | FILE_SHARE_WRITE); + uiAccess &= ~XFLM_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 & XFLM_IO_EXCL) + { + udCreateMode = CREATE_NEW; + } + else + { + udCreateMode = CREATE_ALWAYS; + } + } + else + { + udCreateMode = OPEN_EXISTING; + } + + udAccessMode = GENERIC_READ | GENERIC_WRITE; + + if( (!bCreateFlag) && (uiAccess & XFLM_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 & XFLM_IO_CREATE_DIR)) + { + char szTemp[ F_PATH_MAX_SIZE]; + char szDirPath[ 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, 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_XFLM_DIRECT_CREATING_FILE + : (RCODE)NE_XFLM_CREATING_FILE) + : (RCODE)(m_bDoDirectIO + ? (RCODE)NE_XFLM_DIRECT_OPENING_FILE + : (RCODE)NE_XFLM_OPENING_FILE))); + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + m_FileHandle = INVALID_HANDLE_VALUE; + } + + 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); + + 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: Create a unique file name in the specified directory +****************************************************************************/ +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 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 | XFLM_IO_EXCL); + if (rc == NE_XFLM_IO_DISK_FULL) + { + (void)_DeleteFile( szTmpPath); + 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); + } + + return( rc); +} + +/**************************************************************************** +Desc: Open a file +****************************************************************************/ +RCODE XFLMAPI F_FileHdl::Open( + 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); + + 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_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); + } + + 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; + } + + if (!CloseHandle( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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 XFLMAPI F_FileHdl::Flush( void) +{ + RCODE rc = NE_XFLM_OK; + + if( !m_bDoDirectIO) + { + if( !FlushFileBuffers( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_FLUSHING_FILE); + } + } + return( rc); +} + +/**************************************************************************** +Desc: Allocate an aligned buffer. +****************************************************************************/ +RCODE F_FileHdl::AllocAlignBuffer( void) +{ + RCODE rc = NE_XFLM_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_XFLM_MEM); + goto Exit; + } + + gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( m_uiAlignedBuffSize); + +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_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_READING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_XFLM_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_XFLM_OK; + FLMUINT uiBytesRead; + FLMBYTE * pucReadBuffer; + FLMBYTE * pucDestBuffer; + FLMUINT uiMaxBytesToRead; + FLMBOOL bHitEOF; + + flmAssert( m_bFileOpened); + + if( puiBytesReadRV) + { + *puiBytesReadRV = 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)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_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 * puiBytesReadRV) +{ + RCODE rc = NE_XFLM_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 == XFLM_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_XFLM_IO_END_OF_FILE); + goto Exit; + } + +Exit: + return( rc ); +} + +/**************************************************************************** +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; + LARGE_INTEGER liTmp; + + if( !GetFileSizeEx( m_FileHandle, &liTmp)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_GETTING_FILE_SIZE); + goto Exit; + } + + *pui64Size = liTmp.QuadPart; + +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 +WARNING: Direct IO methods are calling this method. Make sure that all changes + to this method work in direct IO mode. +****************************************************************************/ +RCODE XFLMAPI F_FileHdl::Truncate( + FLMUINT64 ui64Size) +{ + RCODE rc = NE_XFLM_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_XFLM_POSITIONING_IN_FILE); + goto Exit; + } + + // Set the new file size. + + if( !SetEndOfFile( m_FileHandle)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_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, // Must be on a sector boundary + FLMUINT uiMaxBytesToExtend, + FLMBOOL bFlush) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + pOverlapped->Offset = (DWORD)(ui64EndOfLastWrite & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64EndOfLastWrite >> 32); + + if (!ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_XFLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + // Do the write + + if( !WriteFile( m_FileHandle, m_pucAlignedBuff, + uiBytesToWrite, &uiBytesWritten, pOverlapped)) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_WRITING_FILE); + + // Don't care if it is a disk full error, because + // extending the file is optional work. + + if( rc == NE_XFLM_IO_DISK_FULL) + { + rc = NE_XFLM_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_XFLM_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_XFLM_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 == XFLM_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_XFLM_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_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_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_XFLM_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_XFLM_SETTING_UP_FOR_WRITE); + goto Exit; + } + } + + pOverlapped->Offset = (DWORD)(ui64LastWriteOffset & 0xFFFFFFFF); + pOverlapped->OffsetHigh = (DWORD)(ui64LastWriteOffset >> 32); + + if (!ResetEvent( pOverlapped->hEvent)) + { + rc = MapWinErrorToFlaim( GetLastError(), + NE_XFLM_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_XFLM_WRITING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_XFLM_WRITING_FILE); + goto Exit; + } + } + + if (uiBytesWritten < uiMaxBytesToWrite) + { + rc = RC_SET( NE_XFLM_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 XFLMAPI F_FileHdl::Write( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT * puiBytesWrittenRV) +{ + RCODE rc = NE_XFLM_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 == XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_WRITING_FILE); + goto Exit; + } + } + else + { + rc = MapWinErrorToFlaim( udErr, NE_XFLM_WRITING_FILE); + goto Exit; + } + } + + if( puiBytesWrittenRV) + { + *puiBytesWrittenRV = uiBytesWritten; + } + + m_ui64CurrentPos = ui64WriteOffset + uiBytesWritten; + + if (uiBytesWritten < uiBytesToWrite) + { + rc = RC_SET( NE_XFLM_IO_DISK_FULL); + goto Exit; + } + +Exit: + return( rc ); +} + +/**************************************************************************** +Desc: Deletes a file +****************************************************************************/ +FSTATIC RCODE _DeleteFile( + char * pszPath) +{ + RCODE rc = NE_XFLM_OK; + + if( DeleteFile( (LPTSTR)pszPath) == FALSE) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_IO_DELETING_FILE); + } + + return rc; +} + +#endif // #if defined( FLM_WIN) + +#if defined( FLM_WATCOM_NLM) + int gv_winDummy(void) + { + return( 0); + } +#endif diff --git a/version5/src/fwin.h b/version5/src/fwin.h new file mode 100644 index 0000000..8c5f1f8 --- /dev/null +++ b/version5/src/fwin.h @@ -0,0 +1,302 @@ +//------------------------------------------------------------------------------ +// Desc: This include file contains the class definitions for FLAIM's WIN +// FileHdl classes. +// +// Tabs: 3 +// +// Copyright (c) 1997-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.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FWIN_H +#define FWIN_H + +#ifdef FLM_WIN + +RCODE MapWinErrorToFlaim( + DWORD udErrCode, + RCODE defaultRc); + +// Forward references + +class F_FileHdlPage; + +/*=========================================================================== +Class: F_FileHdl +Desc: The F_FileHdl class provides support for basic IO operations + using the WIN I/O calls. +===========================================================================*/ +class F_FileHdl : public IF_FileHdl, public XF_Base +{ +public: + F_FileHdl(); // F_FileHdl Constructor + + virtual ~F_FileHdl(); // F_FileHdl Destructor - free/close this + // file handle + + // BEGINNING OF FUNCTIONS THAT MUST BE IMPLEMENTED ON ALL PLATFORMS + + RCODE XFLMAPI Close( void); // Close a file - The destructor will call this + // This is used to obtain an error code. + + RCODE XFLMAPI Create( // Create a new file. + const char * pszFileName, // File to be created + FLMUINT uiIoFlags); // Access and Mode Flags + + RCODE XFLMAPI CreateUnique( // Create a new file (with a unique file name). + const char * pszDirName, // Directory where the file is to be created + const char * pszFileExtension, // Extension to be used on the new file. + FLMUINT uiIoFlags); // Access and Mode Flags + + RCODE XFLMAPI Open( // Initiates access to an existing file. + const char * pszFileName, // File to be opened + FLMUINT uiIoFlags); // Access and Mode Flags + + RCODE XFLMAPI Flush( void); // Flushes a file's buffers to disk + + RCODE XFLMAPI Read( // Reads a buffer of data from a file + FLMUINT64 ui64Offset, // Offset to being reading at. + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesRead); // [out] number of bytes read + + RCODE XFLMAPI Seek( // Moves the current position in the file + FLMUINT64 ui64Offset, // Offset to seek to + FLMINT iWhence, // Location to apply sdwOffset to. + FLMUINT64 * pui64NewOffset); // [out] new file offset + + RCODE XFLMAPI Size( // Returns to size of the open file. + FLMUINT64 * pui64Size); // [out] size of the file + + RCODE XFLMAPI Tell( // Returns to current position of the file + // pointer in the open file. + FLMUINT64 * pui64Offset); // [out] current file position + + RCODE XFLMAPI Truncate( // Decreases the size of a file. + FLMUINT64 ui64Size); // Size to truncate the file to. + + RCODE XFLMAPI Write( // Writes a buffer of data to a file. + FLMUINT64 ui64Offset, // Offset to seek to. + FLMUINT uiLength, // Number of bytes to write. + const void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT * puiBytesWritten); // Number of bytes written. + + // Some I/O subsystems (such as direct IO) can only read and write sectors + // (512 byte chunks). If uiOffset is not on a sector boundary or + // uiLength is not an exact multiple of a sector size, the I/O system + // would have to try to read or write a partial sector - something that + // requires extra overhead, particularly for write operations - because + // in order to write a partial sector, the I/O subsystem first has to + // read the sector in to memory before writing it out in order to + // preserve the part of the sector that was not being written to. + + // The SectorRead and SectorWrite routines are provided to allow + // the caller to tell the I/O subsystem that it is OK to do full + // sector reads or writes if it needs to, because pvBuffer is + // guaranteed to be a multiple of 512 bytes big. If the I/O + // subsystem can only do sector reads and writes, it can use the + // extra buffer space in pvBuffer. When a program calls SectorWrite + // it is also telling the I/O subsystem that it does not need to + // read a partially written sector from disk before writing it out. + // It will be OK to write whatever data is in the pvBuffer to fill out + // the sector. + + FINLINE RCODE XFLMAPI SectorRead( // Allows sector reads to be done. + FLMUINT64 ui64ReadOffset, // Offset to being reading at. + FLMUINT uiBytesToRead, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesReadRV) // [out] number of bytes read + { + if (m_bDoDirectIO) + { + return( DirectRead( ui64ReadOffset, uiBytesToRead, + pvBuffer, TRUE, puiBytesReadRV)); + } + else + { + return( Read( ui64ReadOffset, uiBytesToRead, pvBuffer, puiBytesReadRV)); + } + } + + FINLINE RCODE XFLMAPI SectorWrite( // Allows sector writes to be done. + FLMUINT64 ui64WriteOffset, // Offset to seek to. + FLMUINT uiBytesToWrite, // Number of bytes to write. + const void * pvBuffer, // Buffer that contains bytes to be written + FLMUINT uiBufferSize, // Actual buffer size. + void * pvBufferObj, // Buffer object for async write + FLMUINT * puiBytesWrittenRV, // Number of bytes written. + FLMBOOL bZeroFill = TRUE) // Zero fill the buffer? + { + uiBufferSize = uiBufferSize; // Parameter is not used. + 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 XFLMAPI CanDoAsync( // Return whether or not we can do async + void) // writes. + { + return( m_bCanDoAsync); + } + + FINLINE void XFLMAPI setExtendSize( + FLMUINT uiExtendSize) + { + m_uiExtendSize = uiExtendSize; + } + + FINLINE void XFLMAPI setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) + { + m_uiMaxAutoExtendSize = uiMaxAutoExtendSize; + } + + // METHODS NEEDED TO INTERACT WITH FILE HANDLE MANAGER + + FINLINE void setupFileHdl( + FLMUINT uiFileId, + FLMBOOL bDeleteOnRelease) + { + m_uiFileId = uiFileId; + m_bDeleteOnRelease = bDeleteOnRelease; + } + + FINLINE FLMUINT getFileId( void) + { + return m_uiFileId; + } + + // END OF FUNCTIONS THAT MUST BE DEFINED FOR ALL PLATFORMS + + 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( // Open or create a file + const char * pszFileName, // Name of file to open or create. + FLMUINT uiAccess, // Access flags + FLMBOOL bCreateFlag); // Create flag + + RCODE AllocAlignBuffer( void); // Allocate an aligned buffer. + + RCODE DoOneRead( + FLMUINT64 ui64Offset, // Offset being reading at. + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesRead); // [out] number of bytes read + + RCODE DirectRead( // Reads a buffer of data from a file + FLMUINT64 uiOffset, // Offset being reading at. + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMBOOL bBuffHasFullSectors, // Buffer is sector aligned. + FLMUINT * puiBytesRead); // [out] number of bytes read + + RCODE DirectWrite( // Writes a buffer of data from a file + FLMUINT64 uiOffset, // Offset being written to. + FLMUINT uiLength, // Number of bytes to write + const void * pvBuffer, // Buffer to write from. + F_IOBuffer * pBufferObj, // Buffer object for async writes + FLMBOOL bBuffHasFullSectors, // Buffer is sector aligned. + FLMBOOL bZeroFill, + FLMUINT * puiBytesWritten); // [out] number of bytes written + + 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); + + // The following are for every platform + + F_FileHdl * m_pNext; // Next file handle in list + F_FileHdl * m_pPrev; // Prev file handle in list + FLMBOOL m_bInList; // Is this file handle in a list? + FLMBOOL m_bFileOpened; // Is the file currently opened/closed. + FLMUINT m_uiAvailTime; // Time when placed in avail list. + FLMUINT m_uiFileId; // FFILE Unique File Id + FLMBOOL m_bDeleteOnRelease; // Delete this file when it is released. + FLMBOOL m_bOpenedReadOnly; // Opened the file read only + FLMBOOL m_bOpenedExclusive; // Opened the file in exclusive mode + char * m_pszFileName; // File name for this FileHdl + + // The following are for windows platform + + HANDLE m_FileHandle; // WIN file handle + FLMUINT m_uiBlockSize; // Block size, if known. + FLMUINT m_uiBytesPerSector; // Bytes per sector for this volume. + FLMUINT64 m_ui64NotOnSectorBoundMask; + FLMUINT64 m_ui64GetSectorBoundMask; + FLMBOOL m_bDoDirectIO; // TRUE = do direct file I/O + FLMUINT m_uiExtendSize; // Size to extend by if in direct mode. + FLMUINT m_uiMaxAutoExtendSize; + // Don't do additional extending + // once file reaches this size. + FLMBYTE * m_pucAlignedBuff; + // Buffer that is aligned for doing + // direct IO. + FLMUINT m_uiAlignedBuffSize; + // Size of aligned buffer. + FLMUINT64 m_ui64CurrentPos; // Current position in file + FLMBOOL m_bCanDoAsync; // Is this handle set up to do ASYNC? + OVERLAPPED m_Overlapped; // Used when NOT doing async. + +friend class F_FileHdlPage; +friend class F_FileHdlMgr; + +}; + +#endif // #ifdef FLM_WIN + +#endif // #ifndef FWIN_H diff --git a/version5/src/fxml.cpp b/version5/src/fxml.cpp new file mode 100644 index 0000000..0d06285 --- /dev/null +++ b/version5/src/fxml.cpp @@ -0,0 +1,7167 @@ +//------------------------------------------------------------------------------ +// 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 "flaimsys.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 + +#ifdef FLM_WIN + + // VISIT: When doing a release build, we use the /O2 compiler switch, + // which turns on various optimizations. However, for some strange + // reason, the compiler (version msvc7) hangs when compiling this file + // with the /O2 switch. This pragma below essentially disables the /O2 + // switch for this file. -- This file compiled just fine under msvc6 + // without having to disable optimizations. Therefore, when the next + // version of the Microsoft compiler comes out, it would be worth + // trying it again without this pragma to see if the compiler still + // hangs. + + #pragma optimize("", off) +#endif + +// Local typedefs + +typedef struct +{ + char * pszEntity; + FLMUINT uiValue; +} CharEntity; + +// Global data + +extern FLMUNICODE gv_uzXFLAIMNamespace[]; + +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 +}; + +FSTATIC RCODE exportUniValue( + IF_OStream * pOStream, + FLMUNICODE * puzStr, + FLMUINT uiStrChars, + FLMBOOL bEncodeSpecialChars, + FLMUINT uiIndentCount); + +// Function / Method Implementations + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_XML::F_XML() +{ + m_pCharTable = NULL; +} + +/**************************************************************************** +Desc: Destructor +****************************************************************************/ +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 F_XML::buildCharTable( void) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pCharTable) + { + f_free( &m_pCharTable); + } + + if( RC_BAD( rc = f_calloc( sizeof( XMLCHAR) * 0xFFFF, &m_pCharTable))) + { + goto Exit; + } + + setCharFlag( 0x0041, 0x005A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0061, 0x007A, FLM_XML_BASE_CHAR); + setCharFlag( 0x00C0, 0x00D6, FLM_XML_BASE_CHAR); + setCharFlag( 0x00D8, 0x00F6, FLM_XML_BASE_CHAR); + setCharFlag( 0x00F8, 0x00FF, FLM_XML_BASE_CHAR); + setCharFlag( 0x0100, 0x0131, FLM_XML_BASE_CHAR); + setCharFlag( 0x0134, 0x013E, FLM_XML_BASE_CHAR); + setCharFlag( 0x0141, 0x0148, FLM_XML_BASE_CHAR); + setCharFlag( 0x014A, 0x017E, FLM_XML_BASE_CHAR); + setCharFlag( 0x0180, 0x01C3, FLM_XML_BASE_CHAR); + setCharFlag( 0x01CD, 0x01F0, FLM_XML_BASE_CHAR); + setCharFlag( 0x01F4, 0x01F5, FLM_XML_BASE_CHAR); + setCharFlag( 0x01FA, 0x0217, FLM_XML_BASE_CHAR); + setCharFlag( 0x0250, 0x02A8, FLM_XML_BASE_CHAR); + setCharFlag( 0x02BB, 0x02C1, FLM_XML_BASE_CHAR); + setCharFlag( 0x0386, 0x0386, FLM_XML_BASE_CHAR); + setCharFlag( 0x0388, 0x038A, FLM_XML_BASE_CHAR); + setCharFlag( 0x038C, 0x038C, FLM_XML_BASE_CHAR); + setCharFlag( 0x038E, 0x03A1, FLM_XML_BASE_CHAR); + setCharFlag( 0x03A3, 0x03CE, FLM_XML_BASE_CHAR); + setCharFlag( 0x03D0, 0x03D6, FLM_XML_BASE_CHAR); + setCharFlag( 0x03DA, 0x03DA, FLM_XML_BASE_CHAR); + setCharFlag( 0x03DC, 0x03DC, FLM_XML_BASE_CHAR); + setCharFlag( 0x03DE, 0x03DE, FLM_XML_BASE_CHAR); + setCharFlag( 0x03E0, 0x03E0, FLM_XML_BASE_CHAR); + setCharFlag( 0x03E2, 0x03F3, FLM_XML_BASE_CHAR); + setCharFlag( 0x0401, 0x040C, FLM_XML_BASE_CHAR); + setCharFlag( 0x040E, 0x044F, FLM_XML_BASE_CHAR); + setCharFlag( 0x0451, 0x045C, FLM_XML_BASE_CHAR); + setCharFlag( 0x045E, 0x0481, FLM_XML_BASE_CHAR); + setCharFlag( 0x0490, 0x04C4, FLM_XML_BASE_CHAR); + setCharFlag( 0x04C7, 0x04C8, FLM_XML_BASE_CHAR); + setCharFlag( 0x04CB, 0x04CC, FLM_XML_BASE_CHAR); + setCharFlag( 0x04D0, 0x04EB, FLM_XML_BASE_CHAR); + setCharFlag( 0x04EE, 0x04F5, FLM_XML_BASE_CHAR); + setCharFlag( 0x04F8, 0x04F9, FLM_XML_BASE_CHAR); + setCharFlag( 0x0531, 0x0556, FLM_XML_BASE_CHAR); + setCharFlag( 0x0559, 0x0559, FLM_XML_BASE_CHAR); + setCharFlag( 0x0561, 0x0586, FLM_XML_BASE_CHAR); + setCharFlag( 0x05D0, 0x05EA, FLM_XML_BASE_CHAR); + setCharFlag( 0x05F0, 0x05F2, FLM_XML_BASE_CHAR); + setCharFlag( 0x0621, 0x063A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0641, 0x06B7, FLM_XML_BASE_CHAR); + setCharFlag( 0x06BA, 0x06BE, FLM_XML_BASE_CHAR); + setCharFlag( 0x06C0, 0x06CE, FLM_XML_BASE_CHAR); + setCharFlag( 0x06D0, 0x06D3, FLM_XML_BASE_CHAR); + setCharFlag( 0x06D5, 0x06D5, FLM_XML_BASE_CHAR); + setCharFlag( 0x06E5, 0x06E6, FLM_XML_BASE_CHAR); + setCharFlag( 0x0905, 0x0939, FLM_XML_BASE_CHAR); + setCharFlag( 0x093D, 0x093D, FLM_XML_BASE_CHAR); + setCharFlag( 0x0958, 0x0961, FLM_XML_BASE_CHAR); + setCharFlag( 0x0985, 0x098C, FLM_XML_BASE_CHAR); + setCharFlag( 0x098F, 0x0990, FLM_XML_BASE_CHAR); + setCharFlag( 0x0993, 0x09A8, FLM_XML_BASE_CHAR); + setCharFlag( 0x09AA, 0x09B0, FLM_XML_BASE_CHAR); + setCharFlag( 0x09B2, 0x09B2, FLM_XML_BASE_CHAR); + setCharFlag( 0x09B6, 0x09B9, FLM_XML_BASE_CHAR); + setCharFlag( 0x0061, 0x007A, FLM_XML_BASE_CHAR); + setCharFlag( 0x09DC, 0x09DD, FLM_XML_BASE_CHAR); + setCharFlag( 0x09DF, 0x09E1, FLM_XML_BASE_CHAR); + setCharFlag( 0x09F0, 0x09F1, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A05, 0x0A0A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A0F, 0x0A10, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A13, 0x0A28, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A2A, 0x0A30, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A32, 0x0A33, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A35, 0x0A36, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A38, 0x0A39, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A59, 0x0A5C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A5E, 0x0A5E, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A72, 0x0A74, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A85, 0x0A8B, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A8D, 0x0A8D, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A8F, 0x0A91, FLM_XML_BASE_CHAR); + setCharFlag( 0x0A93, 0x0AA8, FLM_XML_BASE_CHAR); + setCharFlag( 0x0AAA, 0x0AB0, FLM_XML_BASE_CHAR); + setCharFlag( 0x0AB2, 0x0AB3, FLM_XML_BASE_CHAR); + setCharFlag( 0x0AB5, 0x0AB9, FLM_XML_BASE_CHAR); + setCharFlag( 0x0ABD, 0x0ABD, FLM_XML_BASE_CHAR); + setCharFlag( 0x0AE0, 0x0AE0, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B05, 0x0B0C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B0F, 0x0B10, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B13, 0x0B28, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B2A, 0x0B30, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B32, 0x0B33, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B36, 0x0B39, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B3D, 0x0B3D, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B5C, 0x0B5D, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B5F, 0x0B61, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B85, 0x0B8A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B8E, 0x0B90, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B92, 0x0B95, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B99, 0x0B9A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B9C, 0x0B9C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0B9E, 0x0B9F, FLM_XML_BASE_CHAR); + setCharFlag( 0x0BA3, 0x0BA4, FLM_XML_BASE_CHAR); + setCharFlag( 0x0BA8, 0x0BAA, FLM_XML_BASE_CHAR); + setCharFlag( 0x0BAE, 0x0BB5, FLM_XML_BASE_CHAR); + setCharFlag( 0x0BB7, 0x0BB9, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C05, 0x0C0C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C0E, 0x0C10, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C12, 0x0C28, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C2A, 0x0C33, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C35, 0x0C39, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C60, 0x0C61, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C85, 0x0C8C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C8E, 0x0C90, FLM_XML_BASE_CHAR); + setCharFlag( 0x0C92, 0x0CA8, FLM_XML_BASE_CHAR); + setCharFlag( 0x0CAA, 0x0CB3, FLM_XML_BASE_CHAR); + setCharFlag( 0x0CB5, 0x0CB9, FLM_XML_BASE_CHAR); + setCharFlag( 0x0CDE, 0x0CDE, FLM_XML_BASE_CHAR); + setCharFlag( 0x0CE0, 0x0CE1, FLM_XML_BASE_CHAR); + setCharFlag( 0x0D05, 0x0D0C, FLM_XML_BASE_CHAR); + setCharFlag( 0x0D0E, 0x0D10, FLM_XML_BASE_CHAR); + setCharFlag( 0x0D12, 0x0D28, FLM_XML_BASE_CHAR); + setCharFlag( 0x0D2A, 0x0D39, FLM_XML_BASE_CHAR); + setCharFlag( 0x0D60, 0x0D61, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E01, 0x0E2E, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E30, 0x0E30, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E32, 0x0E33, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E40, 0x0E45, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E81, 0x0E82, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E84, 0x0E84, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E87, 0x0E88, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E8A, 0x0E8A, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E8D, 0x0E8D, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E94, 0x0E97, FLM_XML_BASE_CHAR); + setCharFlag( 0x0E99, 0x0E9F, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EA1, 0x0EA3, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EA5, 0x0EA5, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EA7, 0x0EA7, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EAA, 0x0EAB, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EAD, 0x0EAE, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EB0, 0x0EB0, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EB2, 0x0EB3, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EBD, 0x0EBD, FLM_XML_BASE_CHAR); + setCharFlag( 0x0EC0, 0x0EC4, FLM_XML_BASE_CHAR); + setCharFlag( 0x0F40, 0x0F47, FLM_XML_BASE_CHAR); + setCharFlag( 0x0F49, 0x0F69, FLM_XML_BASE_CHAR); + setCharFlag( 0x10A0, 0x10C5, FLM_XML_BASE_CHAR); + setCharFlag( 0x10D0, 0x10F6, FLM_XML_BASE_CHAR); + setCharFlag( 0x1100, 0x1100, FLM_XML_BASE_CHAR); + setCharFlag( 0x1102, 0x1103, FLM_XML_BASE_CHAR); + setCharFlag( 0x1105, 0x1107, FLM_XML_BASE_CHAR); + setCharFlag( 0x1109, 0x1109, FLM_XML_BASE_CHAR); + setCharFlag( 0x110B, 0x110C, FLM_XML_BASE_CHAR); + setCharFlag( 0x110E, 0x1112, FLM_XML_BASE_CHAR); + setCharFlag( 0x113C, 0x113C, FLM_XML_BASE_CHAR); + setCharFlag( 0x113E, 0x113E, FLM_XML_BASE_CHAR); + setCharFlag( 0x1140, 0x1140, FLM_XML_BASE_CHAR); + setCharFlag( 0x114C, 0x114C, FLM_XML_BASE_CHAR); + setCharFlag( 0x114E, 0x114E, FLM_XML_BASE_CHAR); + setCharFlag( 0x1150, 0x1150, FLM_XML_BASE_CHAR); + setCharFlag( 0x1154, 0x1155, FLM_XML_BASE_CHAR); + setCharFlag( 0x1159, 0x1159, FLM_XML_BASE_CHAR); + setCharFlag( 0x115F, 0x1161, FLM_XML_BASE_CHAR); + setCharFlag( 0x1163, 0x1163, FLM_XML_BASE_CHAR); + setCharFlag( 0x1165, 0x1165, FLM_XML_BASE_CHAR); + setCharFlag( 0x1167, 0x1167, FLM_XML_BASE_CHAR); + setCharFlag( 0x1169, 0x1169, FLM_XML_BASE_CHAR); + setCharFlag( 0x116D, 0x116E, FLM_XML_BASE_CHAR); + setCharFlag( 0x1172, 0x1173, FLM_XML_BASE_CHAR); + setCharFlag( 0x1175, 0x1175, FLM_XML_BASE_CHAR); + setCharFlag( 0x119E, 0x119E, FLM_XML_BASE_CHAR); + setCharFlag( 0x11A8, 0x11A8, FLM_XML_BASE_CHAR); + setCharFlag( 0x11AB, 0x11AB, FLM_XML_BASE_CHAR); + setCharFlag( 0x11AE, 0x11AF, FLM_XML_BASE_CHAR); + setCharFlag( 0x11B7, 0x11B8, FLM_XML_BASE_CHAR); + setCharFlag( 0x11BA, 0x11BA, FLM_XML_BASE_CHAR); + setCharFlag( 0x11BC, 0x11C2, FLM_XML_BASE_CHAR); + setCharFlag( 0x11EB, 0x11EB, FLM_XML_BASE_CHAR); + setCharFlag( 0x11F0, 0x11F0, FLM_XML_BASE_CHAR); + setCharFlag( 0x11F9, 0x11F9, FLM_XML_BASE_CHAR); + setCharFlag( 0x1E00, 0x1E9B, FLM_XML_BASE_CHAR); + setCharFlag( 0x1EA0, 0x1EF9, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F00, 0x1F15, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F18, 0x1F1D, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F20, 0x1F45, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F48, 0x1F4D, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F50, 0x1F57, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F59, 0x1F59, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F5B, 0x1F5B, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F5D, 0x1F5D, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F5F, 0x1F7D, FLM_XML_BASE_CHAR); + setCharFlag( 0x1F80, 0x1FB4, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FB6, 0x1FBC, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FBE, 0x1FBE, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FC2, 0x1FC4, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FC6, 0x1FCC, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FD0, 0x1FD3, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FD6, 0x1FDB, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FE0, 0x1FEC, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FF2, 0x1FF4, FLM_XML_BASE_CHAR); + setCharFlag( 0x1FF6, 0x1FFC, FLM_XML_BASE_CHAR); + setCharFlag( 0x2126, 0x2126, FLM_XML_BASE_CHAR); + setCharFlag( 0x212A, 0x212B, FLM_XML_BASE_CHAR); + setCharFlag( 0x212E, 0x212E, FLM_XML_BASE_CHAR); + setCharFlag( 0x2180, 0x2182, FLM_XML_BASE_CHAR); + setCharFlag( 0x3041, 0x3094, FLM_XML_BASE_CHAR); + setCharFlag( 0x30A1, 0x30FA, FLM_XML_BASE_CHAR); + setCharFlag( 0x3105, 0x312C, FLM_XML_BASE_CHAR); + setCharFlag( 0xAC00, 0xD7A3, FLM_XML_BASE_CHAR); + + setCharFlag( 0x4E00, 0x9FA5, FLM_XML_IDEOGRAPHIC); + setCharFlag( 0x3007, 0x3007, FLM_XML_IDEOGRAPHIC); + setCharFlag( 0x3021, 0x3029, FLM_XML_IDEOGRAPHIC); + + setCharFlag( 0x0300, 0x0345, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0360, 0x0361, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0483, 0x0486, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0591, 0x05A1, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x05A3, 0x05B9, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x05BB, 0x05BD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x05BF, 0x05BF, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x05C1, 0x05C2, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x05C4, 0x05C4, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x064B, 0x0652, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0670, 0x0670, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x06D6, 0x06DC, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x06DD, 0x06DF, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x06E0, 0x06E4, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x06E7, 0x06E8, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x06EA, 0x06ED, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0901, 0x0903, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x093C, 0x093C, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x093E, 0x094C, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x094D, 0x094D, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0951, 0x0954, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0962, 0x0963, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0981, 0x0983, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09BC, 0x09BC, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09BE, 0x09BE, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09BF, 0x09BF, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09C0, 0x09C4, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09C7, 0x09C8, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09CB, 0x09CD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09D7, 0x09D7, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x09E2, 0x09E3, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A02, 0x0A02, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A3C, 0x0A3C, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A3E, 0x0A3E, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A3F, 0x0A3F, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A40, 0x0A42, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A47, 0x0A48, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A4B, 0x0A4D, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A70, 0x0A71, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0A81, 0x0A83, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0ABC, 0x0ABC, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0ABE, 0x0AC5, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0AC7, 0x0AC9, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0ACB, 0x0ACD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B01, 0x0B03, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B3C, 0x0B3C, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B3E, 0x0B43, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B47, 0x0B48, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B4B, 0x0B4D, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B56, 0x0B57, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0B82, 0x0B83, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0BBE, 0x0BC2, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0BC6, 0x0BC8, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0BCA, 0x0BCD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0BD7, 0x0BD7, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C01, 0x0C03, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C3E, 0x0C44, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C46, 0x0C48, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C4A, 0x0C4D, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C55, 0x0C56, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0C82, 0x0C83, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0CBE, 0x0CC4, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0CC6, 0x0CC8, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0CCA, 0x0CCD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0CD5, 0x0CD6, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0D02, 0x0D03, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0D3E, 0x0D43, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0D46, 0x0D48, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0D4A, 0x0D4D, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0D57, 0x0D57, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0E31, 0x0E31, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0E34, 0x0E3A, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0E47, 0x0E4E, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0EB1, 0x0EB1, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0EB4, 0x0EB9, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0EBB, 0x0EBC, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0EC8, 0x0ECD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F18, 0x0F19, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F35, 0x0F35, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F37, 0x0F37, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F39, 0x0F39, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F3E, 0x0F3E, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F3F, 0x0F3F, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F71, 0x0F84, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F86, 0x0F8B, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F90, 0x0F95, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F97, 0x0F97, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0F99, 0x0FAD, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0FB1, 0x0FB7, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x0FB9, 0x0FB9, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x20D0, 0x20DC, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x20E1, 0x20E1, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x302A, 0x302F, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x3099, 0x3099, FLM_XML_COMBINING_CHAR); + setCharFlag( 0x309A, 0x309A, FLM_XML_COMBINING_CHAR); + + setCharFlag( 0x0030, 0x0039, FLM_XML_DIGIT); + setCharFlag( 0x0660, 0x0669, FLM_XML_DIGIT); + setCharFlag( 0x06F0, 0x06F9, FLM_XML_DIGIT); + setCharFlag( 0x0966, 0x096F, FLM_XML_DIGIT); + setCharFlag( 0x09E6, 0x09EF, FLM_XML_DIGIT); + setCharFlag( 0x0A66, 0x0A6F, FLM_XML_DIGIT); + setCharFlag( 0x0AE6, 0x0AEF, FLM_XML_DIGIT); + setCharFlag( 0x0B66, 0x0B6F, FLM_XML_DIGIT); + setCharFlag( 0x0BE7, 0x0BEF, FLM_XML_DIGIT); + setCharFlag( 0x0C66, 0x0C6F, FLM_XML_DIGIT); + setCharFlag( 0x0CE6, 0x0CEF, FLM_XML_DIGIT); + setCharFlag( 0x0D66, 0x0D6F, FLM_XML_DIGIT); + setCharFlag( 0x0E50, 0x0E59, FLM_XML_DIGIT); + setCharFlag( 0x0ED0, 0x0ED9, FLM_XML_DIGIT); + setCharFlag( 0x0F20, 0x0F29, FLM_XML_DIGIT); + + setCharFlag( 0x00B7, 0x00B7, FLM_XML_EXTENDER); + setCharFlag( 0x02D0, 0x02D0, FLM_XML_EXTENDER); + setCharFlag( 0x02D1, 0x02D1, FLM_XML_EXTENDER); + setCharFlag( 0x0387, 0x0387, FLM_XML_EXTENDER); + setCharFlag( 0x0640, 0x0640, FLM_XML_EXTENDER); + setCharFlag( 0x0E46, 0x0E46, FLM_XML_EXTENDER); + setCharFlag( 0x0EC6, 0x0EC6, FLM_XML_EXTENDER); + setCharFlag( 0x3005, 0x3005, FLM_XML_EXTENDER); + setCharFlag( 0x3031, 0x3035, FLM_XML_EXTENDER); + setCharFlag( 0x309D, 0x309E, FLM_XML_EXTENDER); + setCharFlag( 0x30FC, 0x30FE, FLM_XML_EXTENDER); + + setCharFlag( 0x0009, 0x0009, FLM_XML_WHITESPACE); + setCharFlag( 0x000A, 0x000A, FLM_XML_WHITESPACE); + setCharFlag( 0x000D, 0x000D, FLM_XML_WHITESPACE); + setCharFlag( 0x0020, 0x0020, FLM_XML_WHITESPACE); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns TRUE if the character is a valid XML PubID character +****************************************************************************/ +FLMBOOL 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 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 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 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 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 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 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 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 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 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 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 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_XMLImport::F_XMLImport() +{ + 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_XMLImport::~F_XMLImport() +{ + 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 F_XMLImport::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 = XFLM_XML_USASCII_ENCODING; + m_pDb = NULL; + m_uiCollection = 0; + f_memset( &m_importStats, 0, sizeof( XFLM_IMPORT_STATS)); + + popNamespaces( getNamespaceCount()); + m_tmpPool.poolReset( NULL); + resetAttrList(); +} + +/**************************************************************************** +Desc: Initializes the object (allocates buffers, etc.) +****************************************************************************/ +RCODE F_XMLImport::setup( void) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( !m_bSetup); + + if( RC_BAD( rc = resizeValBuffer( 2048))) + { + goto Exit; + } + + if( RC_BAD( rc = buildCharTable())) + { + 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 F_XMLImport::import( + F_IStream * pStream, + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT uiFlags, + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + F_DOMNode ** ppNewNode, + XFLM_IMPORT_STATS * pImportStats) +{ + RCODE rc = NE_XFLM_OK; + + // Reset the state of the parser + + reset(); + + // If a root element was passed in, do some sanity checks + // before importing the XML stream + + if (pNodeToLinkTo) + { + FLMUINT uiTmp; + + if( RC_BAD( rc = pNodeToLinkTo->getCollection( pDb, &uiTmp))) + { + goto Exit; + } + + if( uiTmp != uiCollection) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + } + + m_pDb = pDb; + m_uiCollection = uiCollection; + + // 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; + } + + m_pDb = NULL; + m_uiCollection = 0; + return( rc); +} + +/**************************************************************************** +Desc: Process an XML prolog +****************************************************************************/ +RCODE F_XMLImport::processProlog( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + goto Exit; + } + + if (lineHasToken( "createNode( m_pDb, DATA_NODE, 0, + XFLM_LAST_CHILD, (IF_DOMNode **)&pData))) + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset, + XML_ERR_CREATING_DATA_NODE, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + goto Exit; + } + + switch( pParent->m_pCachedNode->getDataType()) + { + case XFLM_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 XFLM_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 XFLM_BINARY_TYPE: + { + if( RC_BAD( rc = pData->setBinary( m_pDb, pucValue, uiValueLen))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + +Exit: + + if( pData) + { + pData->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML element +****************************************************************************/ +RCODE F_XMLImport::processElement( + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + F_DOMNode ** ppNewNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bHasContent; + FLMBOOL bFlushedValue = FALSE; + FLMUINT uiChars; + FLMUINT uiOffset = 0; + FLMUNICODE uChar; + F_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; + + if( RC_BAD( rc = processSTag( pNodeToLinkTo, eInsertLoc, &bHasContent, &pElement))) + { + goto Exit; + } + if (ppNewNode) + { + *ppNewNode = pElement; + (*ppNewNode)->AddRef(); + } + + if( !bHasContent) + { + 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( pElement->m_pCachedNode->getDataType() == XFLM_TEXT_TYPE || + pElement->m_pCachedNode->getDataType() == XFLM_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_pCachedNode->getDataType() == XFLM_BINARY_TYPE) + { + ungetChar(); + + if( RC_BAD( rc = getBinaryVal( &uiOffset))) + { + goto Exit; + } + } + else if (uChar == FLM_UNICODE_AMP) + { + if( RC_BAD( rc = processReference( &uChar))) + { + goto Exit; + } + + flmAssert( uChar); + if (pElement->m_pCachedNode->getDataType() != XFLM_NODATA_TYPE) + { + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = uChar; + uiOffset += sizeof( FLMUNICODE); + uiWhitespaceStartOffset = 0; + + if( uiOffset >= m_uiValBufSize) + { + if( RC_BAD( rc = resizeValBuffer( ~((FLMUINT)0)))) + { + goto Exit; + } + } + } + } + else + { + if( pElement->m_pCachedNode->getDataType() != XFLM_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_XFLM_INVALID_XML); + goto Exit; + } + if( puzPrefix) + { + FLMUINT uiPrefixId1; + FLMUINT uiPrefixId2; + + 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_XFLM_INVALID_XML); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pElement->getPrefixId( m_pDb, &uiTmp))) + { + goto Exit; + } + + if( uiTmp) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_XFLM_INVALID_XML); + goto Exit; + } + } + + if( RC_BAD( rc = findNamespace( puzPrefix, &pNamespace))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_PREFIX_NOT_DEFINED, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_XFLM_INVALID_XML); + } + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getElementNameId( + pNamespace->getURIPtr(), puzLocal, &uiTmp))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_XFLM_INVALID_XML); + } + goto Exit; + } + + if( pElement->getNameId() != uiTmp) + { + setErrInfo( m_uiCurrLineNum, + uiSavedOffset, + XML_ERR_ELEMENT_NAME_MISMATCH, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_XFLM_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_XFLM_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_XMLImport::processSTag( + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + FLMBOOL * pbHasContent, + F_DOMNode ** ppElement) +{ + FLMUNICODE uChar; + FLMUINT uiChars; + F_DOMNode * pElement = NULL; + F_XMLNamespace * pNamespace = NULL; + FLMUNICODE * puzTmpPrefix; + FLMUNICODE * puzPrefix = NULL; + FLMUNICODE * puzTmpLocal; + FLMUNICODE * puzLocal = NULL; + FLMUINT uiNameId; + FLMUINT uiAllocSize; + void * pvMark = m_tmpPool.poolMark(); + RCODE rc = NE_XFLM_OK; + 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_XFLM_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_XFLM_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_XFLM_NOT_FOUND) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_PREFIX_NOT_DEFINED, + uiSavedFilePos, + uiSavedLineBytes); + rc = RC_SET( NE_XFLM_INVALID_XML); + } + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getElementNameId( + pNamespace->getURIPtr(), puzLocal, &uiNameId))) + { + if( rc != NE_XFLM_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_XFLM_UNDEFINED_ELEMENT_NAME); + goto Exit; + } + + // Automatically extend the schema + + uiNameId = 0; + if( RC_BAD( rc = m_pDb->createElementDef( + pNamespace->getURIPtr(), + puzLocal, XFLM_TEXT_TYPE, &uiNameId))) + { + goto Exit; + } + } + + // Create the element node + + if( pNodeToLinkTo) + { + if( RC_BAD( rc = pNodeToLinkTo->createNode( m_pDb, ELEMENT_NODE, + uiNameId, eInsertLoc, (IF_DOMNode **)&pElement))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_ELEMENT_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pDb->createRootElement( m_uiCollection, + uiNameId, (IF_DOMNode **)&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_XFLM_INVALID_XML); + goto Exit; + } + } + else + { + setErrInfo( m_uiCurrLineNum, + m_uiCurrLineOffset - 1, + XML_ERR_EXPECTING_GT, + m_uiCurrLineFilePos, + m_uiCurrLineBytes); + rc = RC_SET( NE_XFLM_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( m_pDb, 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_XMLImport::processAttributeList( void) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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_XFLM_INVALID_XML); + goto Exit; + } + else if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + + if( RC_BAD( rc = pushNamespace( + &pAttr->puzLocalName [6], pAttr->puzVal))) + { + goto Exit; + } + + uiNamespaceCount++; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML declaration +****************************************************************************/ +RCODE F_XMLImport::processXMLDecl( void) +{ + RCODE rc = NE_XFLM_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_XFLM_INVALID_XML); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes an XML document type declaration +****************************************************************************/ +RCODE F_XMLImport::processDocTypeDecl( void) +{ + RCODE rc = NE_XFLM_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_XMLImport::processNotationDecl( void) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_INVALID_XML); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Processes child content +****************************************************************************/ +RCODE F_XMLImport::processChildContent( void) +{ + FLMUNICODE uChar; + FLMUINT uiItemCount = 0; + FLMUINT uiDelimCount = 0; + FLMBOOL bChoice = FALSE; + FLMBOOL bSeq = FALSE; + RCODE rc = NE_XFLM_OK; + + // 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_XFLM_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_XFLM_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_XFLM_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_XMLImport::processMisc( void) +{ + RCODE rc = NE_XFLM_OK; + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace( FALSE))) + { + if( rc == NE_XFLM_IO_END_OF_FILE || rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_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( m_pDb, COMMENT_NODE, 0, + XFLM_LAST_CHILD, (IF_DOMNode **)&pComment))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_COMMENT_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; + + if( RC_BAD( rc = pComment->setUnicode( + m_pDb, (FLMUNICODE *)m_pucValBuf))) + { + goto Exit; + } + + pComment->Release(); + pComment = NULL; + } + +Exit: + + if( pComment) + { + pComment->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Processes a CDATA tag +****************************************************************************/ +RCODE F_XMLImport::processCDATA( + F_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes) +{ + FLMUNICODE uChar; + FLMUINT uiOffset = 0; + F_DOMNode * pCData = NULL; + RCODE rc = NE_XFLM_OK; + + // 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( m_pDb, CDATA_SECTION_NODE, 0, + XFLM_LAST_CHILD, (IF_DOMNode **)&pCData))) + { + setErrInfo( uiSavedLineNum, + uiSavedOffset, + XML_ERR_CREATING_CDATA_NODE, + uiSavedFilePos, + uiSavedLineBytes); + goto Exit; + } + + *((FLMUNICODE *)(&m_pucValBuf[ uiOffset])) = 0; + if( RC_BAD( rc = pCData->setUnicode( m_pDb, (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_XMLImport::skipWhitespace( + FLMBOOL bRequired) +{ + FLMUNICODE uChar; + FLMUINT uiCount = 0; + RCODE rc = NE_XFLM_OK; + + 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_XFLM_INVALID_XML); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_XMLImport::resizeValBuffer( + FLMUINT uiSize) +{ + RCODE rc = NE_XFLM_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_XMLImport::getBinaryVal( + FLMUINT * puiLength) +{ + RCODE rc = NE_XFLM_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_XFLM_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_XFLM_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_XFLM_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) +{ + F_XMLNamespace * pTmpNamespace = m_pFirstNamespace; + RCODE rc = NE_XFLM_OK; + + 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_XFLM_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) +{ + F_XMLNamespace * pNewNamespace = NULL; + RCODE rc = NE_XFLM_OK; + + if( (pNewNamespace = f_new F_XMLNamespace) == NULL) + { + rc = RC_SET( NE_XFLM_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_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XMLNamespace::setPrefix( + FLMUNICODE * puzPrefix) +{ + FLMUINT uiLen; + RCODE rc = NE_XFLM_OK; + + 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) +{ + FLMUINT uiLen; + RCODE rc = NE_XFLM_OK; + + 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) +{ + FLMUINT uiLen; + RCODE rc = NE_XFLM_OK; + + 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: +****************************************************************************/ +RCODE F_XMLImport::addAttributesToElement( + F_DOMNode * pElement) +{ + RCODE rc = NE_XFLM_OK; + XML_ATTR * pAttr; + F_XMLNamespace * pNamespace = NULL; + F_DOMNode * pTmpNode = NULL; + FLMUINT uiNameId; + FLMUINT uiAttrDataType; + + // 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_XFLM_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( m_pDb, ATTR_XMLNS_TAG, + (IF_DOMNode **)&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->uiFlags & F_PREFIXED_NS_DECL) + { + // Find the attribute definition + + if( RC_BAD( rc = m_pDb->getAttributeNameId( + NULL, pAttr->puzLocalName, &uiNameId))) + { + if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + + if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG)) + { + rc = RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME); + goto Exit; + } + + uiNameId = 0; + if( RC_BAD( rc = m_pDb->createAttributeDef( + NULL, pAttr->puzLocalName, XFLM_TEXT_TYPE, &uiNameId, + (IF_DOMNode **)&pTmpNode))) + { + goto Exit; + } + } + + // Add the namespace declaration to the element + + if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, + (IF_DOMNode **)&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_XFLM_NOT_FOUND) + { + setErrInfo( pAttr->uiLineNum, pAttr->uiLineOffset, + XML_ERR_PREFIX_NOT_DEFINED, pAttr->uiLineFilePos, + pAttr->uiLineBytes); + rc = RC_SET( NE_XFLM_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_XFLM_NOT_FOUND) + { + goto Exit; + } + + if( !(m_uiFlags & FLM_XML_EXTEND_DICT_FLAG) || + (pNamespace && + f_unicmp( pNamespace->getURIPtr(), gv_uzXFLAIMNamespace) == 0)) + { + rc = RC_SET( NE_XFLM_UNDEFINED_ATTRIBUTE_NAME); + goto Exit; + } + + uiNameId = 0; + if( RC_BAD( rc = m_pDb->createAttributeDef( + pNamespace ? pNamespace->getURIPtr() : NULL, + pAttr->puzLocalName, XFLM_TEXT_TYPE, &uiNameId))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pElement->createAttribute( m_pDb, uiNameId, + (IF_DOMNode **)&pTmpNode))) + { + goto Exit; + } + + if (pAttr->puzPrefix) + { + if( RC_BAD( rc = pTmpNode->setPrefix( m_pDb, pAttr->puzPrefix))) + { + if( rc == NE_XFLM_NOT_FOUND) + { + setErrInfo( pAttr->uiLineNum, + pAttr->uiLineOffset, + XML_ERR_PREFIX_NOT_DEFINED, + pAttr->uiLineFilePos, + pAttr->uiLineBytes); + rc = RC_SET( NE_XFLM_INVALID_XML); + } + goto Exit; + } + } + + if( RC_BAD( rc = pTmpNode->getDataType( m_pDb, &uiAttrDataType))) + { + goto Exit; + } + + switch( uiAttrDataType) + { + case XFLM_TEXT_TYPE: + { + if( RC_BAD( rc = pTmpNode->setUnicode( + m_pDb, pAttr->puzVal))) + { + goto Exit; + } + break; + } + + case XFLM_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( m_pDb, ui64Val))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pTmpNode->setINT64( m_pDb, -((FLMINT64)ui64Val)))) + { + goto Exit; + } + } + + break; + } + + case XFLM_BINARY_TYPE: + { + FLMBOOL bHavePreamble; + FLMUNICODE * puzStr = pAttr->puzVal; + FLMUINT uiOffset = 0; + + // Convert the Unicode value to binary + + while( puzStr && *puzStr) + { + bHavePreamble = FALSE; + + while( gv_XFlmSysData.pXml->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_XFLM_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_XFLM_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( gv_XFlmSysData.pXml->isWhitespace( *puzStr)) + { + puzStr++; + } + + if( *puzStr == FLM_UNICODE_COMMA) + { + puzStr++; + } + } + + if( RC_BAD( rc = pTmpNode->setBinary( + m_pDb, m_pucValBuf, uiOffset))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + } + } + +Exit: + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( pNamespace) + { + pNamespace->Release(); + } + + return( rc); +} + +// 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 XF_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 XF_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_XFLM_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. +*****************************************************************************/ +RCODE F_Attribute::setupAttribute( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_XFLM_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_XFLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + + if (!isXMLNS( m_puzName)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + else if (m_uiNameChars == 5) + { + m_bDefaultNamespaceDecl = TRUE; + } + else if (m_puzName [5] != ':') + { + rc = RC_SET_AND_ASSERT( NE_XFLM_INVALID_NAMESPACE_DECL); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Set the prefix for an attribute. +*****************************************************************************/ +RCODE F_Attribute::setPrefix( void) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Export a unicode string to the string buffer - as UTF8. +*****************************************************************************/ +FSTATIC RCODE exportUniValue( + IF_OStream * pOStream, + FLMUNICODE * puzStr, + FLMUINT uiStrChars, + FLMBOOL bEncodeSpecialChars, + FLMUINT uiIndentCount + ) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Output an attribute to the string buffer. +*****************************************************************************/ +RCODE F_Attribute::outputAttr( + IF_OStream * pOStream) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Allocate a new attribute. +*****************************************************************************/ +RCODE F_Element::allocAttr( + F_Attribute ** ppAttr + ) +{ + RCODE rc = NE_XFLM_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_XFLM_MEM); + goto Exit; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Save an attribute in an element. Put at end of list. +*****************************************************************************/ +RCODE F_Element::saveAttribute( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Allocate space for the element's name and namespace. +*****************************************************************************/ +RCODE F_Element::allocNameSpace( void) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Setup an element with its namespace, etc. +*****************************************************************************/ +RCODE F_Element::setupElement( + IF_Db * pDb, + IF_DOMNode * pNode + ) +{ + RCODE rc = NE_XFLM_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_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Add an attribute that is a namespace to an element. +*****************************************************************************/ +RCODE F_Element::addNamespaceDecl( + FLMUNICODE * puzPrefix, + FLMUINT uiPrefixChars, + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + F_Attribute ** ppAttr + ) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Generate a random prefix, ensure that it is not defined anywhere + in the path. +*****************************************************************************/ +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; +} + +/***************************************************************************** +Desc: Find a prefix for a namespace +*****************************************************************************/ +RCODE F_Element::findPrefix( + FLMUNICODE * puzNamespace, + FLMUINT uiNamespaceChars, + FLMBOOL bForElement, + FLMUNICODE ** ppuzPrefix, + FLMUINT * puiPrefixChars) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Output the element name, with its attributes - this marks the + beginning of the element. +*****************************************************************************/ +RCODE F_Element::outputElem( + IF_OStream * pOStream, + FLMBOOL bStartOfElement, + FLMBOOL bEndOfElement, + FLMBOOL bAddNewLine) +{ + RCODE rc = NE_XFLM_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); +} + +/***************************************************************************** +Desc: Output Data that is contained on an element node. +*****************************************************************************/ + +RCODE F_Element::outputLocalData( + IF_OStream * pOStream, + IF_DOMNode * pDbNode, + IF_Db * ifpDb, + eExportFormatType eFormatType, + FLMUINT uiIndentCount) +{ + RCODE rc = NE_XFLM_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 >= XFLM_EXPORT_INDENT_DATA ? uiIndentCount : 0))) + { + goto Exit; + } + +Exit: + + return ( rc); +} + +/***************************************************************************** +Desc: Outputs a UTF8 stream of XML, starting at the specified node. Node and + all of its descendant nodes are output. +*****************************************************************************/ +RCODE XFLMAPI F_Db::exportXML( + IF_DOMNode * pStartNode, + IF_OStream * pOStream, + eExportFormatType eFormatType) +{ + RCODE rc = NE_XFLM_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_XFLM_MEM); + goto Exit; + } + } + + pCurrElement = pTmpElement; + + if (RC_BAD( rc = pCurrElement->setupElement( (IF_Db *)this, pDbNode))) + { + goto Exit; + } + + if( eFormatType >= XFLM_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 >= XFLM_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_XFLM_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_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + // Need to close previous element + + if( uiIndentCount) + { + uiIndentCount--; + } + + if( RC_BAD( rc = pCurrElement->outputElem( pOStream, FALSE, TRUE, + eFormatType >= XFLM_EXPORT_NEW_LINE + ? TRUE + : FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = pDbNode->getParentNode( (IF_Db *)this, &pDbNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + // There should be a parent node at this point! + + rc = RC_SET_AND_ASSERT( NE_XFLM_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 >= XFLM_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 >= XFLM_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_XFLM_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_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Parent node better be an element + + if( pDbNode->getNodeType() != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_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); +} diff --git a/version5/src/fxml.h b/version5/src/fxml.h new file mode 100644 index 0000000..7b4b73a --- /dev/null +++ b/version5/src/fxml.h @@ -0,0 +1,644 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the FLAIM XML wrapper class +// +// Tabs: 3 +// +// Copyright (c) 1999-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: fxml.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FXML_H +#define FXML_H + +typedef struct xmlChar +{ + FLMBYTE ucFlags; +} XMLCHAR; + +class F_XML : public XF_RefCount, public virtual XF_Base +{ +public: + + F_XML(); + + ~F_XML(); + + FLMBOOL isPubidChar( + FLMUNICODE uChar); + + FLMBOOL isQuoteChar( + FLMUNICODE uChar); + + FLMBOOL isWhitespace( + FLMUNICODE uChar); + + FLMBOOL isExtender( + FLMUNICODE uChar); + + FLMBOOL isCombiningChar( + FLMUNICODE uChar); + + FLMBOOL isNameChar( + FLMUNICODE uChar); + + FLMBOOL isNCNameChar( + FLMUNICODE uChar); + + FLMBOOL isIdeographic( + FLMUNICODE uChar); + + FLMBOOL isBaseChar( + FLMUNICODE uChar); + + FLMBOOL isDigit( + FLMUNICODE uChar); + + FLMBOOL isLetter( + FLMUNICODE uChar); + + void setCharFlag( + FLMUNICODE uLowChar, + FLMUNICODE uHighChar, + FLMUINT16 ui16Flag); + + FLMBOOL isNameValid( + FLMUNICODE * puzName, + FLMBYTE * pszName); + + RCODE buildCharTable( void); + +private: + + XMLCHAR * m_pCharTable; +}; + +/*============================================================================ +Desc: FLAIM's XML namespace class +============================================================================*/ +class F_XMLNamespace : public XF_RefCount, public XF_Base +{ +public: + + FINLINE F_XMLNamespace() + { + m_puzPrefix = NULL; + m_puzURI = NULL; + m_pNext = NULL; + } + + 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: Namespace manager class +============================================================================*/ +class F_XMLNamespaceMgr : public XF_RefCount, public virtual XF_Base +{ +public: + + F_XMLNamespaceMgr(); + + ~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; +}; + +// Typedefs + +typedef enum +{ + XML_STATS +} eXMLStatus; + +// This callback is currently only used by the non-com utilities, +// which is why we haven't bothered to make it an interface +typedef RCODE (* XML_STATUS_HOOK)( + eXMLStatus eStatusType, + void * pvArg1, + void * pvArg2, + void * pvArg3, + void * pvUserData); + +/*============================================================================ +Desc: FLAIM's XML import class +============================================================================*/ +class F_XMLImport: public F_XML, public F_XMLNamespaceMgr +{ +public: + + F_XMLImport(); + + ~F_XMLImport(); + + RCODE setup( void); + + void reset( void); + + RCODE import( + F_IStream * pStream, + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT uiFlags, + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + F_DOMNode ** ppNewNode, + XFLM_IMPORT_STATS * pImportStats); + + FINLINE void 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 + { + FLMUNICODE uzChar = m_puzCurrLineBuf [m_uiCurrLineOffset++]; + return( uzChar); + } + } + + FINLINE FLMUNICODE peekChar( void) + { + if (m_uiCurrLineOffset == m_uiCurrLineNumChars) + { + return( (FLMUNICODE)0); + } + else + { + return( m_puzCurrLineBuf [m_uiCurrLineOffset]); + } + } + + FINLINE void ungetChar( void) + { + + // There should never be a reason to unget past the beginning of the current + // line. + + 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( + F_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes); + + RCODE processAttributeList( void); + + RCODE processComment( + F_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( + F_DOMNode * pParent, + FLMUINT uiSavedLineNum, + FLMUINT uiSavedOffset, + FLMUINT uiSavedFilePos, + FLMUINT uiSavedLineBytes); + + RCODE processElement( + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + F_DOMNode ** ppNewNode); + + RCODE unicodeToNumber64( + FLMUNICODE * puzVal, + FLMUINT64 * pui64Val, + FLMBOOL * pbNeg); + + RCODE flushElementValue( + F_DOMNode * pParent, + FLMBYTE * pucValue, + FLMUINT uiValueLen); + + RCODE getBinaryVal( + FLMUINT * puiLength); + + RCODE fixNamingTag( + F_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( + F_DOMNode * pNodeToLinkTo, + eNodeInsertLoc eInsertLoc, + FLMBOOL * pbHasContent, + F_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_XFLM_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_XFLM_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_XFLM_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_XFLM_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( + F_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; + } + + // Data + + F_Db * m_pDb; + FLMUINT m_uiCollection; + 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; + F_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; + XFLM_IMPORT_STATS m_importStats; + F_Pool m_tmpPool; + + // Attribute management + + 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); +} + +#endif // FXML_H diff --git a/version5/src/fxpath.cpp b/version5/src/fxpath.cpp new file mode 100644 index 0000000..af2e849 --- /dev/null +++ b/version5/src/fxpath.cpp @@ -0,0 +1,2479 @@ +//------------------------------------------------------------------------------ +// Desc: Methods for parsing and evaluating XPATH queries +// +// 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: fxpath.cpp 3123 2006-01-24 17:19:50 -0700 (Tue, 24 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE addCallbackFunc( + IF_Query * pQuery); + +/**************************************************************************** +Desc: Setup the tokenizer +****************************************************************************/ +RCODE F_XPathTokenizer::setup( + IF_IStream * pIStream) +{ + if( m_pIStream) + { + m_pIStream->Release(); + m_pIStream = NULL; + } + + if( pIStream) + { + m_pIStream = pIStream; + m_pIStream->AddRef(); + } + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: Skips any whitespace characters starting at the current location +****************************************************************************/ +RCODE F_XPathTokenizer::skipWhitespace( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + + for( ;;) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( !gv_XFlmSysData.pXml->isWhitespace( uChar)) + { + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + + break; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Reads the next character from the stream. + If no more characters are available, a NULL (0) character will be + returned +****************************************************************************/ +RCODE F_XPathTokenizer::getChar( + FLMUNICODE * puChar) +{ + RCODE rc = NE_XFLM_OK; + + if( m_uiUngetCount) + { + *puChar = m_uUngetBuf[ --m_uiUngetCount]; + } + else + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( m_pIStream, puChar))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + + rc = NE_XFLM_OK; + *puChar = 0; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Returns the next character that will be read from the stream. + If no more characters are available, a NULL (0) character will be + returned +****************************************************************************/ +RCODE F_XPathTokenizer::peekChar( + FLMUNICODE * puChar) +{ + RCODE rc = NE_XFLM_OK; + + if( m_uiUngetCount) + { + *puChar = m_uUngetBuf[ m_uiUngetCount - 1]; + } + else + { + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( m_pIStream, puChar))) + { + if( rc == NE_XFLM_EOF_HIT) + { + *puChar = 0; + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( RC_BAD( rc = ungetChar( *puChar))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Pushes the passed-in character onto the unget stack +****************************************************************************/ +RCODE F_XPathTokenizer::ungetChar( + FLMUNICODE uChar) +{ + RCODE rc = NE_XFLM_OK; + + if( !uChar) + { + goto Exit; + } + + if( m_uiUngetCount == XPATH_MAX_UNGET_CHARS) + { + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + + m_uUngetBuf[ m_uiUngetCount++] = uChar; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Gets the next XPATH token from the query stream +****************************************************************************/ +RCODE F_XPathTokenizer::getNextToken( + F_XPathToken * pToken) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + FLMBOOL bConsumeParens = FALSE; + + pToken->reset(); + + if( RC_BAD( rc = skipWhitespace())) + { + goto Exit; + } + + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + switch( uChar) + { + case 0: + { + pToken->m_eTokenType = END_TOKEN; + break; + } + + case FLM_UNICODE_LPAREN: + { + pToken->m_eTokenType = OP_LPAREN_TOKEN; + break; + } + + case FLM_UNICODE_RPAREN: + { + pToken->m_eTokenType = OP_RPAREN_TOKEN; + break; + } + + case FLM_UNICODE_LBRACKET: + { + pToken->m_eTokenType = OP_LBRACKET_TOKEN; + break; + } + + case FLM_UNICODE_RBRACKET: + { + pToken->m_eTokenType = OP_RBRACKET_TOKEN; + break; + } + + case FLM_UNICODE_PERIOD: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_PERIOD) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + pToken->m_eTokenType = DOUBLE_PERIOD_TOKEN; + } + else + { + pToken->m_eTokenType = PERIOD_TOKEN; + } + break; + } + + case FLM_UNICODE_ATSIGN: + { + pToken->m_eTokenType = AXIS_ATSIGN_TOKEN; + pToken->m_ui64Val = (FLMUINT64)ATTRIBUTE_AXIS; + break; + } + + case FLM_UNICODE_COMMA: + { + pToken->m_eTokenType = COMMA_TOKEN; + break; + } + + case FLM_UNICODE_COLON: + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uChar != FLM_UNICODE_COLON) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + pToken->m_eTokenType = DOUBLE_COLON_TOKEN; + break; + } + + case FLM_UNICODE_FSLASH: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_FSLASH) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + pToken->m_eTokenType = OP_DOUBLE_FSLASH_TOKEN; + } + else + { + pToken->m_eTokenType = OP_FSLASH_TOKEN; + } + break; + } + + case FLM_UNICODE_PIPE: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_PIPE) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + pToken->m_eTokenType = OP_OR_TOKEN; + } + else + { + pToken->m_eTokenType = OP_UNION_TOKEN; + } + break; + } + + case FLM_UNICODE_AMP: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_AMP) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + pToken->m_eTokenType = OP_AND_TOKEN; + } + else + { + pToken->m_eTokenType = OP_BITAND_TOKEN; + } + break; + } + + case FLM_UNICODE_PLUS: + { + pToken->m_eTokenType = OP_PLUS_TOKEN; + break; + } + + case FLM_UNICODE_HYPHEN: + { + pToken->m_eTokenType = OP_MINUS_TOKEN; + break; + } + + case FLM_UNICODE_TILDE: + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uChar != FLM_UNICODE_EQ) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + pToken->m_eTokenType = OP_APPROX_EQ_TOKEN; + break; + } + + case FLM_UNICODE_EQ: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + // Check for double equal and allow it + // in expressions + + if( uChar == FLM_UNICODE_EQ) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + } + + pToken->m_eTokenType = OP_EQ_TOKEN; + break; + } + + case FLM_UNICODE_BANG: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_EQ) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + pToken->m_eTokenType = OP_NE_TOKEN; + } + else + { + pToken->m_eTokenType = OP_NOT_TOKEN; + } + + break; + } + + case FLM_UNICODE_LT: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_EQ) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + pToken->m_eTokenType = OP_LE_TOKEN; + } + else + { + pToken->m_eTokenType = OP_LT_TOKEN; + } + break; + } + + case FLM_UNICODE_GT: + { + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_EQ) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + pToken->m_eTokenType = OP_GE_TOKEN; + } + else + { + pToken->m_eTokenType = OP_GT_TOKEN; + } + break; + } + + case FLM_UNICODE_ASTERISK: + { + if( m_eLastTokenType != UNKNOWN_TOKEN && + m_eLastTokenType != AXIS_ATSIGN_TOKEN && + m_eLastTokenType != DOUBLE_COLON_TOKEN && + m_eLastTokenType != OP_LPAREN_TOKEN && + m_eLastTokenType != OP_LBRACKET_TOKEN && + m_eLastTokenType != COMMA_TOKEN && + !isOperator( m_eLastTokenType)) + { + pToken->m_eTokenType = OP_MULT_TOKEN; + } + else + { + pToken->m_eTokenType = NAME_TEST_WILD_TOKEN; + } + break; + } + + case FLM_UNICODE_DOLLAR: + { + if( RC_BAD( rc = getName( pToken))) + { + goto Exit; + } + + pToken->m_eTokenType = VAR_REF_TOKEN; + break; + } + + case FLM_UNICODE_0: + case FLM_UNICODE_1: + case FLM_UNICODE_2: + case FLM_UNICODE_3: + case FLM_UNICODE_4: + case FLM_UNICODE_5: + case FLM_UNICODE_6: + case FLM_UNICODE_7: + case FLM_UNICODE_8: + case FLM_UNICODE_9: + { + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + + if( RC_BAD( rc = getNumber( pToken))) + { + goto Exit; + } + + pToken->m_eTokenType = NUMBER_TOKEN; + break; + } + + case FLM_UNICODE_APOS: + case FLM_UNICODE_QUOTE: + { + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + + if( RC_BAD( rc = getLiteral( pToken))) + { + goto Exit; + } + + pToken->m_eTokenType = LITERAL_TOKEN; + break; + } + + case FLM_UNICODE_LBRACE: + { + pToken->m_eTokenType = LBRACE_TOKEN; + break; + } + + case FLM_UNICODE_RBRACE: + { + pToken->m_eTokenType = RBRACE_TOKEN; + break; + } + + default: + { + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + + if( RC_BAD( rc = getName( pToken))) + { + goto Exit; + } + + if( pToken->m_puzPrefix) + { + pToken->m_eTokenType = NAME_TEST_QNAME_TOKEN; + goto Exit; + } + + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + // Node type or function name? + + if( uChar == FLM_UNICODE_LPAREN) + { + switch( pToken->m_puzLocal[ 0]) + { + case FLM_UNICODE_b: + if( f_uninativecmp( pToken->m_puzLocal, "binary") == 0) + { + pToken->m_eTokenType = BINARY_TOKEN; + if (RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + if( RC_BAD( rc = skipWhitespace())) + { + goto Exit; + } + if (RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + if (uChar != FLM_UNICODE_APOS && uChar != FLM_UNICODE_QUOTE) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + if( RC_BAD( rc = getBinary( pToken))) + { + goto Exit; + } + if( RC_BAD( rc = skipWhitespace())) + { + goto Exit; + } + if (RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + if (uChar != FLM_UNICODE_RPAREN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_c: + if( f_uninativecmp( pToken->m_puzLocal, "ceiling") == 0) + { + pToken->m_eTokenType = FUNC_CEILING_TOKEN; + } + if( f_uninativecmp( pToken->m_puzLocal, "comment") == 0) + { + pToken->m_eTokenType = NODE_TYPE_COMMENT_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "concat") == 0) + { + pToken->m_eTokenType = FUNC_CONCAT_TOKEN; + } + else if( f_uninativecmp( + pToken->m_puzLocal, "contains") == 0) + { + pToken->m_eTokenType = FUNC_CONTAINS_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "count") == 0) + { + pToken->m_eTokenType = FUNC_COUNT_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_f: + if( f_uninativecmp( pToken->m_puzLocal, "false") == 0) + { + pToken->m_eTokenType = FUNC_FALSE_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "floor") == 0) + { + pToken->m_eTokenType = FUNC_FLOOR_TOKEN; + } + else if (f_uninativecmp( pToken->m_puzLocal, + "funcCB") == 0) + { + pToken->m_eTokenType = FUNC_CB_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_i: + if( f_uninativecmp( pToken->m_puzLocal, "id") == 0) + { + pToken->m_eTokenType = FUNC_ID_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_l: + if( f_uninativecmp( pToken->m_puzLocal, "lang") == 0) + { + pToken->m_eTokenType = FUNC_LANG_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "last") == 0) + { + pToken->m_eTokenType = FUNC_LAST_TOKEN; + } + else if( f_uninativecmp( + pToken->m_puzLocal, "local-name") == 0) + { + pToken->m_eTokenType = FUNC_LOCAL_NAME_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_n: + if( f_uninativecmp( pToken->m_puzLocal, "name") == 0) + { + pToken->m_eTokenType = FUNC_NAME_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "namespace-uri") == 0) + { + pToken->m_eTokenType = FUNC_NAMESPACE_URI_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "node") == 0) + { + pToken->m_eTokenType = NODE_TYPE_NODE_TOKEN; + bConsumeParens = TRUE; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "normalize-space") == 0) + { + pToken->m_eTokenType = FUNC_NORM_SPACE_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "not") == 0) + { + pToken->m_eTokenType = FUNC_NOT_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "number") == 0) + { + pToken->m_eTokenType = FUNC_NUMBER_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_p: + if( f_uninativecmp( pToken->m_puzLocal, "position") == 0) + { + pToken->m_eTokenType = FUNC_POSITION_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "processing-instruction") == 0) + { + pToken->m_eTokenType = NODE_TYPE_PI_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_r: + if( f_uninativecmp( pToken->m_puzLocal, + "round") == 0) + { + pToken->m_eTokenType = FUNC_ROUND_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_s: + if( f_uninativecmp( pToken->m_puzLocal, + "starts-with") == 0) + { + pToken->m_eTokenType = FUNC_STARTS_WITH_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "string") == 0) + { + pToken->m_eTokenType = FUNC_STRING_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "string-length") == 0) + { + pToken->m_eTokenType = FUNC_STR_LEN_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "substring") == 0) + { + pToken->m_eTokenType = FUNC_SUBSTR_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "substring-after") == 0) + { + pToken->m_eTokenType = FUNC_SUBSTR_AFTER_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "substring-before") == 0) + { + pToken->m_eTokenType = FUNC_SUBSTR_BEFORE_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, "sum") == 0) + { + pToken->m_eTokenType = FUNC_SUM_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_t: + if( f_uninativecmp( pToken->m_puzLocal, + "text") == 0) + { + pToken->m_eTokenType = NODE_TYPE_TEXT_TOKEN; + bConsumeParens = TRUE; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "translate") == 0) + { + pToken->m_eTokenType = FUNC_TRANSLATE_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "true") == 0) + { + pToken->m_eTokenType = FUNC_TRUE_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + case FLM_UNICODE_u: + if( f_uninativecmp( pToken->m_puzLocal, + "unknown") == 0) + { + pToken->m_eTokenType = FUNC_UNKNOWN_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + default: + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + goto Exit; + } + + // Axis specifier or node name? + + if( uChar == FLM_UNICODE_COLON) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( RC_BAD( rc = peekChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_COLON) + { + if( RC_BAD( rc = ungetChar( FLM_UNICODE_COLON))) + { + goto Exit; + } + + // If we have reached this point, we have a name followed + // by '::'. Verify that the name is a valid axis specifier. + + switch( pToken->m_puzLocal[ 0]) + { + case FLM_UNICODE_a: + if( f_uninativecmp( pToken->m_puzLocal, + "ancestor") == 0) + { + pToken->m_eTokenType = AXIS_ANCESTOR_TOKEN; + pToken->m_ui64Val = (FLMUINT64)ANCESTOR_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "ancestor-or-self") == 0) + { + pToken->m_eTokenType = AXIS_ANCESTOR_OR_SELF_TOKEN; + pToken->m_ui64Val = (FLMUINT64)ANCESTOR_OR_SELF_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "attribute") == 0) + { + pToken->m_eTokenType = AXIS_ATTRIB_TOKEN; + pToken->m_ui64Val = (FLMUINT64)ATTRIBUTE_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_c: + if( f_uninativecmp( pToken->m_puzLocal, + "child") == 0) + { + pToken->m_eTokenType = AXIS_CHILD_TOKEN; + pToken->m_ui64Val = (FLMUINT64)CHILD_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_d: + if( f_uninativecmp( pToken->m_puzLocal, + "descendant") == 0) + { + pToken->m_eTokenType = AXIS_DESCENDANT_TOKEN; + pToken->m_ui64Val = (FLMUINT64)DESCENDANT_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "descendant-or-self") == 0) + { + pToken->m_eTokenType = AXIS_DESCENDANT_OR_SELF_TOKEN; + pToken->m_ui64Val = (FLMUINT64)DESCENDANT_OR_SELF_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_f: + if( f_uninativecmp( pToken->m_puzLocal, + "following") == 0) + { + pToken->m_eTokenType = AXIS_FOLLOWING_TOKEN; + pToken->m_ui64Val = (FLMUINT64)FOLLOWING_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "following-sibling") == 0) + { + pToken->m_eTokenType = AXIS_FOLLOWING_SIB_TOKEN; + pToken->m_ui64Val = (FLMUINT64)FOLLOWING_SIBLING_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_m: + if( f_uninativecmp( pToken->m_puzLocal, + "meta") == 0) + { + pToken->m_eTokenType = AXIS_META_TOKEN; + pToken->m_ui64Val = (FLMUINT64)META_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_n: + if( f_uninativecmp( pToken->m_puzLocal, + "namespace") == 0) + { + pToken->m_eTokenType = AXIS_NAMESPACE_TOKEN; + pToken->m_ui64Val = (FLMUINT64)NAMESPACE_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_p: + if( f_uninativecmp( pToken->m_puzLocal, + "parent") == 0) + { + pToken->m_eTokenType = AXIS_PARENT_TOKEN; + pToken->m_ui64Val = (FLMUINT64)PARENT_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "preceding") == 0) + { + pToken->m_eTokenType = AXIS_PRECEDING_TOKEN; + pToken->m_ui64Val = (FLMUINT64)PRECEDING_AXIS; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "preceding-sibling") == 0) + { + pToken->m_eTokenType = AXIS_PRECEDING_SIB_TOKEN; + pToken->m_ui64Val = (FLMUINT64)PRECEDING_SIBLING_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + case FLM_UNICODE_s: + if( f_uninativecmp( pToken->m_puzLocal, + "self") == 0) + { + pToken->m_eTokenType = AXIS_SELF_TOKEN; + pToken->m_ui64Val = (FLMUINT64)SELF_AXIS; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + break; + + default: + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + goto Exit; + } + else if( uChar == FLM_UNICODE_ASTERISK) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + pToken->m_eTokenType = NAME_TEST_NCWILD_TOKEN; + goto Exit; + } + else + { + if( RC_BAD( rc = ungetChar( FLM_UNICODE_COLON))) + { + goto Exit; + } + } + } + + // Operator? + + if( m_eLastTokenType != UNKNOWN_TOKEN && + m_eLastTokenType != LBRACE_TOKEN && + m_eLastTokenType != RBRACE_TOKEN && + m_eLastTokenType != DOUBLE_COLON_TOKEN && + m_eLastTokenType != OP_LPAREN_TOKEN && + m_eLastTokenType != OP_LBRACKET_TOKEN && + m_eLastTokenType != COMMA_TOKEN && + !isOperator( m_eLastTokenType) && + !isAxisSpecifier( m_eLastTokenType)) + { + if( f_uninativecmp( pToken->m_puzLocal, + "and") == 0) + { + pToken->m_eTokenType = OP_AND_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "or") == 0) + { + pToken->m_eTokenType = OP_OR_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "bitor") == 0) + { + pToken->m_eTokenType = OP_BITOR_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "bitand") == 0) + { + pToken->m_eTokenType = OP_BITAND_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "bitxor") == 0) + { + pToken->m_eTokenType = OP_BITXOR_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "mod") == 0) + { + pToken->m_eTokenType = OP_MOD_TOKEN; + } + else if( f_uninativecmp( pToken->m_puzLocal, + "div") == 0) + { + pToken->m_eTokenType = OP_DIV_TOKEN; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + } + + goto Exit; + } + + // None of the above conditions matched, so we have a QName + + pToken->m_eTokenType = NAME_TEST_QNAME_TOKEN; + break; + } + } + + // Get the operator flags (if any) + + if( tokenCanHaveFlags( pToken->m_eTokenType)) + { + FLMUNICODE uzTmpBuf[ 64]; + FLMUNICODE uTmpChar; + FLMUINT uiOffset; + + if( RC_BAD( rc = skipWhitespace())) + { + goto Exit; + } + + if( RC_BAD( rc = peekChar( &uTmpChar))) + { + goto Exit; + } + + if( uTmpChar == FLM_UNICODE_LBRACE) + { + if( RC_BAD( rc = getChar( &uTmpChar))) + { + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = skipWhitespace())) + { + goto Exit; + } + + uiOffset = 0; + + for( ;;) + { + if( RC_BAD( rc = getChar( &uTmpChar))) + { + goto Exit; + } + + if( !gv_XFlmSysData.pXml->isLetter( uTmpChar) && + uTmpChar != FLM_UNICODE_UNDERSCORE) + { + uzTmpBuf[ uiOffset] = 0; + if( f_uninativecmp( uzTmpBuf, "ci") == 0 || + f_uninativecmp( uzTmpBuf, "caseinsensitive") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_CASE_INSENSITIVE; + } + else if( f_uninativecmp( uzTmpBuf, "cs") == 0 || + f_uninativecmp( uzTmpBuf, "compressspace") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_COMPRESS_WHITESPACE; + } + else if( f_uninativecmp( uzTmpBuf, "ignls") == 0 || + f_uninativecmp( uzTmpBuf, "ignoreleadingspace") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_IGNORE_LEADING_SPACE; + } + else if( f_uninativecmp( uzTmpBuf, "ignts") == 0 || + f_uninativecmp( uzTmpBuf, "ignoretrailingspace") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_IGNORE_TRAILING_SPACE; + } + else if( f_uninativecmp( uzTmpBuf, "wstosp") == 0 || + f_uninativecmp( uzTmpBuf, "whitespaceasspace") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_WHITESPACE_AS_SPACE; + } + else if( f_uninativecmp( uzTmpBuf, "ns") == 0 || + f_uninativecmp( uzTmpBuf, "nospace") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_NO_WHITESPACE; + } + else if( f_uninativecmp( uzTmpBuf, "nu") == 0 || + f_uninativecmp( uzTmpBuf, "nounderscores") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_NO_UNDERSCORES; + } + else if( f_uninativecmp( uzTmpBuf, "nd") == 0 || + f_uninativecmp( uzTmpBuf, "nodashes") == 0) + { + pToken->m_uiTokenFlags |= XFLM_COMP_NO_DASHES; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + break; + } + + uzTmpBuf[ uiOffset++] = uTmpChar; + } + + if( !uTmpChar) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + else if( uTmpChar == FLM_UNICODE_RBRACE) + { + break; + } + else if( uTmpChar != FLM_UNICODE_COMMA) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + } + } + +Exit: + + m_eLastTokenType = pToken->m_eTokenType; + + // Are we expecting a () sequence after the token that we need to + // just consume? + + if (RC_OK( rc) && bConsumeParens) + { + if( RC_OK( rc = skipWhitespace())) + { + if( RC_OK( rc = getChar( &uChar))) + { + if (uChar != FLM_UNICODE_LPAREN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + } + else if (RC_OK( rc = skipWhitespace())) + { + if (RC_OK( rc = getChar( &uChar))) + { + if (uChar != FLM_UNICODE_RPAREN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + } + } + } + } + } + } + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XPathTokenizer::getName( + F_XPathToken * pToken) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOffset; + FLMBOOL bFoundColon = FALSE; + FLMUINT uiMaxChars; + FLMUNICODE uChar; + FLMUNICODE uTmpChar; + + uiOffset = 0; + if( (uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE)) < 32) + { + if( RC_BAD( rc = pToken->resizeBuffer( 32 * sizeof( FLMUNICODE)))) + { + goto Exit; + } + uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE); + } + + pToken->m_puzLocal = (FLMUNICODE *)pToken->m_pValBuf; + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( !gv_XFlmSysData.pXml->isLetter( uChar) && + uChar != FLM_UNICODE_UNDERSCORE) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset++] = uChar; + for( ;;) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uiOffset == uiMaxChars) + { + if( RC_BAD( rc = pToken->resizeBuffer( + pToken->m_uiValBufSize * sizeof( FLMUNICODE) * 2))) + { + goto Exit; + } + uiMaxChars *= 2; + } + + if( uChar == FLM_UNICODE_COLON) + { + if( bFoundColon) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = peekChar( &uTmpChar))) + { + goto Exit; + } + + if( !gv_XFlmSysData.pXml->isNCNameChar( uTmpChar)) + { + break; + } + + uChar = 0; + pToken->m_puzPrefix = (FLMUNICODE *)pToken->m_pValBuf; + pToken->m_puzLocal = + &(((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset + 1]); + bFoundColon = TRUE; + } + else if( !gv_XFlmSysData.pXml->isNCNameChar( uChar)) + { + break; + } + + ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset++] = uChar; + } + + ((FLMUNICODE *)pToken->m_pValBuf)[ uiOffset] = 0; + + if( bFoundColon && + (*pToken->m_puzPrefix == 0 || *pToken->m_puzLocal == 0)) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XPathTokenizer::getNumber( + F_XPathToken * pToken) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64Num = 0; + FLMUNICODE uChar; + + for( ;;) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uChar < FLM_UNICODE_0 || uChar > FLM_UNICODE_9) + { + if( RC_BAD( rc = ungetChar( uChar))) + { + goto Exit; + } + + break; + } + +#if defined ( FLM_LINUX) || defined ( FLM_NLM) || defined( FLM_OSX) + if( ui64Num > ((0xFFFFFFFFFFFFFFFFULL / 10) + (uChar - FLM_UNICODE_0))) +#else + if( ui64Num > ((0xFFFFFFFFFFFFFFFF / 10) + (uChar - FLM_UNICODE_0))) +#endif + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Num = (FLMUINT64)((ui64Num * 10) + (uChar - FLM_UNICODE_0)); + } + + pToken->m_ui64Val = ui64Num; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XPathTokenizer::getBinary( + F_XPathToken * pToken) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + FLMBOOL bDoubleQuote = FALSE; + FLMUINT uiOffset = 0; + FLMUINT uiMaxBytes; + FLMBYTE * pucBuf; + FLMBOOL bHaveHighNibble = FALSE; + FLMBYTE ucHighNibble = 0; + FLMBYTE ucNibble; + + if( (uiMaxBytes = pToken->m_uiValBufSize) < 64) + { + if( RC_BAD( rc = pToken->resizeBuffer( 64))) + { + goto Exit; + } + uiMaxBytes = pToken->m_uiValBufSize; + } + + pucBuf = (FLMBYTE *)pToken->m_pValBuf; + + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_QUOTE) + { + bDoubleQuote = TRUE; + } + else if( uChar != FLM_UNICODE_APOS) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if ( uChar == 0) + { + rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_INPUT); + goto Exit; + } + + if( bDoubleQuote && uChar == FLM_UNICODE_QUOTE) + { + break; + } + else if( !bDoubleQuote && uChar == FLM_UNICODE_APOS) + { + break; + } + + if (uChar >= FLM_UNICODE_0 && uChar <= FLM_UNICODE_9) + { + ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_0); + } + else if (uChar >= FLM_UNICODE_A && uChar <= FLM_UNICODE_F) + { + ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_A + 10); + } + else if (uChar >= FLM_UNICODE_a && uChar <= FLM_UNICODE_f) + { + ucNibble = (FLMBYTE)(uChar - FLM_UNICODE_a + 10); + } + else if (gv_XFlmSysData.pXml->isWhitespace( uChar)) + { + // Just skip whitespace, anything else we will treat as an + // error. + continue; + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + if (!bHaveHighNibble) + { + ucHighNibble = (FLMBYTE)(ucNibble << 4); + bHaveHighNibble = TRUE; + } + else + { + pucBuf [uiOffset++] = ucHighNibble | ucNibble; + if( uiOffset == uiMaxBytes) + { + if( RC_BAD( rc = pToken->resizeBuffer( + pToken->m_uiValBufSize * 2))) + { + goto Exit; + } + uiMaxBytes *= 2; + } + bHaveHighNibble = FALSE; + } + } + + if (bHaveHighNibble) + { + pucBuf [uiOffset++] = ucHighNibble; + } + pToken->m_uiValBufLen = uiOffset; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XPathTokenizer::getLiteral( + F_XPathToken * pToken) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar; + FLMBOOL bDoubleQuote = FALSE; + FLMUINT uiOffset; + FLMUINT uiMaxChars; + + uiOffset = 0; + if( (uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE)) < 32) + { + if( RC_BAD( rc = pToken->resizeBuffer( 32 * sizeof( FLMUNICODE)))) + { + goto Exit; + } + uiMaxChars = pToken->m_uiValBufSize / sizeof( FLMUNICODE); + } + + pToken->m_puzLocal = (FLMUNICODE *)pToken->m_pValBuf; + + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if( uChar == FLM_UNICODE_QUOTE) + { + bDoubleQuote = TRUE; + } + else if( uChar != FLM_UNICODE_APOS) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + for( ;;) + { + if( RC_BAD( rc = getChar( &uChar))) + { + goto Exit; + } + + if ( uChar == 0) + { + rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_INPUT); + goto Exit; + } + + if( bDoubleQuote && uChar == FLM_UNICODE_QUOTE) + { + break; + } + else if( !bDoubleQuote && uChar == FLM_UNICODE_APOS) + { + break; + } + + pToken->m_puzLocal[ uiOffset++] = uChar; + + if( uiOffset == uiMaxChars) + { + if( RC_BAD( rc = pToken->resizeBuffer( + pToken->m_uiValBufSize * sizeof( FLMUNICODE) * 2))) + { + goto Exit; + } + uiMaxChars *= 2; + } + } + + pToken->m_puzLocal[ uiOffset++] = 0; + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Class for handling query validator calls +***************************************************************************/ +class XFLAIM_QueryValFunc : public IF_QueryValFunc, public XF_Base +{ +public: + + XFLAIM_QueryValFunc() + { + } + + virtual ~XFLAIM_QueryValFunc() + { + } + + RCODE XFLMAPI getValue( + IF_Db * pDb, + IF_DOMNode * pContextNode, + ValIterator eValueToGet, + eValTypes * peValType, + FLMBOOL * pbLastValue, + void * pvVal, + IF_DynaBuf * pDynaBuf = NULL); + + RCODE XFLMAPI cloneSelf( + IF_QueryValFunc ** ppNewObj); +}; + +/**************************************************************************** +Desc: Get the next value for a query function. Since this is really just + code to test the callback, it always returns a value whose type + is boolean and whose value is true. +****************************************************************************/ +RCODE XFLMAPI XFLAIM_QueryValFunc::getValue( + IF_Db *, // pDb, + IF_DOMNode *, // pContextNode, + ValIterator eValueToGet, + eValTypes * peValType, + FLMBOOL * pbLastValue, + void * pvVal, + IF_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_XFLM_OK; + + if (pDynaBuf) + { + (void)pDynaBuf->truncateData( 0); + } + + if (eValueToGet != GET_FIRST_VAL && eValueToGet != GET_LAST_VAL) + { + rc = (eValueToGet == GET_NEXT_VAL) + ? NE_XFLM_EOF_HIT + : NE_XFLM_BOF_HIT; + goto Exit; + } + + *pbLastValue = TRUE; + *peValType = XFLM_BOOL_VAL; + *((XFlmBoolType *)pvVal) = XFLM_TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Copy self to create a new object. +****************************************************************************/ +RCODE XFLMAPI XFLAIM_QueryValFunc::cloneSelf( + IF_QueryValFunc ** ppNewObj) +{ + + // No need to copy - simply reference, because it has no private + // state data that needs to have its own copy. + + *ppNewObj = (IF_QueryValFunc *)this; + (*ppNewObj)->AddRef(); + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Add a callback function predicate. +***************************************************************************/ +FSTATIC RCODE addCallbackFunc( + IF_Query * pQuery) +{ + RCODE rc = NE_XFLM_OK; + XFLAIM_QueryValFunc * pFuncObj = NULL; + + // Create a callback function object + + if ((pFuncObj = f_new XFLAIM_QueryValFunc) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pQuery->addFunction( pFuncObj, TRUE))) + { + goto Exit; + } + +Exit: + + if (pFuncObj) + { + pFuncObj->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Parse an XPATH query +****************************************************************************/ +RCODE F_XPath::parseQuery( + F_Db * pDb, + IF_IStream * pIStream, + IF_Query * pQuery) +{ + RCODE rc = NE_XFLM_OK; + F_NameTable * pNameTable = NULL; + F_XMLNamespace * pNamespace = NULL; + eXPathAxisTypes eCurrentAxis = CHILD_AXIS; + eDomNodeType eNodeType = ELEMENT_NODE; + FLMUINT uiDictNum; + + if( RC_BAD( rc = pDb->getNameTable( &pNameTable))) + { + goto Exit; + } + + if( RC_BAD( rc = m_tokenizer.setup( pIStream))) + { + goto Exit; + } + + // Process any namespace declarations + + popNamespaces( getNamespaceCount()); + + if( RC_BAD( rc = pushNamespace( NULL, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.getType() == LBRACE_TOKEN) + { + for( ;;) + { + if( (pNamespace = f_new F_XMLNamespace) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.getType() != NAME_TEST_QNAME_TOKEN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( m_curToken.getPrefixPtr()) + { + if( f_uninativecmp( m_curToken.getPrefixPtr(), + "xmlns") != 0) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = pNamespace->setPrefix( + m_curToken.getLocalPtr()))) + { + goto Exit; + } + } + else + { + if( f_uninativecmp( m_curToken.getLocalPtr(), + "xmlns") != 0) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.getType() != OP_EQ_TOKEN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.getType() != LITERAL_TOKEN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = pNamespace->setURI( + m_curToken.getLocalPtr()))) + { + goto Exit; + } + + if( RC_BAD( rc = pushNamespace( pNamespace))) + { + goto Exit; + } + + pNamespace->Release(); + pNamespace = NULL; + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.getType() == RBRACE_TOKEN) + { + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + break; + } + else if( m_curToken.getType() != COMMA_TOKEN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + } + + for( ;;) + { + + if( m_curToken.m_eTokenType == END_TOKEN) + { + break; + } + + switch( m_curToken.m_eTokenType) + { + case OP_AND_TOKEN: + case OP_OR_TOKEN: + case OP_EQ_TOKEN: + case OP_APPROX_EQ_TOKEN: + case OP_NOT_TOKEN: + case OP_NE_TOKEN: + case OP_LT_TOKEN: + case OP_LE_TOKEN: + case OP_GT_TOKEN: + case OP_GE_TOKEN: + case OP_BITAND_TOKEN: + case OP_BITOR_TOKEN: + case OP_BITXOR_TOKEN: + case OP_MULT_TOKEN: + case OP_DIV_TOKEN: + case OP_MOD_TOKEN: + case OP_PLUS_TOKEN: + case OP_MINUS_TOKEN: + case OP_LPAREN_TOKEN: + case OP_RPAREN_TOKEN: + case OP_COMMA_TOKEN: + case OP_LBRACKET_TOKEN: + case OP_RBRACKET_TOKEN: + if( RC_BAD( rc = pQuery->addOperator( + (eQueryOperators)m_curToken.m_eTokenType, + m_curToken.m_uiTokenFlags))) + { + goto Exit; + } + break; + + case LITERAL_TOKEN: + if( RC_BAD( rc = pQuery->addUnicodeValue( + (FLMUNICODE *)m_curToken.m_pValBuf))) + { + goto Exit; + } + break; + + case BINARY_TOKEN: + if( RC_BAD( rc = pQuery->addBinaryValue( + m_curToken.m_pValBuf, + m_curToken.m_uiValBufLen))) + { + goto Exit; + } + break; + + case NUMBER_TOKEN: + if( RC_BAD( rc = pQuery->addUINT64Value( m_curToken.m_ui64Val))) + { + goto Exit; + } + break; + + case FUNC_TRUE_TOKEN: + case FUNC_FALSE_TOKEN: + case FUNC_UNKNOWN_TOKEN: + { + FLMBOOL bVal = (m_curToken.m_eTokenType == FUNC_TRUE_TOKEN) + ? TRUE + : FALSE; + + FLMBOOL bUnknown = (m_curToken.m_eTokenType == FUNC_UNKNOWN_TOKEN) + ? TRUE + : FALSE; + + if ( RC_BAD( rc = pQuery->addBoolean( bVal, bUnknown))) + { + goto Exit; + } + + // consume the opening and closing paren + + if ( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if ( m_curToken.m_eTokenType != OP_LPAREN_TOKEN) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_LPAREN); + goto Exit; + } + + if ( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if ( m_curToken.m_eTokenType != OP_RPAREN_TOKEN) + { + rc = RC_SET( NE_XFLM_Q_EXPECTING_RPAREN); + goto Exit; + } + } + break; + + case FUNC_LAST_TOKEN: + case FUNC_POSITION_TOKEN: + case FUNC_COUNT_TOKEN: + case FUNC_ID_TOKEN: + case FUNC_LOCAL_NAME_TOKEN: + case FUNC_NAMESPACE_URI_TOKEN: + case FUNC_NAME_TOKEN: + case FUNC_STRING_TOKEN: + case FUNC_CONCAT_TOKEN: + case FUNC_STARTS_WITH_TOKEN: + case FUNC_CONTAINS_TOKEN: + case FUNC_SUBSTR_BEFORE_TOKEN: + case FUNC_SUBSTR_AFTER_TOKEN: + case FUNC_SUBSTR_TOKEN: + case FUNC_STR_LEN_TOKEN: + case FUNC_NORM_SPACE_TOKEN: + case FUNC_TRANSLATE_TOKEN: + case FUNC_NOT_TOKEN: + case FUNC_LANG_TOKEN: + case FUNC_NUMBER_TOKEN: + case FUNC_SUM_TOKEN: + case FUNC_FLOOR_TOKEN: + case FUNC_CEILING_TOKEN: + case FUNC_ROUND_TOKEN: + if( RC_BAD( rc = pQuery->addFunction( XFLM_FUNC_xxx))) + { + goto Exit; + } + break; + + case FUNC_CB_TOKEN: + if (RC_BAD( rc = addCallbackFunc( pQuery))) + { + goto Exit; + } + break; + + case AXIS_ANCESTOR_TOKEN: + case AXIS_ANCESTOR_OR_SELF_TOKEN: + case AXIS_CHILD_TOKEN: + case AXIS_DESCENDANT_TOKEN: + case AXIS_DESCENDANT_OR_SELF_TOKEN: + case AXIS_FOLLOWING_TOKEN: + case AXIS_FOLLOWING_SIB_TOKEN: + case AXIS_PARENT_TOKEN: + case AXIS_PRECEDING_TOKEN: + case AXIS_PRECEDING_SIB_TOKEN: + case AXIS_SELF_TOKEN: + case AXIS_ATTRIB_TOKEN: + case AXIS_NAMESPACE_TOKEN: + case AXIS_ATSIGN_TOKEN: + case AXIS_META_TOKEN: + { + eCurrentAxis = (eXPathAxisTypes)m_curToken.m_ui64Val; + eNodeType = ELEMENT_NODE; + + if (m_curToken.m_eTokenType == AXIS_ATSIGN_TOKEN) + { + eNodeType = ATTRIBUTE_NODE; + } + else + { + if (m_curToken.m_eTokenType == AXIS_META_TOKEN) + { + eNodeType = ANY_NODE_TYPE; + } + else if (m_curToken.m_eTokenType == AXIS_ATTRIB_TOKEN || + m_curToken.m_eTokenType == AXIS_NAMESPACE_TOKEN) + { + eNodeType = ATTRIBUTE_NODE; + } + else if (m_curToken.m_eTokenType == AXIS_SELF_TOKEN) + { + eNodeType = ANY_NODE_TYPE; + } + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + if( m_curToken.m_eTokenType != DOUBLE_COLON_TOKEN) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + switch( m_curToken.m_eTokenType) + { + case NAME_TEST_WILD_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + eNodeType, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + } + + case NAME_TEST_QNAME_TOKEN: + { + if (eCurrentAxis == META_AXIS) + { + if (f_uninativecmp( m_curToken.getLocalPtr(), + "nodeid") == 0) + { + uiDictNum = XFLM_META_NODE_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "documentid") == 0) + { + uiDictNum = XFLM_META_DOCUMENT_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "parentid") == 0) + { + uiDictNum = XFLM_META_PARENT_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "firstchildid") == 0) + { + uiDictNum = XFLM_META_FIRST_CHILD_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "lastchildid") == 0) + { + uiDictNum = XFLM_META_LAST_CHILD_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "nextsiblingid") == 0) + { + uiDictNum = XFLM_META_NEXT_SIBLING_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "prevsiblingid") == 0) + { + uiDictNum = XFLM_META_PREV_SIBLING_ID; + } + else if (f_uninativecmp( m_curToken.getLocalPtr(), + "value") == 0) + { + uiDictNum = XFLM_META_VALUE; + } + else + { + rc = RC_SET( NE_XFLM_Q_INVALID_META_DATA_TYPE); + goto Exit; + } + } + else + { + if( m_curToken.getPrefixPtr()) + { + if( RC_BAD( rc = findNamespace( + m_curToken.getPrefixPtr(), &pNamespace))) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + pDb, eNodeType == ELEMENT_NODE + ? ELM_ELEMENT_TAG + : ELM_ATTRIBUTE_TAG, + m_curToken.getLocalPtr(), + NULL, pNamespace ? TRUE : FALSE, + pNamespace ? pNamespace->getURIPtr() : NULL, + &uiDictNum, NULL))) + { + if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + + if( !m_curToken.getPrefixPtr()) + { + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + pDb, eNodeType == ELEMENT_NODE + ? ELM_ELEMENT_TAG + : ELM_ATTRIBUTE_TAG, + m_curToken.getLocalPtr(), + NULL, FALSE, NULL, &uiDictNum, NULL))) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + + if( pNamespace) + { + pNamespace->Release(); + pNamespace = NULL; + } + } + + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + eNodeType, uiDictNum))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + } + case NODE_TYPE_NODE_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + ANY_NODE_TYPE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + } + case NODE_TYPE_TEXT_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + DATA_NODE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + } + case NAME_TEST_NCWILD_TOKEN: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + case NODE_TYPE_COMMENT_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + COMMENT_NODE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + } + + case NODE_TYPE_PI_TOKEN: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + default: + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + break; + } + + case NAME_TEST_QNAME_TOKEN: + + eNodeType = ELEMENT_NODE; + + if( RC_BAD( rc = findNamespace( + m_curToken.getPrefixPtr(), &pNamespace))) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + pDb, ELM_ELEMENT_TAG, m_curToken.getLocalPtr(), + NULL, pNamespace ? TRUE : FALSE, pNamespace ? pNamespace->getURIPtr() : NULL, + &uiDictNum, NULL))) + { + if( rc != NE_XFLM_NOT_FOUND) + { + goto Exit; + } + + if( !m_curToken.getPrefixPtr()) + { + if( RC_BAD( rc = pNameTable->getFromTagTypeAndName( + pDb, ELM_ELEMENT_TAG, m_curToken.getLocalPtr(), + NULL, FALSE, NULL, &uiDictNum, NULL))) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + else + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + + if( pNamespace) + { + pNamespace->Release(); + pNamespace = NULL; + } + + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + eNodeType, uiDictNum))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + + case PERIOD_TOKEN: + if( RC_BAD( rc = pQuery->addXPathComponent( SELF_AXIS, + ANY_NODE_TYPE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + break; + + case DOUBLE_PERIOD_TOKEN: + if ( RC_BAD( rc = pQuery->addXPathComponent( PARENT_AXIS, + ANY_NODE_TYPE, 0))) + { + goto Exit; + } + + eCurrentAxis = PARENT_AXIS; + eNodeType = ELEMENT_NODE; + break; + + case OP_FSLASH_TOKEN: + eCurrentAxis = CHILD_AXIS; + break; + + case OP_DOUBLE_FSLASH_TOKEN: + eCurrentAxis = DESCENDANT_AXIS; + break; + + case NODE_TYPE_NODE_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + ANY_NODE_TYPE, 0))) + { + goto Exit; + } + eCurrentAxis = CHILD_AXIS; + break; + } + + case NODE_TYPE_COMMENT_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + COMMENT_NODE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + + break; + } + + case NODE_TYPE_TEXT_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + DATA_NODE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + + break; + } + + case NAME_TEST_WILD_TOKEN: + { + if( RC_BAD( rc = pQuery->addXPathComponent( eCurrentAxis, + ELEMENT_NODE, 0))) + { + goto Exit; + } + + eCurrentAxis = CHILD_AXIS; + eNodeType = ELEMENT_NODE; + + break; + } + + case DOUBLE_COLON_TOKEN: + case OP_UNION_TOKEN: + case NODE_TYPE_PI_TOKEN: + case NAME_TEST_NCWILD_TOKEN: + case VAR_REF_TOKEN: + case END_TOKEN: + default: + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + + if( RC_BAD( rc = getNextToken())) + { + goto Exit; + } + + } + +Exit: + + m_tokenizer.setup( NULL); + + if( pNamespace) + { + pNamespace->Release(); + } + + if( pNameTable) + { + pNameTable->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Parse an XPATH query +****************************************************************************/ +RCODE F_XPath::parseQuery( + F_Db * pDb, + char * pszQuery, + IF_Query * pQuery) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream istream; + + if( RC_BAD( rc= istream.open( (FLMBYTE *)pszQuery, f_strlen( pszQuery)))) + { + goto Exit; + } + + if( RC_BAD( rc = parseQuery( pDb, &istream, pQuery))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_XPath::getNextToken( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_tokenizer.getNextToken( &m_curToken))) + { + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version5/src/fxpath.h b/version5/src/fxpath.h new file mode 100644 index 0000000..5dc7893 --- /dev/null +++ b/version5/src/fxpath.h @@ -0,0 +1,454 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the FLAIM XML import and export utility classes +// +// 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: fxpath.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FXPATH_H +#define FXPATH_H + +typedef enum +{ + UNKNOWN_TOKEN = 0, // 0 + OP_AND_TOKEN = XFLM_AND_OP, // 1 + OP_OR_TOKEN = XFLM_OR_OP, // 2 + OP_NOT_TOKEN = XFLM_NOT_OP, // 3 + OP_EQ_TOKEN = XFLM_EQ_OP, // 4 + OP_NE_TOKEN = XFLM_NE_OP, // 5 + OP_APPROX_EQ_TOKEN = XFLM_APPROX_EQ_OP, // 6 + OP_LT_TOKEN = XFLM_LT_OP, // 7 + OP_LE_TOKEN = XFLM_LE_OP, // 8 + OP_GT_TOKEN = XFLM_GT_OP, // 9 + OP_GE_TOKEN = XFLM_GE_OP, // 10 + OP_BITAND_TOKEN = XFLM_BITAND_OP, // 11 + OP_BITOR_TOKEN = XFLM_BITOR_OP, // 12 + OP_BITXOR_TOKEN = XFLM_BITXOR_OP, // 13 + OP_MULT_TOKEN = XFLM_MULT_OP, // 14 + OP_DIV_TOKEN = XFLM_DIV_OP, // 15 + OP_MOD_TOKEN = XFLM_MOD_OP, // 16 + OP_PLUS_TOKEN = XFLM_PLUS_OP, // 17 + OP_MINUS_TOKEN = XFLM_MINUS_OP, // 18 + OP_NEG_TOKEN = XFLM_NEG_OP, // 19 + OP_LPAREN_TOKEN = XFLM_LPAREN_OP, // 20 + OP_RPAREN_TOKEN = XFLM_RPAREN_OP, // 21 + OP_COMMA_TOKEN = XFLM_COMMA_OP, // 22 + OP_LBRACKET_TOKEN = XFLM_LBRACKET_OP, // 23 + OP_RBRACKET_TOKEN = XFLM_RBRACKET_OP, // 24 + OP_FSLASH_TOKEN, // 25 + OP_DOUBLE_FSLASH_TOKEN, // 26 + OP_UNION_TOKEN, // 27 + PERIOD_TOKEN, // 28 + DOUBLE_PERIOD_TOKEN, // 29 + COMMA_TOKEN, // 30 + DOUBLE_COLON_TOKEN, // 31 + NAME_TEST_WILD_TOKEN, // 32 + NAME_TEST_NCWILD_TOKEN, // 33 + NAME_TEST_QNAME_TOKEN, // 34 + NODE_TYPE_COMMENT_TOKEN, // 35 + NODE_TYPE_TEXT_TOKEN, // 36 + NODE_TYPE_PI_TOKEN, // 37 + NODE_TYPE_NODE_TOKEN, // 38 + AXIS_ANCESTOR_TOKEN, // 39 + AXIS_ANCESTOR_OR_SELF_TOKEN, // 40 + AXIS_ATTRIB_TOKEN, // 41 + AXIS_CHILD_TOKEN, // 42 + AXIS_DESCENDANT_TOKEN, // 43 + AXIS_DESCENDANT_OR_SELF_TOKEN, // 44 + AXIS_FOLLOWING_TOKEN, // 45 + AXIS_FOLLOWING_SIB_TOKEN, // 46 + AXIS_NAMESPACE_TOKEN, // 47 + AXIS_PARENT_TOKEN, // 48 + AXIS_PRECEDING_TOKEN, // 49 + AXIS_PRECEDING_SIB_TOKEN, // 50 + AXIS_SELF_TOKEN, // 51 + AXIS_ATSIGN_TOKEN, // 52 + AXIS_META_TOKEN, // 53 + LITERAL_TOKEN, // 54 + NUMBER_TOKEN, // 55 + VAR_REF_TOKEN, // 56 + LBRACE_TOKEN, // 57 + RBRACE_TOKEN, // 58 + FUNC_LAST_TOKEN, // 59 + FUNC_POSITION_TOKEN, // 60 + FUNC_COUNT_TOKEN, // 61 + FUNC_ID_TOKEN, // 62 + FUNC_LOCAL_NAME_TOKEN, // 63 + FUNC_NAMESPACE_URI_TOKEN, // 64 + FUNC_NAME_TOKEN, // 65 + FUNC_STRING_TOKEN, // 66 + FUNC_CONCAT_TOKEN, // 67 + FUNC_STARTS_WITH_TOKEN, // 68 + FUNC_CONTAINS_TOKEN, // 69 + FUNC_SUBSTR_BEFORE_TOKEN, // 70 + FUNC_SUBSTR_AFTER_TOKEN, // 71 + FUNC_SUBSTR_TOKEN, // 72 + FUNC_STR_LEN_TOKEN, // 73 + FUNC_NORM_SPACE_TOKEN, // 74 + FUNC_TRANSLATE_TOKEN, // 75 + FUNC_NOT_TOKEN, // 76 + FUNC_TRUE_TOKEN, // 77 + FUNC_FALSE_TOKEN, // 78 + FUNC_UNKNOWN_TOKEN, // 79 + FUNC_LANG_TOKEN, // 80 + FUNC_NUMBER_TOKEN, // 81 + FUNC_SUM_TOKEN, // 82 + FUNC_FLOOR_TOKEN, // 83 + FUNC_CEILING_TOKEN, // 84 + FUNC_ROUND_TOKEN, // 85 + BINARY_TOKEN, // 86 + FUNC_CB_TOKEN, // 87 + END_TOKEN // 88 +} eXPathTokenType; + +class F_XPathBase : public virtual XF_Base +{ +public: + + FINLINE FLMBOOL isOperator( + eXPathTokenType eType) + { + switch( eType) + { + case OP_AND_TOKEN: + case OP_OR_TOKEN: + case OP_MOD_TOKEN: + case OP_DIV_TOKEN: + case OP_MULT_TOKEN: + case OP_FSLASH_TOKEN: + case OP_DOUBLE_FSLASH_TOKEN: + case OP_UNION_TOKEN: + case OP_PLUS_TOKEN: + case OP_MINUS_TOKEN: + case OP_EQ_TOKEN: + case OP_NE_TOKEN: + case OP_LT_TOKEN: + case OP_LE_TOKEN: + case OP_GT_TOKEN: + case OP_GE_TOKEN: + return( TRUE); + default: + break; + } + + return( FALSE); + } + + FINLINE FLMBOOL tokenCanHaveFlags( + eXPathTokenType eType) + { + switch( eType) + { + case OP_EQ_TOKEN: + case OP_NE_TOKEN: + case OP_LT_TOKEN: + case OP_LE_TOKEN: + case OP_GT_TOKEN: + case OP_GE_TOKEN: + return( TRUE); + default: + break; + } + + return( FALSE); + } + + FINLINE FLMBOOL isAxisSpecifier( + eXPathTokenType eType) + { + switch( eType) + { + case AXIS_ANCESTOR_TOKEN: + case AXIS_ANCESTOR_OR_SELF_TOKEN: + case AXIS_ATTRIB_TOKEN: + case AXIS_CHILD_TOKEN: + case AXIS_DESCENDANT_TOKEN: + case AXIS_DESCENDANT_OR_SELF_TOKEN: + case AXIS_FOLLOWING_TOKEN: + case AXIS_FOLLOWING_SIB_TOKEN: + case AXIS_NAMESPACE_TOKEN: + case AXIS_PARENT_TOKEN: + case AXIS_PRECEDING_TOKEN: + case AXIS_PRECEDING_SIB_TOKEN: + case AXIS_SELF_TOKEN: + case AXIS_ATSIGN_TOKEN: + return( TRUE); + default: + break; + } + + return( FALSE); + } +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_XPathToken : public F_XPathBase +{ +public: + + F_XPathToken() + { + m_pValBuf = NULL; + m_uiValBufSize = 0; + reset(); + } + + ~F_XPathToken() + { + if( m_pValBuf) + { + f_free( &m_pValBuf); + } + } + + FINLINE void reset( void) + { + m_eTokenType = UNKNOWN_TOKEN; + m_uiTokenFlags = 0; + m_ui64Val = 0; + m_puzPrefix = NULL; + m_puzLocal = NULL;; + } + + FINLINE RCODE resizeBuffer( + FLMUINT uiNewSize) + { + RCODE rc = NE_XFLM_OK; + void * pOrigBuf = m_pValBuf; + + if( !m_pValBuf) + { + if( RC_BAD( rc = f_alloc( uiNewSize, &m_pValBuf))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = f_realloc( uiNewSize, &m_pValBuf))) + { + goto Exit; + } + + if( m_puzPrefix) + { + m_puzPrefix = (FLMUNICODE *)(((FLMBYTE *)m_puzPrefix - + (FLMBYTE *)pOrigBuf) + (FLMBYTE *)m_pValBuf); + } + + if( m_puzLocal) + { + m_puzLocal = (FLMUNICODE *)(((FLMBYTE *)m_puzLocal - + (FLMBYTE *)pOrigBuf) + (FLMBYTE *)m_pValBuf); + } + } + + m_uiValBufSize = uiNewSize; + + Exit: + + return( rc); + } + + FINLINE eXPathTokenType getType( void) + { + return( m_eTokenType); + } + + FINLINE FLMUNICODE * getPrefixPtr( void) + { + return( m_puzPrefix); + } + + FINLINE FLMUNICODE * getLocalPtr( void) + { + return( m_puzLocal); + } + + FINLINE FLMUINT64 getNumber( void) + { + return( m_ui64Val); + } + + FINLINE FLMUINT getTokenFlags( void) + { + return( m_uiTokenFlags); + } + +private: + + eXPathTokenType m_eTokenType; + FLMUINT m_uiTokenFlags; + void * m_pValBuf; + FLMUINT m_uiValBufSize; + FLMUINT m_uiValBufLen; + FLMUINT64 m_ui64Val; + FLMUNICODE * m_puzPrefix; + FLMUNICODE * m_puzLocal; + +friend class F_XPathTokenizer; +friend class F_XPath; +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_XPathTokenizer : public F_XPathBase, public F_XMLNamespaceMgr +{ +public: + + F_XPathTokenizer() + { + m_pIStream = NULL; + m_uiUngetCount = 0; + m_eLastTokenType = UNKNOWN_TOKEN; + } + + ~F_XPathTokenizer() + { + if( m_pIStream) + { + m_pIStream->Release(); + } + } + + RCODE setup( + IF_IStream * pIStream); + + RCODE getNextToken( + F_XPathToken * pToken); + +private: + + RCODE skipWhitespace( void); + + RCODE getChar( + FLMUNICODE * puChar); + + RCODE peekChar( + FLMUNICODE * puChar); + + RCODE ungetChar( + FLMUNICODE uChar); + + RCODE getNumber( + F_XPathToken * pToken); + + RCODE getName( + F_XPathToken * pToken); + + RCODE getBinary( + F_XPathToken * pToken); + + RCODE getLiteral( + F_XPathToken * pToken); + + IF_IStream * m_pIStream; + eXPathTokenType m_eLastTokenType; + FLMUINT m_uiUngetCount; +#define XPATH_MAX_UNGET_CHARS 4 + FLMUNICODE m_uUngetBuf[ XPATH_MAX_UNGET_CHARS]; +}; + +class F_XPathExpr; +class F_XPathPredicate; +class F_XPathAxisProducer; +class F_XPathStep; + +/***************************************************************************** +Desc: +******************************************************************************/ +class F_XPath : public F_XPathBase, public F_XMLNamespaceMgr +{ +public: + + F_XPath() + { + } + + ~F_XPath() + { + } + + RCODE parseQuery( + F_Db * pDb, + IF_IStream * pIStream, + IF_Query * pQuery); + + RCODE parseQuery( + F_Db * pDb, + char * pszQuery, + IF_Query * pQuery); + +private: + + RCODE processFilterExpr( + F_XPathExpr ** ppExpr); + + RCODE processPathExpr( + F_XPathExpr ** ppExpr); + + RCODE processUnionExpr( + F_XPathExpr ** ppExpr); + + RCODE processNodeTest( + FLMBOOL bAttr, + F_XPathExpr ** ppExpr); + + RCODE processStep( + F_XPathExpr ** ppExpr); + + RCODE processRelativeLocationPath( + F_XPathExpr ** ppExpr); + + RCODE processUnaryExpr( + F_XPathExpr ** ppExpr); + + RCODE processOrExpr( + F_XPathExpr ** ppExpr); + + RCODE processAndExpr( + F_XPathExpr ** ppExpr); + + RCODE processEqualityExpr( + F_XPathExpr ** ppExpr); + + RCODE processRelationalExpr( + F_XPathExpr ** ppExpr); + + RCODE processAdditiveExpr( + F_XPathExpr ** ppExpr); + + RCODE processMultiplicativeExpr( + F_XPathExpr ** ppExpr); + + RCODE processPrimaryExpr( + F_XPathExpr ** ppExpr); + + RCODE getNextToken( void); + + F_XPathTokenizer m_tokenizer; + F_XPathToken m_curToken; +}; + +#endif // FXPATH_H diff --git a/version5/src/inifile.cpp b/version5/src/inifile.cpp new file mode 100644 index 0000000..2475954 --- /dev/null +++ b/version5/src/inifile.cpp @@ -0,0 +1,1082 @@ +//------------------------------------------------------------------------------ +// 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 "xflaim.h" +#include "flaimsys.h" +#include "inifile.h" + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IniFile::F_IniFile() +{ + m_pFirstLine = NULL; + m_pLastLine = NULL; + m_bReady = FALSE; + m_bModified = FALSE; + m_pszFileName = NULL; + m_pFile = NULL; + m_pPool = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +F_IniFile::~F_IniFile() +{ + if( m_pszFileName) + { + f_free( &m_pszFileName); + } + + if( m_pPool) + { + m_pPool->Release(); + } + + if( m_pFile) + { + m_pFile->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_IniFile::Init() +{ + RCODE rc = NE_XFLM_OK; + + if( m_pPool) + { + m_pPool->Release(); + } + + if (( m_pPool = f_new F_Pool) == NULL) + { + rc = RC_SET( NE_XFLM_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 F_IniFile::Read( + char * pszFileName + ) +{ + RCODE rc = NE_XFLM_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_pFile); + + // 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->Open( pszFileName, + XFLM_IO_RDONLY, &m_pFile))) + { + 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_XFLM_IO_END_OF_FILE) + { + goto Exit; + } + + if (rc == NE_XFLM_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_XFLM_IO_END_OF_FILE) ) + { + goto Exit; + } + + if( rc == NE_XFLM_IO_END_OF_FILE) + { + bEOF = TRUE; + } + uiBytesInLine += uiBytesAvail; + } + + if ( (RC_OK( rc) || (rc == NE_XFLM_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_XFLM_SYNTAX) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + } // end of while (!bEOF) + +Exit: + + // Close the file + + if( m_pFile) + { + m_pFile->Close(); + m_pFile->Release(); + m_pFile = NULL; + } + + // Free the buffer + + if (pszReadBuf) + { + f_free( &pszReadBuf); + } + + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + + return rc; +} + +/**************************************************************************** +Desc: Copies the data stored in the INI_LINE structs to the ini file +****************************************************************************/ +RCODE F_IniFile::Write() +{ + RCODE rc = NE_XFLM_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_pFile); + + if (RC_BAD( rc = gv_pFileSystem->Create( m_pszFileName, + XFLM_IO_RDWR, &m_pFile))) + { + goto Exit; + } + + pCurLine = m_pFirstLine; + while (pCurLine) + { + if (pCurLine->pszParamName) + { + // Output the param name + if (RC_BAD (rc = m_pFile->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_pFile->Write( uiFileOffset, 1, + (void *)"=", &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + + if (RC_BAD (rc = m_pFile->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_pFile->Write( uiFileOffset, 2, + (void *)" #", &uiBytesWritten))) + { + goto Exit; + } + } + else + { + if (RC_BAD (rc = m_pFile->Write( uiFileOffset, 1, + (void *)"#", &uiBytesWritten))) + { + goto Exit; + } + } + + uiFileOffset += uiBytesWritten; + + if (RC_BAD (rc = m_pFile->Write( uiFileOffset, + f_strlen( pCurLine->pszComment), + pCurLine->pszComment, + &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + + } + + // Write out a newline... + if (RC_BAD (rc = m_pFile->Write( uiFileOffset, f_strlen( "\n"), + (void *)"\n", &uiBytesWritten))) + { + goto Exit; + } + + uiFileOffset += uiBytesWritten; + + pCurLine = pCurLine->pNext; + + } // end for loop + + // The contents of disk now represent the the structs in memory, + // so reset the bModified flag + m_bModified = FALSE; + +Exit: + // Close the file + if (m_pFile) + { + m_pFile->Close(); + m_pFile->Release(); + m_pFile = NULL; + } + + return rc; +} + +/**************************************************************************** +Desc: Retrieves the value associated with the specified name from the list + of INI_STRUCTs +****************************************************************************/ +FLMBOOL 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 F_IniFile::SetParam( + const char * pszParamName, + FLMUINT uiParamVal) +{ + RCODE rc = NE_XFLM_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 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 F_IniFile::SetParam( + const char * pszParamName, + FLMBOOL bParamVal) +{ + RCODE rc = NE_XFLM_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 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 F_IniFile::SetParam( + const char * pszParamName, + const char * pszParamVal) +{ + RCODE rc = NE_XFLM_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_XFLM_OK; + FLMUINT uiBytesRead = 0; + FLMUINT uiBytesInLine = 0; + FLMUINT uiEOLBytes = 0; + FLMBOOL bEOL = FALSE; + + flmAssert( m_pFile); + + rc = m_pFile->Read( m_uiFileOffset, *puiBytes, + (FLMBYTE *)pszBuf, &uiBytesRead); + + if ( RC_OK( rc) || rc == NE_XFLM_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_XFLM_IO_END_OF_FILE... + + if( rc == NE_XFLM_IO_END_OF_FILE && + (uiBytesInLine + uiEOLBytes) < uiBytesRead) + { + rc = NE_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_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_XFLM_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/version5/src/inifile.h b/version5/src/inifile.h new file mode 100644 index 0000000..3218fa7 --- /dev/null +++ b/version5/src/inifile.h @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// Desc: Class to support reading/writing/parsing of .ini file +// Every line in the .ini file may have a parameter part (consisting of a +// parameter name and an optional value) and a comment part. Empty lines +// are also allowed. Parameter names don't have to have values, but a +// value must have a name. +// +// 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.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +typedef struct IniLine +{ + char * pszParamName; + char * pszParamValue; + char * pszComment; + struct IniLine * pPrev; + struct IniLine * pNext; +} INI_LINE; + +class F_IniFile : public XF_RefCount, public XF_Base +{ +public: + + F_IniFile(); + + virtual ~F_IniFile(); + + RCODE Init( void); + + RCODE Read( + char * pszFileName); + + RCODE Write( void); + + FLMBOOL GetParam( + const char * pszParamName, + FLMUINT * puiParamVal); + + RCODE SetParam( + const char * pszParamName, + FLMUINT uiParamVal); + + FLMBOOL GetParam( + const char * pszParamName, + FLMBOOL * pbParamVal); + + RCODE SetParam( + const char * pszParamName, + FLMBOOL bParamVal); + + FLMBOOL GetParam( + const char * pszParamName, + char ** ppszParamVal); + + RCODE SetParam( + const char * pszParamName, + const char * pszParamVal); + + FLMBOOL 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_pFile; + char * m_pszFileName; + INI_LINE * m_pFirstLine; + INI_LINE * m_pLastLine; + FLMBOOL m_bReady; + FLMBOOL m_bModified; + FLMUINT m_uiFileOffset; +}; diff --git a/version5/src/kybldkey.cpp b/version5/src/kybldkey.cpp new file mode 100644 index 0000000..119ce8b --- /dev/null +++ b/version5/src/kybldkey.cpp @@ -0,0 +1,1981 @@ +//------------------------------------------------------------------------------ +// Desc: Build from and until keys from a predicate +// +// Tabs: 3 +// +// Copyright (c) 1996-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: kybldkey.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC FLMUINT kyAddInclComponent( + ICD * pIcd, + FLMBYTE * pucKeyEnd, + FLMBOOL bFromKey, + FLMUINT uiMaxSpaceLeft); + +FINLINE void flmSetupFirstToLastKey( + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen); + +FSTATIC RCODE flmAddNonTextKeyPiece( + PATH_PRED * pPred, + IXD * pIxd, + ICD * pIcd, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbCanCompareOnKey); + +FSTATIC RCODE flmUTF8FindWildcard( + const FLMBYTE * pucValue, + FLMUINT * puiCharPos, + FLMUINT * puiCompareRules); + +FSTATIC RCODE flmCountCharacters( + const FLMBYTE * pucValue, + FLMUINT uiValueLen, + FLMUINT uiMaxToCount, + FLMUINT * puiCompareRules, + FLMUINT * puiCount); + +FSTATIC RCODE flmSelectBestSubstr( + const FLMBYTE ** ppucValue, + FLMUINT * puiValueLen, + FLMUINT * puiCompareRules, + FLMBOOL * pbTrailingWildcard, + FLMBOOL * pbNotUsingFirstOfString); + +FSTATIC void setFromCaseByte( + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + FLMUINT uiCaseLen, + FLMBOOL bIsDBCS, + FLMBOOL bAscending, + FLMBOOL bExcl); + +FSTATIC void setUntilCaseByte( + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMUINT uiCaseLen, + FLMBOOL bIsDBCS, + FLMBOOL bAscending, + FLMBOOL bExcl); + +FSTATIC RCODE flmAddTextKeyPiece( + PATH_PRED * pPred, + IXD * pIxd, + ICD * pIcd, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbCanCompareOnKey); + +/**************************************************************************** +Desc: Add what is needed to an until key so that it is greater than or equal + for all possible components that come after the primary component. + In other words, this key should be less than any keys whose primary + component is greater than it, but greater than all keys whose primary + component is less than or equal to it. +****************************************************************************/ +FSTATIC FLMUINT kyAddInclComponent( + ICD * pIcd, + FLMBYTE * pucKeyEnd, + FLMBOOL bFromKey, + FLMUINT uiMaxSpaceLeft) +{ + FLMUINT uiBytesAdded = 0; + + // If there is a next key component that would be expected, set it to + // the highest possible value. + + if (pIcd->pNextKeyComponent) + { + + // Must at least be room for a 2 byte length. + + if (uiMaxSpaceLeft >= 2) + { + // Need 2nd key component to sort lower if it is the from key + // higher if it is the until key. Note that KEY_LOW_VALUE and + // KEY_HIGH_VALUE always sort lower/higher no matter whether the + // component is ascending or descending. + + if (bFromKey) + { + UW2FBA( (FLMUINT16)KEY_LOW_VALUE, pucKeyEnd); + } + else + { + UW2FBA( (FLMUINT16)KEY_HIGH_VALUE, pucKeyEnd); + } + uiBytesAdded = 2; + } + } + else + { + + // There are no more key components. + // Output one byte of 0xFF - which should be higher than any + // possible SEN that could be output for document ID and node IDs. + // Only do this for until keys. For from keys, no need to add + // anything, because an empty list of IDs will be be inclusive + // on the from side - it will sort lower, and therefore be inclusive. + + if (uiMaxSpaceLeft && !bFromKey) + { + *pucKeyEnd = 0xFF; + uiBytesAdded = 1; + } + } + + return( uiBytesAdded); +} + +/**************************************************************************** +Desc: Setup a first-to-last key for the index. +****************************************************************************/ +FINLINE void flmSetupFirstToLastKey( + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen + ) +{ + UW2FBA( KEY_LOW_VALUE, pucFromKey); + UW2FBA( KEY_HIGH_VALUE, pucUntilKey); + *puiFromKeyLen = 2; + *puiUntilKeyLen = 2; +} + +/**************************************************************************** +Desc: Add a key piece to the from and until key. Text fields are not + handled in this routine because of their complexity. +Notes: The goal of this code is to build a the collated compound piece + for the 'from' and 'until' key only once instead of twice. +****************************************************************************/ +FSTATIC RCODE flmAddNonTextKeyPiece( + PATH_PRED * pPred, + IXD * pIxd, + ICD * pIcd, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbCanCompareOnKey) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFromKeyLen = 0; + FLMUINT uiUntilKeyLen = 0; + FLMBYTE * pucFromKeyLenPos = pucFromKey; + FLMBYTE * pucUntilKeyLenPos = pucUntilKey; + FLMBOOL bDataTruncated; + FLMBYTE * pucFromBuf; + FLMUINT uiFromBufLen; + FLMBYTE * pucUntilBuf; + FLMUINT uiUntilBufLen; + FLMBYTE ucFromNumberBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE ucUntilNumberBuf [FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiValue; + FLMINT iValue; + FLMBOOL bNeg; + FLMUINT64 ui64Value; + FLMINT64 i64Value; + FLMUINT uiFromFlags = 0; + FLMUINT uiUntilFlags = 0; + FQVALUE * pFromValue; + FQVALUE * pUntilValue; + FLMBOOL bInclFrom; + FLMBOOL bInclUntil; + FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE; + + // Leave room for the component length + + pucFromKey += 2; + pucUntilKey += 2; + + // Handle the presence case here - this is not done in kyCollate. + + if (pIcd->uiFlags & ICD_PRESENCE) + { + longToByte( (FLMUINT32)pIcd->uiDictNum, pucFromKey); + uiFromKeyLen = uiUntilKeyLen = 4; + f_memcpy( pucUntilKey, pucFromKey, uiUntilKeyLen); + } + else if (pIcd->uiFlags & ICD_METAPHONE) + { + F_BufferIStream bufferIStream; + + if (pPred->eOperator != XFLM_APPROX_EQ_OP || + pPred->pFromValue->eValType != XFLM_UTF8_VAL) + { + flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, + pucUntilKeyLenPos, puiUntilKeyLen); + if (pPred->eOperator != XFLM_EXISTS_OP) + { + *pbCanCompareOnKey = FALSE; + } + goto Exit; + } + *pbCanCompareOnKey = FALSE; + + // The value type in pPred->pFromValue is XFLM_UTF8_VAL, but the + // calling routine should have put the metaphone value into + // pPred->pFromValue->val.uiVal. Sort of weird, but was the + // only way we could evaluate the cost of multiple words in + // the string. + + uiFromBufLen = sizeof( ucFromNumberBuf); + if( RC_BAD( rc = FlmUINT2Storage( pPred->pFromValue->val.uiVal, + &uiFromBufLen, ucFromNumberBuf))) + { + goto Exit; + } + pucFromBuf = &ucFromNumberBuf [0]; + + if (RC_BAD( rc = bufferIStream.open( pucFromBuf, uiFromBufLen))) + { + goto Exit; + } + + uiFromKeyLen = MAX_KEY_SIZ - 2; + bDataTruncated = FALSE; + + // Pass 0 for compare rules because it is non-text + + if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, + &bufferIStream, XFLM_NUMBER_TYPE, + pIcd->uiFlags, 0, + pIcd->uiLimit, NULL, NULL, + pIxd->uiLanguage, FALSE, FALSE, + &bDataTruncated, NULL))) + { + goto Exit; + } + + if (bDataTruncated) + { + // This should never happen on numeric data. + + flmAssert( 0); + *pbCanCompareOnKey = FALSE; + } + + if (uiFromKeyLen) + { + f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); + } + + uiUntilKeyLen = uiFromKeyLen; + } + else + { + if (pPred->eOperator == XFLM_EXISTS_OP || + pPred->eOperator == XFLM_NE_OP || + pPred->eOperator == XFLM_APPROX_EQ_OP) + { + + // Setup a first-to-last key + + flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, + pucUntilKeyLenPos, puiUntilKeyLen); + goto Exit; + } + + // Only other operator possible is the range operator + + flmAssert( pPred->eOperator == XFLM_RANGE_OP); + + if (bAscending) + { + pFromValue = pPred->pFromValue; + bInclFrom = pPred->bInclFrom; + pUntilValue = pPred->pUntilValue; + bInclUntil = pPred->bInclUntil; + } + else + { + pFromValue = pPred->pUntilValue; + bInclFrom = pPred->bInclUntil; + pUntilValue = pPred->pFromValue; + bInclUntil = pPred->bInclFrom; + } + + // Set up from buffer + + if (!pFromValue) + { + pucFromBuf = NULL; + uiFromBufLen = 0; + } + else + { + switch (pFromValue->eValType) + { + case XFLM_UINT_VAL: + uiValue = pFromValue->val.uiVal; + if (!bInclFrom) + { + if (bAscending) + { + uiValue++; + } + else + { + uiValue--; + } + } + uiFromBufLen = sizeof( ucFromNumberBuf); + if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiFromBufLen, + ucFromNumberBuf))) + { + goto Exit; + } + pucFromBuf = &ucFromNumberBuf [0]; + break; + + case XFLM_INT_VAL: + iValue = pFromValue->val.iVal; + if (!bInclFrom) + { + if (bAscending) + { + iValue++; + } + else + { + iValue--; + } + } + uiFromBufLen = sizeof( ucFromNumberBuf); + if (RC_BAD( rc = FlmINT2Storage( iValue, &uiFromBufLen, + ucFromNumberBuf))) + { + goto Exit; + } + pucFromBuf = &ucFromNumberBuf [0]; + break; + + case XFLM_UINT64_VAL: + ui64Value = pFromValue->val.ui64Val; + if (!bInclFrom) + { + if (bAscending) + { + ui64Value++; + } + else + { + ui64Value--; + } + } + uiFromBufLen = sizeof( ucFromNumberBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen, + ucFromNumberBuf, FALSE, FALSE))) + { + goto Exit; + } + pucFromBuf = &ucFromNumberBuf [0]; + break; + + case XFLM_INT64_VAL: + i64Value = pFromValue->val.i64Val; + if (!bInclFrom) + { + if (bAscending) + { + i64Value++; + } + else + { + i64Value--; + } + } + if (i64Value < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)-i64Value; + } + else + { + bNeg = FALSE; + ui64Value = (FLMUINT64)i64Value; + } + + uiFromBufLen = sizeof( ucFromNumberBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiFromBufLen, + ucFromNumberBuf, bNeg, FALSE))) + { + goto Exit; + } + pucFromBuf = &ucFromNumberBuf [0]; + break; + + case XFLM_BINARY_VAL: + pucFromBuf = pFromValue->val.pucBuf; + uiFromBufLen = pFromValue->uiDataLen; + if (!bInclFrom) + { + + // Should use EXCLUSIVE_GT_FLAG even if in descending + // order, because the comparison routines will take + // that into account. + + uiFromFlags |= EXCLUSIVE_GT_FLAG; + } + break; + + default: + + // Text type should have been taken care of elsewhere. + + rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); + goto Exit; + } + } + + // Set up until buffer. + + if (!pUntilValue) + { + pucUntilBuf = NULL; + uiUntilBufLen = 0; + } + else if (pUntilValue == pFromValue) + { + pucUntilBuf = pucFromBuf; + uiUntilBufLen = uiFromBufLen; + } + else + { + switch (pUntilValue->eValType) + { + case XFLM_UINT_VAL: + uiValue = pUntilValue->val.uiVal; + if (!bInclUntil) + { + if (bAscending) + { + uiValue--; + } + else + { + uiValue++; + } + } + uiUntilBufLen = sizeof( ucUntilNumberBuf); + if( RC_BAD( rc = FlmUINT2Storage( uiValue, &uiUntilBufLen, + ucUntilNumberBuf))) + { + goto Exit; + } + pucUntilBuf = &ucUntilNumberBuf [0]; + break; + + case XFLM_INT_VAL: + iValue = pUntilValue->val.iVal; + if (!bInclUntil) + { + if (bAscending) + { + iValue--; + } + else + { + iValue++; + } + } + uiUntilBufLen = sizeof( ucUntilNumberBuf); + if (RC_BAD( rc = FlmINT2Storage( iValue, &uiUntilBufLen, + ucUntilNumberBuf))) + { + goto Exit; + } + pucUntilBuf = &ucUntilNumberBuf [0]; + break; + + case XFLM_UINT64_VAL: + ui64Value = pUntilValue->val.ui64Val; + if (!bInclUntil) + { + if (bAscending) + { + ui64Value--; + } + else + { + ui64Value++; + } + } + uiUntilBufLen = sizeof( ucUntilNumberBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen, + ucUntilNumberBuf, FALSE, FALSE))) + { + goto Exit; + } + pucUntilBuf = &ucUntilNumberBuf [0]; + break; + + case XFLM_INT64_VAL: + i64Value = pUntilValue->val.i64Val; + if (!bInclUntil) + { + if (bAscending) + { + i64Value--; + } + else + { + i64Value++; + } + } + if (i64Value < 0) + { + bNeg = TRUE; + ui64Value = (FLMUINT64)-i64Value; + } + else + { + bNeg = FALSE; + ui64Value = (FLMUINT64)i64Value; + } + + uiUntilBufLen = sizeof( ucUntilNumberBuf); + if (RC_BAD( rc = flmNumber64ToStorage( ui64Value, &uiUntilBufLen, + ucUntilNumberBuf, bNeg, FALSE))) + { + goto Exit; + } + pucUntilBuf = &ucUntilNumberBuf [0]; + break; + + case XFLM_BINARY_VAL: + pucUntilBuf = pUntilValue->val.pucBuf; + uiUntilBufLen = pUntilValue->uiDataLen; + if (!bInclUntil) + { + + // Should use EXCLUSIVE_LT_FLAG even if in descending + // order, because the comparison routines will take + // that into account. + + uiUntilFlags |= EXCLUSIVE_LT_FLAG; + } + break; + + default: + + // Text type should have been taken care of elsewhere. + + rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); + goto Exit; + } + } + + // Generate the keys using the from and until buffers that + // have been set up. + + if (!pucFromBuf && !pucUntilBuf) + { + + // setup a first-to-last key + + flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, + pucUntilKeyLenPos, puiUntilKeyLen); + goto Exit; + } + + // Set up the from key + + if (!pucFromBuf) + { + uiFromKeyLen = KEY_LOW_VALUE; + } + else + { + F_BufferIStream bufferIStream; + + if (RC_BAD( rc = bufferIStream.open( pucFromBuf, uiFromBufLen))) + { + goto Exit; + } + + uiFromKeyLen = MAX_KEY_SIZ - 2; + bDataTruncated = FALSE; + + // Pass 0 for compare rules on non-text component. + + if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, + &bufferIStream, icdGetDataType( pIcd), + pIcd->uiFlags, 0, + pIcd->uiLimit, NULL, NULL, + pIxd->uiLanguage, FALSE, FALSE, + &bDataTruncated, NULL))) + { + goto Exit; + } + + if (bDataTruncated) + { + *pbCanCompareOnKey = FALSE; + + // Save the original data into pFromSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + // Better only be a binary data type at this point. + + flmAssert( pFromValue->eValType == XFLM_BINARY_VAL); + if (RC_BAD( rc = pFromSearchKey->setBinary( pIcd->uiKeyComponent - 1, + pucFromBuf, uiFromBufLen))) + { + goto Exit; + } + uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + } + + // Set up the until key + + if (!pucUntilBuf) + { + uiUntilKeyLen = KEY_HIGH_VALUE; + } + else if (pucUntilBuf == pucFromBuf) + { + if (uiFromKeyLen) + { + f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); + } + uiUntilKeyLen = uiFromKeyLen; + + // The "exclusive" flags better not have been set in this + // case - because this should only be possible if the operator + // was an EQ. + + flmAssert( !(uiFromFlags & EXCLUSIVE_GT_FLAG) && + !(uiUntilFlags & EXCLUSIVE_LT_FLAG)); + + if (uiFromFlags & SEARCH_KEY_FLAG) + { + + // Save the original data into pUntilSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + // Better only be a binary data type at this point. + + flmAssert( pUntilValue->eValType == XFLM_BINARY_VAL); + if (RC_BAD( rc = pUntilSearchKey->setBinary( pIcd->uiKeyComponent - 1, + pucUntilBuf, uiUntilBufLen))) + { + goto Exit; + } + uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + } + else + { + F_BufferIStream bufferIStream; + + if (RC_BAD( rc = bufferIStream.open( pucUntilBuf, uiUntilBufLen))) + { + goto Exit; + } + uiUntilKeyLen = MAX_KEY_SIZ - 2; + bDataTruncated = FALSE; + + // Pass 0 for compare rule because it is a non-text piece. + + if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen, + &bufferIStream, icdGetDataType( pIcd), + pIcd->uiFlags, 0, + pIcd->uiLimit, NULL, NULL, + pIxd->uiLanguage, FALSE, FALSE, + &bDataTruncated, NULL))) + { + goto Exit; + } + + if (bDataTruncated) + { + *pbCanCompareOnKey = FALSE; + + // Save the original data into pUntilSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + // Better only be a binary data type at this point. + + flmAssert( pUntilValue->eValType == XFLM_BINARY_VAL); + if (RC_BAD( rc = pUntilSearchKey->setBinary( pIcd->uiKeyComponent - 1, + pucUntilBuf, uiUntilBufLen))) + { + goto Exit; + } + uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + } + } + + UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos); + UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos); + + if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < MAX_KEY_SIZ - 2) + { + uiFromKeyLen += kyAddInclComponent( pIcd, &pucFromKey [uiFromKeyLen], + TRUE, MAX_KEY_SIZ - 2 - uiFromKeyLen); + } + if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < MAX_KEY_SIZ - 2) + { + uiUntilKeyLen += kyAddInclComponent( pIcd, &pucUntilKey [uiUntilKeyLen], + FALSE, MAX_KEY_SIZ - 2 - uiUntilKeyLen); + } + + // Set the FROM and UNTIL key length return values. + + if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE) + { + *puiFromKeyLen = uiFromKeyLen + 2; + } + else + { + *puiFromKeyLen = 2; + } + if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE) + { + *puiUntilKeyLen = uiUntilKeyLen + 2; + } + else + { + *puiUntilKeyLen = 2; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Finds the location of a wildcard in the internal text string, if any. +****************************************************************************/ +FSTATIC RCODE flmUTF8FindWildcard( + const FLMBYTE * pucValue, + FLMUINT * puiCharPos, + FLMUINT * puiCompareRules) +{ + RCODE rc = NE_XFLM_OK; + const FLMBYTE * pucSaveVal; + const FLMBYTE * pucStart = pucValue; + FLMUNICODE uzChar; + FLMUINT uiCompareRules = *puiCompareRules; + + flmAssert( pucValue); + *puiCharPos = FLM_MAX_UINT; + + for( ;;) + { + pucSaveVal = pucValue; + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucValue, NULL, &uzChar))) + { + goto Exit; + } + + if (!uzChar) + { + break; + } + + if ((uzChar = flmConvertChar( uzChar, uiCompareRules)) == 0) + { + continue; + } + if (uzChar == ASCII_WILDCARD) + { + *puiCharPos = (FLMUINT)(pucSaveVal - pucStart); + goto Exit; + } + if (uzChar != ASCII_SPACE) + { + + // Once we hit a non-space character - except for the wildcard, + // we can remove the ignore leading space rule. + + uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); + if (uzChar == ASCII_BACKSLASH) + { + + // Skip the escaped character + + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucValue, NULL, &uzChar))) + { + goto Exit; + } + + if (!uzChar) + { + rc = RC_SET( NE_XFLM_SYNTAX); + goto Exit; + } + } + } + } + +Exit: + + *puiCompareRules = uiCompareRules; + + return( rc); +} + +/**************************************************************************** +Desc: Count the number of characters that would be returned. +****************************************************************************/ +FSTATIC RCODE flmCountCharacters( + const FLMBYTE * pucValue, + FLMUINT uiValueLen, + FLMUINT uiMaxToCount, + FLMUINT * puiCompareRules, + FLMUINT * puiCharCount) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumChars = 0; + FLMUINT uiCompareRules = *puiCompareRules; + const FLMBYTE * pucEnd = &pucValue [uiValueLen]; + FLMUNICODE uzChar; + FLMBOOL bLastCharWasSpace = FALSE; + FLMUINT uiNumSpaces = 0; + + while (uiNumChars < uiMaxToCount) + { + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucValue, pucEnd, &uzChar))) + { + goto Exit; + } + + if (!uzChar) + { + if (bLastCharWasSpace) + { + // The spaces are trailing spaces, so if the ignore trailing + // space flag is set, we do nothing. If the compress space + // flag is set, we will increment by one. Otherwise, we will + // add in a count for all of the spaces. + + if (!(uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE)) + { + if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) + { + uiNumChars++; + } + else + { + uiNumChars += uiNumSpaces; + } + } + } + break; + } + + if ((uzChar = flmConvertChar( uzChar, uiCompareRules)) == 0) + { + continue; + } + + if (uzChar == ASCII_SPACE) + { + if (!bLastCharWasSpace) + { + bLastCharWasSpace = TRUE; + uiNumSpaces = 0; + } + uiNumSpaces++; + } + else + { + + // Once we hit a non-space character, disable the ignore + // leading space flag. + + uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); + if (bLastCharWasSpace) + { + bLastCharWasSpace = FALSE; + if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) + { + + // Consecutive spaces are compressed to a single space. + + uiNumChars++; + } + else + { + + // The spaces were not trailing spaces and were not compressed + // so we need to count all of them. + + uiNumChars += uiNumSpaces; + } + } + if (uzChar == ASCII_BACKSLASH) + { + + // Skip the next character, no matter what it is - only want + // to count one character here. A backslash followed by any + // character is only a single character. + + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucValue, pucEnd, &uzChar))) + { + goto Exit; + } + } + uiNumChars++; + } + } + +Exit: + + *puiCharCount = uiNumChars; + *puiCompareRules = uiCompareRules; + return( rc); +} + +/**************************************************************************** +Desc: Select the best substring for a CONTAINS or MATCH_END search. +****************************************************************************/ +FSTATIC RCODE flmSelectBestSubstr( + const FLMBYTE ** ppucValue, // [in/out] + FLMUINT * puiValueLen, // [in/out] + FLMUINT * puiCompareRules, + FLMBOOL * pbTrailingWildcard, // [in] change if found a wildcard + FLMBOOL * pbNotUsingFirstOfString) +{ + RCODE rc = NE_XFLM_OK; + const FLMBYTE * pucValue = *ppucValue; + const FLMBYTE * pucCurValue; + const FLMBYTE * pucBest; + const FLMBYTE * pucEnd; + const FLMBYTE * pucTmp; + FLMBOOL bBestTerminatesWithWildCard = *pbTrailingWildcard; + FLMUINT uiCurLen; + FLMUINT uiBestNumChars; + FLMUINT uiBestValueLen; + FLMUINT uiWildcardPos; + FLMUINT uiTargetNumChars; + FLMUINT uiNumChars; + FLMBOOL bNotUsingFirstOfString = FALSE; + FLMUNICODE uzChar; + FLMUNICODE uzDummy; + +#define GOOD_ENOUGH_CHARS 16 + + // There may not be any wildcards at all. Find the first one. + + if (RC_BAD( rc = flmUTF8FindWildcard( pucValue, + &uiWildcardPos, puiCompareRules))) + { + goto Exit; + } + + // FLM_MAX_UINT is returned if no wildcard was found. + + if (uiWildcardPos == FLM_MAX_UINT) + { + goto Exit; + } + + pucEnd = &pucValue [*puiValueLen]; + bBestTerminatesWithWildCard = TRUE; + pucBest = pucValue; + + // Skip past the wildcard + + pucTmp = &pucValue [uiWildcardPos]; + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy))) + { + goto Exit; + } + + uiCurLen = *puiValueLen - (FLMUINT)(pucTmp - pucValue); + pucCurValue = pucTmp; + + uiBestValueLen = uiWildcardPos; + if (RC_BAD( rc = flmCountCharacters( pucValue, uiWildcardPos, + GOOD_ENOUGH_CHARS, puiCompareRules, &uiBestNumChars))) + { + goto Exit; + } + uiTargetNumChars = uiBestNumChars + uiBestNumChars; + + // Here is the great FindADoubleLengthThatIsBetter algorithm. + // Below are the values to pick a next better contains key. + // First Key Size Next Key Size that will be used + // 1 * 2 2 // Single char searches are REALLY BAD + // 2 * 2 4 + // 3 * 2 6 + // 4 * 2 8 + // ... ... + // At each new key piece, increment the target length by 2 so that it + // will be even harder to find a better key. + + while (uiBestNumChars < GOOD_ENOUGH_CHARS) + { + pucTmp = pucCurValue; + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucTmp, pucEnd, &uzChar))) + { + goto Exit; + } + + if (!uzChar) + { + break; + } + + if (RC_BAD( rc = flmUTF8FindWildcard( pucCurValue, &uiWildcardPos, + puiCompareRules))) + { + goto Exit; + } + + // FLM_MAX_UINT is returned when no wildcard is found. + + if (uiWildcardPos == FLM_MAX_UINT) + { + + // No wildcard found + // Check the last section that may or may not have trailing *. + + if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiCurLen, + GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars))) + { + goto Exit; + } + + if (uiNumChars >= uiTargetNumChars) + { + pucBest = pucCurValue; + uiBestValueLen = uiCurLen; + bBestTerminatesWithWildCard = *pbTrailingWildcard; + } + break; + } + else + { + if (RC_BAD( rc = flmCountCharacters( pucCurValue, uiWildcardPos, + GOOD_ENOUGH_CHARS, puiCompareRules, &uiNumChars))) + { + goto Exit; + } + + if (uiNumChars >= uiTargetNumChars) + { + pucBest = pucCurValue; + uiBestValueLen = uiWildcardPos; + uiBestNumChars = uiNumChars; + uiTargetNumChars = uiNumChars + uiNumChars; + } + else + { + uiTargetNumChars += 2; + } + + // Skip past the wildcard + + pucTmp = &pucCurValue[ uiWildcardPos]; + if (RC_BAD( rc = flmGetCharFromUTF8Buf( &pucTmp, pucEnd, &uzDummy))) + { + goto Exit; + } + + uiCurLen -= (FLMUINT)(pucTmp - pucCurValue); + pucCurValue = pucTmp; + } + } + + if (pucBest != *ppucValue) + { + bNotUsingFirstOfString = TRUE; + } + + *ppucValue = pucBest; + *puiValueLen = uiBestValueLen; + *pbTrailingWildcard = bBestTerminatesWithWildCard; + +Exit: + + *pbNotUsingFirstOfString = bNotUsingFirstOfString; + return( rc); +} + +/**************************************************************************** +Desc: Set the case byte on the from key for a case-insensitive search + that is using a case sensitive index. +****************************************************************************/ +FSTATIC void setFromCaseByte( + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + FLMUINT uiCaseLen, + FLMBOOL bIsDBCS, + FLMBOOL bAscending, + FLMBOOL bExcl) +{ + + // Subtract off all but the case marker. + // Remember that for DBCS (Asian) the case marker is two bytes. + + *puiFromKeyLen -= (uiCaseLen - + ((FLMUINT)(bIsDBCS + ? (FLMUINT)2 + : (FLMUINT)1))); + if (bExcl) + { + if (bAscending) + { + // Keys are in ascending order: + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // Thus, to exclude all "abc"s on "from" side we need the + // following key: + // key == abc+6 (COLL_MARKER | SC_UPPER) + 1 + + pucFromKey[ *puiFromKeyLen - 1] = (COLL_MARKER | SC_UPPER); + } + else + { + + // Keys are in descending order: + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // Thus, to exclude "abc"s on "from" side we need the + // following key: + // key == abc+4 (COLL_MARKER | SC_LOWER) + + pucFromKey[ *puiFromKeyLen - 1] = (COLL_MARKER | SC_LOWER); + } + } + else // Inclusive + { + if (bAscending) + { + // Keys are in ascending order: + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // Thus, to include all "abc"s on "from" side, + // we need the following key: + // key == abc+4 (COLL_MARKER | SC_LOWER) + + pucFromKey [*puiFromKeyLen - 1] = COLL_MARKER | SC_LOWER; + } + else + { + + // Keys are in descending order: + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // Thus, to include all "abc"s on "from" side we need the + // following key: + // key == abc+6 (COLL_MARKER | SC_UPPER) + + pucFromKey [*puiFromKeyLen - 1] = COLL_MARKER | SC_UPPER; + } + } +} + +/**************************************************************************** +Desc: Set the case byte on the until key for a case-insensitive search + that is using a case sensitive index. +****************************************************************************/ +FSTATIC void setUntilCaseByte( + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMUINT uiCaseLen, + FLMBOOL bIsDBCS, + FLMBOOL bAscending, + FLMBOOL bExcl) +{ + + // Subtract off all but the case marker. + // Remember that for DBCS (Asian) the case marker is two bytes. + + *puiUntilKeyLen -= (uiCaseLen - + ((FLMUINT)(bIsDBCS + ? (FLMUINT)2 + : (FLMUINT)1))); + if (bExcl) + { + if (bAscending) + { + // Keys are in ascending order: + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // Thus, to exclude all "abc"s on the "until" side we need + // the following key: + // key == abc+4 (COLL_MARKER | SC_LOWER) + + pucUntilKey[ *puiUntilKeyLen - 1] = (COLL_MARKER | SC_LOWER); + } + else + { + + // Keys are in descending order: + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // Thus, to exclude all "abc"s on the "until" side we need + // the following key: + // key == abc+6 (COLL_MARKER | SC_UPPER) + 1 + + pucUntilKey[ *puiUntilKeyLen - 1] = (COLL_MARKER | SC_UPPER); + } + } + else + { + if (bAscending) + { + // Keys are in ascending order: + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // Thus, to get include all "abc"s on the "until" side we need + // the following key: + // key == abc+6 (COLL_MARKER | SC_UPPER) + + pucUntilKey [*puiUntilKeyLen - 1] = (COLL_MARKER | SC_UPPER); + } + else + { + + // Keys are in descending order: + // "ABC" key == abc+6 (6 is COLL_MARKER | SC_UPPER) + // "abc" key == abc+4 (4 is COLL_MARKER | SC_LOWER) + // Thus, to include all "abc"s on the "until side we need + // the following key: + // key == abc+4 (COLL_MARKER | SC_LOWER) + + pucUntilKey [*puiUntilKeyLen - 1] = (COLL_MARKER | SC_LOWER); + } + } +} + +/**************************************************************************** +Desc: Build a text key. +****************************************************************************/ +FSTATIC RCODE flmAddTextKeyPiece( + PATH_PRED * pPred, + IXD * pIxd, + ICD * pIcd, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbCanCompareOnKey) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFromKeyLen = 0; + FLMUINT uiUntilKeyLen = 0; + FLMBYTE * pucFromKeyLenPos = pucFromKey; + FLMBYTE * pucUntilKeyLenPos = pucUntilKey; + FLMUINT uiLanguage = pIxd->uiLanguage; + FLMUINT uiCollationLen = 0; + FLMUINT uiCharCount; + FLMUINT uiCaseLen; + FLMBOOL bOriginalCharsLost = FALSE; + FLMBOOL bIsDBCS = (uiLanguage >= FIRST_DBCS_LANG && + uiLanguage <= LAST_DBCS_LANG) + ? TRUE + : FALSE; + + FLMBOOL bCaseInsensitive = (FLMBOOL)((pPred->uiCompareRules & + XFLM_COMP_CASE_INSENSITIVE) + ? TRUE + : FALSE); + FLMBOOL bDoFirstSubstring = (FLMBOOL)((pIcd->uiFlags & ICD_SUBSTRING) + ? TRUE + : FALSE); + FLMBOOL bDoMatchBegin = FALSE; + FLMBOOL bTrailingWildcard = FALSE; + const FLMBYTE * pucFromUTF8Buf = NULL; + FLMUINT uiFromBufLen = 0; + const FLMBYTE * pucUntilUTF8Buf = NULL; + FLMUINT uiUntilBufLen = 0; + FLMUINT uiWildcardPos; + FLMBOOL bDataTruncated; + FLMUINT uiFromFlags = 0; + FLMUINT uiUntilFlags = 0; + FLMUINT uiCompareRules; + FLMBOOL bAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE: TRUE; + + switch (pPred->eOperator) + { + + // The difference between MATCH and EQ_OP is that EQ does + // not support wildcards embedded in the search key. + + case XFLM_MATCH_OP: + flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); + pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; + uiFromBufLen = pPred->pFromValue->uiDataLen; + uiCompareRules = pIcd->uiCompareRules; + + if (RC_BAD( rc = flmUTF8FindWildcard( pucFromUTF8Buf, &uiWildcardPos, + &uiCompareRules))) + { + goto Exit; + } + + // If there is no wildcard, uiWildcardPos will be FLM_MAX_UINT + + if (uiWildcardPos != FLM_MAX_UINT) + { + + // If wildcard is in position 0, it is NOT + // a match begin. + + if (uiWildcardPos) + { + bDoMatchBegin = TRUE; + uiFromBufLen = uiWildcardPos; + } + } + if (!(pIcd->uiFlags & ICD_SUBSTRING)) + { + + // Index is NOT a substring index + + if (!bDoMatchBegin) + { + + // Wildcard was at the beginning, will have + // to search the index from first to last + + pucFromUTF8Buf = NULL; + } + else + { + bTrailingWildcard = TRUE; + } + } + else + { + FLMBOOL bNotUsingFirstOfString; + + // If this is a substring index look for a + // better 'contains' string to search for. + // We don't like "A*BCDEFG" searches. + + bTrailingWildcard = bDoMatchBegin; + + uiCompareRules = pIcd->uiCompareRules; + if (RC_BAD( rc = flmSelectBestSubstr( &pucFromUTF8Buf, + &uiFromBufLen, + &uiCompareRules, &bTrailingWildcard, + &bNotUsingFirstOfString))) + { + goto Exit; + } + + if (bNotUsingFirstOfString) + { + bDoMatchBegin = bTrailingWildcard; + *pbCanCompareOnKey = FALSE; + bDoFirstSubstring = FALSE; + } + else if (bTrailingWildcard) + { + bDoMatchBegin = TRUE; + } + + if (RC_BAD( rc = flmCountCharacters( pucFromUTF8Buf, + uiFromBufLen, 2, + &uiCompareRules, &uiCharCount))) + { + goto Exit; + } + + // Special case: Single character contains/MEnd in a substr ix. + + if (!bIsDBCS && uiCharCount < 2) + { + pucFromUTF8Buf = NULL; + } + } + + pucUntilUTF8Buf = pucFromUTF8Buf; + uiUntilBufLen = uiFromBufLen; + break; + + case XFLM_RANGE_OP: + if (bAscending) + { + if (pPred->pFromValue) + { + flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); + pucFromUTF8Buf = pPred->pFromValue->val.pucBuf; + uiFromBufLen = pPred->pFromValue->uiDataLen; + } + else + { + // Should have been done up above + + // pucFromUTF8Buf = NULL; + // uiFromBufLen = 0; + } + if (pPred->pUntilValue) + { + flmAssert( pPred->pUntilValue->eValType == XFLM_UTF8_VAL); + pucUntilUTF8Buf = pPred->pUntilValue->val.pucBuf; + uiUntilBufLen = pPred->pUntilValue->uiDataLen; + } + else + { + // Should have been done up above. + + // pucUntilUTF8Buf = NULL; + // uiUntilBufLen = 0; + } + if (!pPred->bInclFrom) + { + uiFromFlags |= EXCLUSIVE_GT_FLAG; + } + if (!pPred->bInclUntil) + { + uiUntilFlags |= EXCLUSIVE_LT_FLAG; + } + } + else + { + if (pPred->pUntilValue) + { + flmAssert( pPred->pUntilValue->eValType == XFLM_UTF8_VAL); + pucFromUTF8Buf = pPred->pUntilValue->val.pucBuf; + uiFromBufLen = pPred->pUntilValue->uiDataLen; + } + else + { + // Should have been done up above + + // pucFromUTF8Buf = NULL; + // uiFromBufLen = 0; + } + + if (pPred->pFromValue) + { + flmAssert( pPred->pFromValue->eValType == XFLM_UTF8_VAL); + pucUntilUTF8Buf = pPred->pFromValue->val.pucBuf; + uiUntilBufLen = pPred->pFromValue->uiDataLen; + } + else + { + // Should have been done up above. + + // pucUntilUTF8Buf = NULL; + // uiUntilBufLen = 0; + } + if (!pPred->bInclUntil) + { + uiFromFlags |= EXCLUSIVE_GT_FLAG; + } + if (!pPred->bInclFrom) + { + uiUntilFlags |= EXCLUSIVE_LT_FLAG; + } + } + break; + + case XFLM_NE_OP: + + // Set up to do full index scan. + + // Buffers should already be NULL. + + // pucFromUTF8Buf = NULL; + // pucUntilUTF8Buf = NULL; + break; + + case XFLM_APPROX_EQ_OP: + + // Set up to do full index scan. + + // Buffers should already be NULL + + // pucFromUTF8Buf = NULL; + // pucUntilUTF8Buf = NULL; + + // Cannot compare on the key if index is upper case, + // even if the bCaseInsensitive flag is set. + + if (pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) + { + *pbCanCompareOnKey = FALSE; + } + break; + + default: + + // Every predicate should have been converted to one of the above + // cases, or should be handled by another routine. + + rc = RC_SET_AND_ASSERT( NE_XFLM_QUERY_SYNTAX); + goto Exit; + } + + // If index is case insensitive, but search is case sensitive + // we must NOT do a key match - we would fail things we should + // not be failing. + + if ((pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && !bCaseInsensitive) + { + *pbCanCompareOnKey = FALSE; + } + + if (!pucFromUTF8Buf && !pucUntilUTF8Buf) + { + + // setup a first-to-last key + + flmSetupFirstToLastKey( pucFromKeyLenPos, puiFromKeyLen, + pucUntilKeyLenPos, puiUntilKeyLen); + goto Exit; + } + + pucFromKey += 2; + pucUntilKey += 2; + if (!pucFromUTF8Buf) + { + uiFromKeyLen = KEY_LOW_VALUE; + } + else + { + F_BufferIStream bufferIStream; + + if (RC_BAD( rc = bufferIStream.open( pucFromUTF8Buf, uiFromBufLen))) + { + goto Exit; + } + + // Add ICD_ESC_CHAR to the icd flags because + // the search string must have BACKSLASHES and '*' escaped. + + uiFromKeyLen = MAX_KEY_SIZ - 2; + bDataTruncated = FALSE; + if (RC_BAD( rc = KYCollateValue( pucFromKey, &uiFromKeyLen, + &bufferIStream, XFLM_TEXT_TYPE, + pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules, + pIcd->uiLimit, + &uiCollationLen, &uiCaseLen, + uiLanguage, bDoFirstSubstring, + FALSE, &bDataTruncated, + &bOriginalCharsLost))) + { + goto Exit; + } + + if (bDataTruncated) + { + *pbCanCompareOnKey = FALSE; + + // Save the original data into pFromSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + if (RC_BAD( rc = pFromSearchKey->setUTF8( pIcd->uiKeyComponent - 1, + pucFromUTF8Buf, uiFromBufLen))) + { + goto Exit; + } + uiFromFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + else if (bOriginalCharsLost) + { + *pbCanCompareOnKey = FALSE; + } + + if (pucFromUTF8Buf != pucUntilUTF8Buf) + { + + // Handle scenario of a case-sensitive index, but search is + // case-insensitive. + + if (uiFromKeyLen && + (bIsDBCS || + (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && + bCaseInsensitive))) + { + setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, + bIsDBCS, bAscending, + (uiFromFlags & EXCLUSIVE_GT_FLAG) + ? TRUE + : FALSE); + } + } + else + { + // Handle case where from and until buffers are the same. + // This should only be possible in the equality case or match begin + // case, in which cases neither the EXCLUSIVE_LT_FLAG or the + // EXCLUSIVE_GT_FLAG should be set. + + flmAssert( uiFromBufLen == uiUntilBufLen); + flmAssert( !(uiFromFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG))); + flmAssert( !(uiUntilFlags & (EXCLUSIVE_GT_FLAG | EXCLUSIVE_LT_FLAG))); + + if (uiFromFlags & SEARCH_KEY_FLAG) + { + + // Save the original data into pUntilSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + if (RC_BAD( rc = pUntilSearchKey->setUTF8( pIcd->uiKeyComponent - 1, + pucUntilUTF8Buf, uiUntilBufLen))) + { + goto Exit; + } + uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + + if (bDoMatchBegin) + { + if (bAscending) + { + + // Handle scenario of a case-sensitive index, but search is + // case-insensitive. + + if (uiFromKeyLen && + (bIsDBCS || + (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && + bCaseInsensitive))) + { + setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, + bIsDBCS, bAscending, FALSE); + } + + // From key is set up properly, setup until key. + + if (uiCollationLen) + { + f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); + } + + // Fill the rest of the until key with high values. + + f_memset( &pucUntilKey[ uiCollationLen], 0xFF, + MAX_KEY_SIZ - uiCollationLen - 2); + uiUntilKeyLen = MAX_KEY_SIZ - 2; + } + else + { + + // Copy from key into until key. + + if (uiFromKeyLen) + { + f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); + } + uiUntilKeyLen = uiFromKeyLen; + + if (uiUntilKeyLen && + (bIsDBCS || + (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && + bCaseInsensitive))) + { + // NOTE: Always inclusive because this is a matchbegin. + + setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, + bIsDBCS, bAscending, FALSE); + } + + // Fill rest of from key with high values after collation values. + + f_memset( &pucFromKey[ uiCollationLen], 0xFF, + MAX_KEY_SIZ - uiCollationLen - 2); + uiFromKeyLen = MAX_KEY_SIZ - 2; + } + } + else + { + + // Copy from key into until key. + + if (!uiFromKeyLen) + { + uiUntilKeyLen = 0; + } + else + { + if (!bDoFirstSubstring) + { + f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); + uiUntilKeyLen = uiFromKeyLen; + } + else if (bAscending) + { + + // Do two copies so that the first substring byte is gone + // in the until key. + + f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); + uiUntilKeyLen = uiCollationLen; + if (bIsDBCS) + { + uiCollationLen++; + } + uiCollationLen++; + f_memcpy( &pucUntilKey [uiUntilKeyLen], + pucFromKey + uiCollationLen, + uiFromKeyLen - uiCollationLen); + uiUntilKeyLen += (uiFromKeyLen - uiCollationLen); + } + else + { + + // Descending order - put the string without the + // first-substring-marker into the from key instead of + // the until key. + + f_memcpy( pucUntilKey, pucFromKey, uiFromKeyLen); + uiUntilKeyLen = uiFromKeyLen; + + // Modify from key to NOT have first-substring-marker. + + f_memcpy( pucUntilKey, pucFromKey, uiCollationLen); + uiFromKeyLen = uiCollationLen; + if (bIsDBCS) + { + uiCollationLen++; + } + uiCollationLen++; + f_memcpy( &pucFromKey [uiFromKeyLen], + pucUntilKey + uiCollationLen, + uiUntilKeyLen - uiCollationLen); + uiFromKeyLen += (uiUntilKeyLen - uiCollationLen); + } + + // Handle scenario of a case-sensitive index, but search is + // case-insensitive. + + if (bIsDBCS || + (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && + bCaseInsensitive)) + { + setFromCaseByte( pucFromKey, &uiFromKeyLen, uiCaseLen, + bIsDBCS, bAscending, FALSE); + setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, + bIsDBCS, bAscending, FALSE); + } + } + } + } + } + + // Do the until key now + + if (!pucUntilUTF8Buf) + { + uiUntilKeyLen = KEY_HIGH_VALUE; + } + else if (pucFromUTF8Buf != pucUntilUTF8Buf) + { + F_BufferIStream bufferIStream; + + if (RC_BAD( rc = bufferIStream.open( pucUntilUTF8Buf, uiUntilBufLen))) + { + goto Exit; + } + + // Add ICD_ESC_CHAR to the icd flags because + // the search string must have BACKSLASHES and '*' escaped. + + uiUntilKeyLen = MAX_KEY_SIZ - 2; + bDataTruncated = FALSE; + if (RC_BAD( rc = KYCollateValue( pucUntilKey, &uiUntilKeyLen, + &bufferIStream, XFLM_TEXT_TYPE, + pIcd->uiFlags | ICD_ESC_CHAR, pIcd->uiCompareRules, + pIcd->uiLimit, + &uiCollationLen, &uiCaseLen, + uiLanguage, bDoFirstSubstring, + FALSE, &bDataTruncated, + &bOriginalCharsLost))) + { + goto Exit; + } + + if (bDataTruncated) + { + + // Save the original data into pUntilSearchKey so the comparison + // routines can do a comparison on the full value if + // necessary. + + if (RC_BAD( rc = pUntilSearchKey->setUTF8( pIcd->uiKeyComponent - 1, + pucUntilUTF8Buf, uiUntilBufLen))) + { + goto Exit; + } + *pbCanCompareOnKey = FALSE; + uiUntilFlags |= (SEARCH_KEY_FLAG | TRUNCATED_FLAG); + } + else if (bOriginalCharsLost) + { + *pbCanCompareOnKey = FALSE; + } + + if (uiUntilKeyLen && + (bIsDBCS || + (!(pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) && + bCaseInsensitive))) + { + setUntilCaseByte( pucUntilKey, &uiUntilKeyLen, uiCaseLen, + bIsDBCS, bAscending, + (uiUntilFlags & EXCLUSIVE_LT_FLAG) + ? TRUE + : FALSE); + } + } + + UW2FBA( (FLMUINT16)(uiFromKeyLen | uiFromFlags), pucFromKeyLenPos); + UW2FBA( (FLMUINT16)(uiUntilKeyLen | uiUntilFlags), pucUntilKeyLenPos); + + if (!(uiFromFlags & EXCLUSIVE_GT_FLAG) && uiFromKeyLen < MAX_KEY_SIZ - 2) + { + uiFromKeyLen += kyAddInclComponent( pIcd, &pucFromKey [uiFromKeyLen], + TRUE, MAX_KEY_SIZ - 2 - uiFromKeyLen); + } + if (!(uiUntilFlags & EXCLUSIVE_LT_FLAG) && uiUntilKeyLen < MAX_KEY_SIZ - 2) + { + uiUntilKeyLen += kyAddInclComponent( pIcd, &pucUntilKey [uiUntilKeyLen], + FALSE, MAX_KEY_SIZ - 2 - uiUntilKeyLen); + } + + // Set the FROM and UNTIL key lengths + + if (uiFromKeyLen != KEY_HIGH_VALUE && uiFromKeyLen != KEY_LOW_VALUE) + { + *puiFromKeyLen = uiFromKeyLen + 2; + } + else + { + *puiFromKeyLen = 2; + } + if (uiUntilKeyLen != KEY_HIGH_VALUE && uiUntilKeyLen != KEY_LOW_VALUE) + { + *puiUntilKeyLen = uiUntilKeyLen + 2; + } + else + { + *puiUntilKeyLen = 2; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Build the from and until keys given a field list with operators and + values and an index. +Notes: The knowledge of query definitions is limited in these routines. +****************************************************************************/ +RCODE flmBuildFromAndUntilKeys( + IXD * pIxd, + PATH_PRED * pPred, + F_DataVector * pFromSearchKey, + FLMBYTE * pucFromKey, + FLMUINT * puiFromKeyLen, + F_DataVector * pUntilSearchKey, + FLMBYTE * pucUntilKey, + FLMUINT * puiUntilKeyLen, + FLMBOOL * pbDoNodeMatch, + FLMBOOL * pbCanCompareOnKey + ) +{ + RCODE rc = NE_XFLM_OK; + ICD * pIcd = pIxd->pFirstKey; + + *puiFromKeyLen = *puiUntilKeyLen = 0; + *pbDoNodeMatch = FALSE; + *pbCanCompareOnKey = TRUE; + + if (!pPred) + { + + // Setup a first-to-last key + + flmSetupFirstToLastKey( pucFromKey, puiFromKeyLen, + pucUntilKey, puiUntilKeyLen); + *pbDoNodeMatch = TRUE; + *pbCanCompareOnKey = FALSE; + } + else + { + + // Predicates we are looking at should NEVER be notted. They + // will have been weeded out earlier. + + flmAssert( !pPred->bNotted); + + // Handle special cases for indexing presence and/or exists predicate. + + + if (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && + !(pIcd->uiFlags & (ICD_PRESENCE | ICD_METAPHONE)) && + pPred->eOperator != XFLM_EXISTS_OP) + { + if (RC_BAD( rc = flmAddTextKeyPiece( pPred, pIxd, + pIcd, pFromSearchKey, pucFromKey, puiFromKeyLen, + pUntilSearchKey, pucUntilKey, puiUntilKeyLen, + pbCanCompareOnKey))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = flmAddNonTextKeyPiece( pPred, pIxd, pIcd, + pFromSearchKey, pucFromKey, puiFromKeyLen, + pUntilSearchKey, pucUntilKey, puiUntilKeyLen, + pbCanCompareOnKey))) + { + goto Exit; + } + } + } + +Exit: + + if (!(*pbCanCompareOnKey)) + { + *pbDoNodeMatch = TRUE; + } + + return( rc); +} + diff --git a/version5/src/kybuild.cpp b/version5/src/kybuild.cpp new file mode 100644 index 0000000..2433548 --- /dev/null +++ b/version5/src/kybuild.cpp @@ -0,0 +1,3281 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the main routines for building of index keys, +// and adding them to the database. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: kybuild.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define STACK_DATA_BUF_SIZE 64 + +typedef struct NodeTraverseTag * NODE_TRAV_p; +typedef struct AnchorNodeTag * ANCHOR_NODE_p; + +typedef struct AnchorNodeTag +{ + FLMUINT64 ui64AnchorNodeId; + FLMUINT uiAnchorNameId; + eDomNodeType eNodeType; + FLMBOOL bSeeIfRepeatingSibs; + ANCHOR_NODE_p pNext; +} ANCHOR_NODE; + +typedef struct NodeTraverseTag +{ + ANCHOR_NODE * pAnchorNode; + F_DOMNode * pNode; + FLMBOOL bInNodeSubtree; + ICD * pIcd; + FLMUINT uiSibIcdAttrs; + FLMUINT uiSibIcdElms; + FLMBOOL bTraverseChildren; + FLMBOOL bTraverseSibs; + NODE_TRAV_p pParent; + NODE_TRAV_p pChild; +} NODE_TRAV; + +// Local function prototypes + +FSTATIC RCODE kyAddIDsToKey( + FLMUINT64 ui64DocumentID, + IXD * pIxd, + CDL_HDR * pCdlTbl, + FLMBYTE * pucKeyBuf, + FLMUINT uiIDBufSize, + FLMUINT * puiIDLen); + +FSTATIC RCODE kySeeIfRepeatingSibs( + F_Db * pDb, + F_DOMNode * pNode, + FLMBOOL * pbHadRepeatingSib); + +FSTATIC RCODE kyFindChildNode( + F_Db * pDb, + F_Pool * pPool, + NODE_TRAV ** ppTrav, + FLMBOOL * pbGotChild, + FLMBOOL * pbHadRepeatingSib); + +FSTATIC RCODE kyFindSibNode( + F_Db * pDb, + NODE_TRAV * pTrav, + FLMBOOL bTestFirstNode, + FLMBOOL * pbGotSib, + FLMBOOL * pbHadRepeatingSib); + +/**************************************************************************** +Desc: Append node IDs to the key buffer. +****************************************************************************/ +FSTATIC RCODE kyAddIDsToKey( + FLMUINT64 ui64DocumentID, + IXD * pIxd, + CDL_HDR * pCdlTbl, + FLMBYTE * pucKeyBuf, + FLMUINT uiIDBufSize, + FLMUINT * puiIDLen) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucIDs = pucKeyBuf; + FLMUINT uiSenLen; + FLMUINT uiIDLen = 0; + ICD * pIcd; + FLMUINT64 ui64Id; + FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE * pucTmpSen; + + // Our ID buffer can never exceed MAX_ID_SIZE bytes (which is always + // defined to be 256), because it has a trailing + // length byte that we put on it - which can only represent up to + // 255 bytes. + + if (uiIDBufSize > MAX_ID_SIZE) + { + uiIDBufSize = MAX_ID_SIZE; + } + + // Put document ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (uiIDBufSize - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64DocumentID, &pucIDs); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64DocumentID, &pucTmpSen); + if (uiSenLen + uiIDLen > uiIDBufSize) + { + rc = RC_SET( NE_XFLM_KEY_OVERFLOW); + goto Exit; + } + f_memcpy( pucIDs, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucIDs += uiSenLen; + } + + // Append the key component NODE IDs to the key + + for (pIcd = pIxd->pFirstKey; pIcd; pIcd = pIcd->pNextKeyComponent) + { + ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && + pCdlTbl [pIcd->uiCdl].pCdlList->pNode + ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() + : (FLMUINT64)0); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (uiIDBufSize - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucIDs); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > uiIDBufSize) + { + rc = RC_SET( NE_XFLM_KEY_OVERFLOW); + goto Exit; + } + f_memcpy( pucIDs, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucIDs += uiSenLen; + } + } + + // Append the data NODE IDs to the key + + for (pIcd = pIxd->pFirstData; pIcd; pIcd = pIcd->pNextDataComponent) + { + ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && + pCdlTbl [pIcd->uiCdl].pCdlList->pNode + ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() + : (FLMUINT64)0); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (uiIDBufSize - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucIDs); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > uiIDBufSize) + { + rc = RC_SET( NE_XFLM_KEY_OVERFLOW); + goto Exit; + } + f_memcpy( pucIDs, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucIDs += uiSenLen; + } + } + + // Append the context NODE IDs to the key + + for (pIcd = pIxd->pFirstContext; pIcd; pIcd = pIcd->pNextKeyComponent) + { + ui64Id = (FLMUINT64)(pCdlTbl [pIcd->uiCdl].pCdlList && + pCdlTbl [pIcd->uiCdl].pCdlList->pNode + ? pCdlTbl [pIcd->uiCdl].pCdlList->pNode->getIxNodeId() + : (FLMUINT64)0); + + // Put node ID into buffer. If there is room for at least nine + // bytes, we can encode the ID right into the buffer safely. Otherwise, + // we have to use a temporary buffer and see if there is room. + + if (uiIDBufSize - uiIDLen >= 9) + { + uiIDLen += flmEncodeSEN( ui64Id, &pucIDs); + } + else + { + pucTmpSen = &ucTmpSen [0]; + uiSenLen = flmEncodeSEN( ui64Id, &pucTmpSen); + if (uiSenLen + uiIDLen > uiIDBufSize) + { + rc = RC_SET( NE_XFLM_KEY_OVERFLOW); + goto Exit; + } + f_memcpy( pucIDs, ucTmpSen, uiSenLen); + uiIDLen += uiSenLen; + pucIDs += uiSenLen; + } + } + + *puiIDLen = uiIDLen; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: F_OldNodeList destructor +*****************************************************************************/ +F_OldNodeList::~F_OldNodeList() +{ + if (m_pNodeList) + { + f_free( &m_pNodeList); + } + m_pool.poolFree(); +} + +/***************************************************************************** +Desc: Find a node in the old node list. +*****************************************************************************/ +FLMBOOL F_OldNodeList::findNodeInList( + eDomNodeType eNodeType, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiNameId, + FLMBYTE ** ppucData, + FLMUINT * puiDataLen, + FLMUINT * puiInsertPos) +{ + FLMBOOL bFound = FALSE; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + eDomNodeType eTblNodeType; + FLMUINT uiTblCollection; + FLMUINT uiTblNameId; + FLMUINT64 ui64TblNodeId; + + // Do binary search in the table + + if ((uiTblSize = m_uiNodeCount) == 0) + { + *puiInsertPos = 0; + goto Exit; + } + + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + eTblNodeType = m_pNodeList[ uiMid].eNodeType; + uiTblCollection = m_pNodeList[ uiMid].uiCollection; + ui64TblNodeId = m_pNodeList[ uiMid].ui64NodeId; + uiTblNameId = m_pNodeList[ uiMid].uiNameId; + + flmAssert( eTblNodeType != INVALID_NODE); + flmAssert( uiTblCollection); + flmAssert( ui64TblNodeId); + flmAssert( uiTblNameId); + + if( eTblNodeType == eNodeType && + uiTblCollection == uiCollection && + ui64TblNodeId == ui64NodeId && + uiTblNameId == uiNameId) + { + bFound = TRUE; + *ppucData = m_pNodeList [uiMid].pucData; + *puiDataLen = m_pNodeList [uiMid].uiDataLen; + *puiInsertPos = uiMid; + goto Exit; + } + + // Check if we are done + + if( uiLow >= uiHigh) + { + // Done, item not found + + if( eNodeType >= eTblNodeType) + { + goto CmpGreaterEq; + } + + if( uiCollection >= uiTblCollection) + { + goto CmpGreaterEq; + } + + if( ui64NodeId >= ui64TblNodeId) + { + goto CmpGreaterEq; + } + + if( uiNameId >= uiTblNameId) + { +CmpGreaterEq: + *puiInsertPos = uiMid + 1; + goto Exit; + } + + *puiInsertPos = uiMid; + goto Exit; + } + + if( eNodeType >= eTblNodeType) + { + goto CmpGreaterEq2; + } + + if( uiCollection >= uiTblCollection) + { + goto CmpGreaterEq2; + } + + if( ui64NodeId >= ui64TblNodeId) + { + goto CmpGreaterEq2; + } + + if( uiNameId >= uiTblNameId) + { +CmpGreaterEq2: + if (uiMid == uiTblSize) + { + *puiInsertPos = uiMid + 1; + goto Exit; + } + uiLow = uiMid + 1; + continue; + } + + if (uiMid == 0) + { + *puiInsertPos = 0; + goto Exit; + } + uiHigh = uiMid - 1; + continue; + } + +Exit: + + return( bFound); +} + +/***************************************************************************** +Desc: Add an old node to the old node list. +*****************************************************************************/ +RCODE F_OldNodeList::addNodeToList( + F_Db * pDb, + F_DOMNode * pNode) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiInsertPos; + FLMBYTE * pucData; + FLMUINT uiDataType; + FLMUINT uiDataLen; + FLMUINT uiChars; + FLMUINT uiBufSize; + FLMUINT uiCollection; + FLMUINT uiNameId; + FLMUINT64 ui64NodeId; + + if( RC_BAD( rc = pNode->getCollection( pDb, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pNode->getDataType( pDb, &uiDataType))) + { + goto Exit; + } + + ui64NodeId = pNode->getIxNodeId(); + uiNameId = pNode->getNameId(); + + // See if the node is already there + + if( !findNodeInList( pNode->getNodeType(), + uiCollection, ui64NodeId, + uiNameId, &pucData, &uiDataLen, &uiInsertPos)) + { + // Expand the size of the table. + + if (m_uiNodeCount == m_uiListSize) + { + if (RC_BAD( rc = f_realloc( (m_uiListSize + 20) * + sizeof( OLD_NODE_DATA), &m_pNodeList))) + { + goto Exit; + } + m_uiListSize += 20; + } + + if (uiInsertPos < m_uiNodeCount) + { + f_memmove( &m_pNodeList [uiInsertPos + 1], + &m_pNodeList [uiInsertPos], + sizeof( OLD_NODE_DATA) * (m_uiNodeCount - uiInsertPos)); + } + + m_pNodeList [uiInsertPos].eNodeType = pNode->getNodeType(); + m_pNodeList [uiInsertPos].uiCollection = uiCollection; + m_pNodeList [uiInsertPos].ui64NodeId = ui64NodeId; + m_pNodeList [uiInsertPos].uiNameId = uiNameId; + m_uiNodeCount++; + + // Set up the data - either unicode or binary. + + if( uiDataType == XFLM_BINARY_TYPE) + { + // Get the length first. + + if( RC_BAD( rc = pNode->getDataLength( pDb, &uiBufSize))) + { + goto Exit; + } + + // Allocate the space needed. + + if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize, + (void **)&m_pNodeList [uiInsertPos].pucData))) + { + goto Exit; + } + + // Go back again and get the data now. + + if( RC_BAD( rc = pNode->getBinary( pDb, m_pNodeList [uiInsertPos].pucData, + 0, uiBufSize, NULL))) + { + goto Exit; + } + + m_pNodeList [uiInsertPos].uiDataLen = uiBufSize; + } + else + { + flmAssert( uiDataType == XFLM_TEXT_TYPE); + + // Get the length first. + + if( RC_BAD( rc = pNode->getUnicodeChars( pDb, &uiChars))) + { + goto Exit; + } + + // Allocate the space needed. + + uiBufSize = (uiChars + 1) * sizeof( FLMUNICODE); + if (RC_BAD( rc = m_pool.poolAlloc( uiBufSize, + (void **)&m_pNodeList [uiInsertPos].pucData))) + { + goto Exit; + } + + // Go back again and get the data now. + + if( RC_BAD( rc = pNode->getUnicode( pDb, + (FLMUNICODE *)m_pNodeList [uiInsertPos].pucData, + uiBufSize, 0, FLM_MAX_UINT))) + { + goto Exit; + } + + m_pNodeList [uiInsertPos].uiDataLen = uiBufSize; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Release all of the nodes in the list. +*****************************************************************************/ +void F_OldNodeList::resetList( void) +{ + m_pool.poolReset( NULL); + m_uiNodeCount = 0; +} + +/**************************************************************************** +Desc: Add an index key to the buffers +****************************************************************************/ +RCODE F_Db::addToKrefTbl( + FLMUINT uiKeyLen, + FLMUINT uiDataLen) +{ + RCODE rc = NE_XFLM_OK; + KREF_ENTRY * pKref; + FLMUINT uiSizeNeeded; + FLMBYTE * pucDest; + + // If the table is FULL, expand the table + + if (m_uiKrefCount == m_uiKrefTblSize) + { + FLMUINT uiAllocSize; + FLMUINT uiOrigKrefTblSize = m_uiKrefTblSize; + + if (m_uiKrefTblSize > 0x8000 / sizeof( KREF_ENTRY *)) + { + m_uiKrefTblSize += 4096; + } + else + { + m_uiKrefTblSize *= 2; + } + + uiAllocSize = m_uiKrefTblSize * sizeof( KREF_ENTRY *); + + rc = f_realloc( uiAllocSize, &m_pKrefTbl); + if (RC_BAD(rc)) + { + m_uiKrefTblSize = uiOrigKrefTblSize; + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + // Allocate memory for the key's KREF and the key itself. + // We allocate one extra byte so we can zero terminate the key + // below. The extra zero character is to ensure that the compare + // in the qsort routine will work. + + uiSizeNeeded = sizeof( KREF_ENTRY) + uiKeyLen + 1 + uiDataLen; + + if (RC_BAD( rc = m_pKrefPool->poolAlloc( uiSizeNeeded, + (void **)&pKref))) + { + goto Exit; + } + + m_pKrefTbl [ m_uiKrefCount++] = pKref; + m_uiTotalKrefBytes += uiSizeNeeded; + + // Fill in all of the fields in the KREF structure. + + pKref->ui16IxNum = (FLMUINT16)m_keyGenInfo.pIxd->uiIndexNum; + pKref->bDelete = m_keyGenInfo.bAddKeys ? FALSE : TRUE; + pKref->ui16KeyLen = (FLMUINT16)uiKeyLen; + pKref->uiSequence = m_uiKrefCount; + pKref->uiDataLen = uiDataLen; + + // Copy the key to just after the KREF structure. + + pucDest = (FLMBYTE *)(&pKref [1]); + f_memcpy( pucDest, m_keyGenInfo.pucKeyBuf, uiKeyLen); + + // Null terminate the key so compare in qsort will work. + + pucDest [uiKeyLen] = 0; + if (uiDataLen) + { + f_memcpy( pucDest + uiKeyLen + 1, m_keyGenInfo.pucData, uiDataLen); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Verifies that all of the nodes in the CDL list are in contexts that + are related according to the index definition. +****************************************************************************/ +RCODE F_Db::verifyKeyContext( + FLMBOOL * pbVerified) +{ + RCODE rc = NE_XFLM_OK; + CDL * pCdl; + CDL * pParentCdl; + ICD * pIcd; + + *pbVerified = FALSE; + pIcd = m_keyGenInfo.pIxd->pIcdTree; + + // Do in-order traversal, from leaf ICDs up. + + while (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + + for (;;) + { + if ((pCdl = m_keyGenInfo.pCdlTbl [pIcd->uiCdl].pCdlList) != NULL) + { + + // If this is a "missing" placeholder and the + // component is required, we cannot build the key + + if (!pCdl->pNode && pIcd->uiKeyComponent && + (pIcd->uiFlags & ICD_REQUIRED_PIECE)) + { + goto Exit; + } + + // If the ICD has a parent, see if the parent has the + // correct node id. + + if (pIcd->pParent) + { + pParentCdl = m_keyGenInfo.pCdlTbl [pIcd->pParent->uiCdl].pCdlList; + + if( !pParentCdl || !pParentCdl->pNode) + { + goto Exit; + } + + if( pParentCdl->pNode->getNodeId() != pCdl->ui64ParentId) + { + goto Exit; + } + } + } + else + { + if (pIcd->uiKeyComponent && (pIcd->uiFlags & ICD_REQUIRED_PIECE)) + { + // This better already have been checked + + flmAssert( 0); + goto Exit; + } + } + + // See if there is a sibling + + if (pIcd->pNextSibling) + { + pIcd = pIcd->pNextSibling; + while (pIcd->pFirstChild) + { + pIcd = pIcd->pFirstChild; + } + } + else + { + if ((pIcd = pIcd->pParent) == NULL) + { + break; + } + } + } + *pbVerified = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Build the data part for a key. +Notes: This routine is recursive in nature. Will recurse the number of + data components defined in the index. +****************************************************************************/ +RCODE F_Db::buildData( + ICD * pIcd, + FLMUINT uiKeyLen, + FLMUINT uiDataLen + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCdl; + CDL * pFirstCdl; + CDL * pCdl; + FLMUINT uiDataComponentLen; + F_DOMNode * pNode = NULL; + FLMBYTE ucTmpSen [FLM_MAX_NUM_BUF_SIZE]; + FLMBYTE * pucTmpSen; + FLMUINT uiSENLen; + FLMBOOL bVerified; + FLMUINT uiIDLen; + + uiCdl = pIcd->uiCdl; + pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; + pCdl = pFirstCdl; + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // If we are not using nodes in the sub-tree, skip any + // that are in the sub-tree. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + + // Go through all of the data CDL - even the ones that are NULL + + for (;;) + { + + // Data components cannot be root tags. + + flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG); + + if (pNode) + { + pNode->Release(); + pNode = NULL; + } + + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; + + if (pCdl) + { + + // NOTE: pNode could be NULL because it is a "missing" placeholder + + pNode = pCdl->pNode; + } + + if (pNode) + { + pNode->AddRef(); + if (RC_BAD( rc = pNode->getDataLength( this, &uiDataComponentLen))) + { + goto Exit; + } + } + else + { + uiDataComponentLen = 0; + } + + // Output the length of the data as a SEN value + + pucTmpSen = &ucTmpSen [0]; + uiSENLen = flmEncodeSEN( uiDataComponentLen, &pucTmpSen); + if (uiDataComponentLen + uiSENLen + uiDataLen > m_keyGenInfo.uiDataBufSize) + { + FLMUINT uiNewSize = uiDataComponentLen + uiSENLen + uiDataLen + 512; + + // Allocate the data buffer if it has not been allocated. Otherwise, + // realloc it. + + if (!m_keyGenInfo.bDataBufAllocated) + { + FLMBYTE * pucNewData; + + if (RC_BAD( rc = f_alloc( uiNewSize, &pucNewData))) + { + goto Exit; + } + + if( uiDataLen) + { + f_memcpy( pucNewData, m_keyGenInfo.pucData, uiDataLen); + } + m_keyGenInfo.pucData = pucNewData; + m_keyGenInfo.bDataBufAllocated = TRUE; + } + else + { + // Reallocate the buffer. + + if (RC_BAD( rc = f_realloc( uiNewSize, &m_keyGenInfo.pucData))) + { + goto Exit; + } + } + + m_keyGenInfo.uiDataBufSize = uiNewSize; + } + f_memcpy( m_keyGenInfo.pucData + uiDataLen, ucTmpSen, uiSENLen); + if (uiDataComponentLen) + { + if (RC_BAD( rc = pNode->getData( this, + m_keyGenInfo.pucData + uiDataLen + uiSENLen, + &uiDataComponentLen))) + { + goto Exit; + } + } + + // If this is the last data CDL, append IDs to the + // key and output the key and data to the KREF. + // Otherwise, recurse down. + + if (pIcd->pNextDataComponent) + { + if (RC_BAD( rc = buildData( pIcd->pNextDataComponent, uiKeyLen, + uiDataLen + uiDataComponentLen + uiSENLen))) + { + goto Exit; + } + } + else if (m_keyGenInfo.pIxd->pFirstContext) + { + if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen, + uiDataLen + uiDataComponentLen + uiSENLen))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = verifyKeyContext( &bVerified))) + { + goto Exit; + } + if (bVerified) + { + if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, + m_keyGenInfo.pIxd, + m_keyGenInfo.pCdlTbl, + &m_keyGenInfo.pucKeyBuf [uiKeyLen], + MAX_KEY_SIZ - uiKeyLen, &uiIDLen))) + { + goto Exit; + } + if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, + uiDataLen + uiDataComponentLen + uiSENLen))) + { + goto Exit; + } + } + } + + // Get the next CDL, if any + + if (pCdl) + { + pCdl = pCdl->pNext; + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // If we are not using nodes in the sub-tree, skip any + // that are in the sub-tree. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + } + if (!pCdl) + { + goto Exit; + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; + + return( rc); +} + +/**************************************************************************** +Desc: Go through the context components of a key. +Notes: This routine is recursive in nature. Will recurse the number of + context components defined in the index. +****************************************************************************/ +RCODE F_Db::buildContext( + ICD * pIcd, + FLMUINT uiKeyLen, + FLMUINT uiDataLen + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCdl; + CDL * pFirstCdl; + CDL * pCdl; + FLMUINT uiIDLen; + F_DOMNode * pNode = NULL; + FLMBOOL bVerified; + + uiCdl = pIcd->uiCdl; + pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; + pCdl = pFirstCdl; + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // If we are not using nodes in the sub-tree, skip any + // that are in the sub-tree. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + + // Go through all of the context CDLs - even the ones that are NULL + + for (;;) + { + if (pNode) + { + pNode->Release(); + pNode = NULL; + } + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; + if (pCdl) + { + + // NOTE: pNode could be NULL because it is a "missing" placeholder + + if ((pNode = pCdl->pNode) != NULL) + { + pNode->AddRef(); + } + } + + // If this is the last context CDL, append IDs to the + // key and output the key and data to the KREF. + // Otherwise, recurse down. + + if (pIcd->pNextKeyComponent) + { + if (RC_BAD( rc = buildContext( pIcd->pNextKeyComponent, + uiKeyLen, uiDataLen))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = verifyKeyContext( &bVerified))) + { + goto Exit; + } + if (bVerified) + { + if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, + m_keyGenInfo.pIxd, + m_keyGenInfo.pCdlTbl, + &m_keyGenInfo.pucKeyBuf [uiKeyLen], + MAX_KEY_SIZ - uiKeyLen, &uiIDLen))) + { + goto Exit; + } + if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, uiDataLen))) + { + goto Exit; + } + } + } + + // Get the next CDL, if any + + if (pCdl) + { + pCdl = pCdl->pNext; + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // If we are not using nodes in the sub-tree, skip any + // that are in the sub-tree. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + } + if (!pCdl) + { + goto Exit; + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; + + return( rc); +} + +/**************************************************************************** +Desc: Finish the current key component. If there is a next one call + build keys. Otherwise, go on to doing data and context pieces. +****************************************************************************/ +RCODE F_Db::finishKeyComponent( + ICD * pIcd, + FLMUINT uiKeyLen + ) +{ + RCODE rc = NE_XFLM_OK; + + if (pIcd->pNextKeyComponent) + { + flmAssert( m_keyGenInfo.bIsCompound); + if (RC_BAD( rc = buildKeys( pIcd->pNextKeyComponent, uiKeyLen))) + { + goto Exit; + } + } + else + { + if (m_keyGenInfo.pIxd->pFirstData) + { + if (RC_BAD( rc = buildData( m_keyGenInfo.pIxd->pFirstData, uiKeyLen, 0))) + { + goto Exit; + } + } + else if (m_keyGenInfo.pIxd->pFirstContext) + { + if (RC_BAD( rc = buildContext( m_keyGenInfo.pIxd->pFirstContext, uiKeyLen, 0))) + { + goto Exit; + } + } + else + { + FLMBOOL bVerified; + + if (RC_BAD( rc = verifyKeyContext( &bVerified))) + { + goto Exit; + } + if (bVerified) + { + FLMUINT uiIDLen; + + if (RC_BAD( rc = kyAddIDsToKey( m_keyGenInfo.ui64DocumentID, + m_keyGenInfo.pIxd, + m_keyGenInfo.pCdlTbl, + &m_keyGenInfo.pucKeyBuf [uiKeyLen], + MAX_KEY_SIZ - uiKeyLen, + &uiIDLen))) + { + goto Exit; + } + if (RC_BAD( rc = addToKrefTbl( uiKeyLen + uiIDLen, 0))) + { + goto Exit; + } + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Generate the keys for a text component. +****************************************************************************/ +RCODE F_Db::genTextKeyComponents( + F_DOMNode * pNode, + ICD * pIcd, + FLMUINT uiKeyLen, + FLMBYTE ** ppucTmpBuf, + FLMUINT * puiTmpBufSize, + void ** ppvMark) +{ + RCODE rc = NE_XFLM_OK; + IF_PosIStream * pIStream = NULL; + FLMUINT uiNumChars; + F_BufferIStream bufferStream; + FLMUINT uiStrBytes; + FLMUINT uiSubstrChars; + FLMUINT uiMeta; + FLMBOOL bEachWord = FALSE; + FLMBOOL bMetaphone = FALSE; + FLMBOOL bSubstring = FALSE; + FLMBOOL bWholeString = FALSE; + FLMBOOL bHadAtLeastOneString = FALSE; + FLMBOOL bDataTruncated; + FLMUINT uiSaveKeyLen; + FLMUINT uiElmLen; + FLMUINT uiKeyLenPos = uiKeyLen; + FLMUINT uiCompareRules = pIcd->uiCompareRules; + F_NodeBufferIStream nodeBufferIStream; + + uiKeyLen += 2; + uiSaveKeyLen = uiKeyLen; + + if (!pNode) + { + goto No_Strings; + } + + if (RC_BAD( rc = pNode->getTextIStream( this, + &nodeBufferIStream, &pIStream, &uiNumChars))) + { + goto Exit; + } + + if (!uiNumChars) + { +No_Strings: + + // Save the key component length + + UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + + if( pIStream) + { + pIStream->Release(); + pIStream = NULL; + } + + rc = finishKeyComponent( pIcd, uiKeyLen); + goto Exit; + } + + if (pIcd->uiFlags & ICD_EACHWORD) + { + bEachWord = TRUE; + + // OR in the compressing of spaces, because we only want to treat + // spaces as delimiters between words. + + uiCompareRules |= XFLM_COMP_COMPRESS_WHITESPACE; + } + else if (pIcd->uiFlags & ICD_METAPHONE) + { + bMetaphone = TRUE; + } + else if (pIcd->uiFlags & ICD_SUBSTRING) + { + bSubstring = TRUE; + } + else + { + bWholeString = TRUE; + } + + // Loop on each word or substring in the value + + for (;;) + { + uiKeyLen = uiSaveKeyLen; + bDataTruncated = FALSE; + + if (bWholeString) + { + uiElmLen = MAX_KEY_SIZ - uiKeyLen; + if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], + &uiElmLen, + pIStream, XFLM_TEXT_TYPE, + pIcd->uiFlags, pIcd->uiCompareRules, + pIcd->uiLimit, NULL, NULL, + m_keyGenInfo.pIxd->uiLanguage, + FALSE, FALSE, + &bDataTruncated, NULL))) + { + goto Exit; + } + } + else if (bEachWord) + { + if (*ppucTmpBuf == NULL) + { + *ppvMark = m_TempPool.poolMark(); + *puiTmpBufSize = (FLMUINT)MAX_KEY_SIZ + 8; + if (RC_BAD( rc = m_TempPool.poolAlloc( *puiTmpBufSize, + (void **)ppucTmpBuf))) + { + goto Exit; + } + } + + uiStrBytes = *puiTmpBufSize; + if( RC_BAD( rc = KYEachWordParse( pIStream, &uiCompareRules, + pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes))) + { + goto Exit; + } + + if (!uiStrBytes) + { + if (!bHadAtLeastOneString) + { + goto No_Strings; + } + break; + } + + if (RC_BAD( rc = bufferStream.open( *ppucTmpBuf, uiStrBytes))) + { + goto Exit; + } + + // Pass 0 for compare rules because KYEachWordParse will already + // have taken care of them - except for XFLM_COMP_CASE_INSENSITIVE. + + uiElmLen = MAX_KEY_SIZ - uiKeyLen; + rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], + &uiElmLen, + &bufferStream, XFLM_TEXT_TYPE, + pIcd->uiFlags, + pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE, + pIcd->uiLimit, + NULL, NULL, + m_keyGenInfo.pIxd->uiLanguage, + FALSE, FALSE, &bDataTruncated, NULL); + bufferStream.close(); + + if( RC_BAD( rc)) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + bHadAtLeastOneString = TRUE; + } + else if (bMetaphone) + { + FLMBYTE ucStorageBuf[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiStorageLen; + + if( RC_BAD( rc = flmGetNextMetaphone( pIStream, &uiMeta))) + { + if( rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + if (!bHadAtLeastOneString) + { + goto No_Strings; + } + break; + } + + uiStorageLen = FLM_MAX_NUM_BUF_SIZE; + if( RC_BAD( rc = flmNumber64ToStorage( uiMeta, + &uiStorageLen, ucStorageBuf, FALSE, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = bufferStream.open( ucStorageBuf, uiStorageLen))) + { + goto Exit; + } + + // Pass 0 for compare rules - only applies to strings. + + uiElmLen = MAX_KEY_SIZ - uiKeyLen; + rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], + &uiElmLen, + &bufferStream, XFLM_NUMBER_TYPE, + pIcd->uiFlags, 0, + pIcd->uiLimit, + NULL, NULL, + m_keyGenInfo.pIxd->uiLanguage, + FALSE, FALSE, NULL, NULL); + bufferStream.close(); + + if( RC_BAD( rc)) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + bHadAtLeastOneString = TRUE; + } + else + { + flmAssert( bSubstring); + if (*ppucTmpBuf == NULL) + { + *ppvMark = m_TempPool.poolMark(); + *puiTmpBufSize = (FLMUINT)MAX_KEY_SIZ + 8; + if (RC_BAD( rc = m_TempPool.poolAlloc( *puiTmpBufSize, + (void **)ppucTmpBuf))) + { + goto Exit; + } + } + uiStrBytes = *puiTmpBufSize; + + if( RC_BAD( rc = KYSubstringParse( pIStream, &uiCompareRules, + pIcd->uiLimit, *ppucTmpBuf, &uiStrBytes, &uiSubstrChars))) + { + goto Exit; + } + + if (!uiStrBytes) + { + if (!bHadAtLeastOneString) + { + goto No_Strings; + } + break; + } + + if (bHadAtLeastOneString && uiSubstrChars == 1 && !m_keyGenInfo.bIsAsia) + { + break; + } + + if (RC_BAD( rc = bufferStream.open( *ppucTmpBuf, uiStrBytes))) + { + goto Exit; + } + + // Pass 0 for compare rules, because KYSubstringParse has already + // taken care of them, except for XFLM_COMP_CASE_INSENSITIVE + + uiElmLen = MAX_KEY_SIZ - uiKeyLen; + rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], + &uiElmLen, + &bufferStream, XFLM_TEXT_TYPE, + pIcd->uiFlags, + pIcd->uiCompareRules & XFLM_COMP_CASE_INSENSITIVE, + pIcd->uiLimit, + NULL, NULL, + m_keyGenInfo.pIxd->uiLanguage, + bHadAtLeastOneString ? FALSE : TRUE, FALSE, + &bDataTruncated, NULL); + + bufferStream.close(); + + if( RC_BAD( rc)) + { + RC_UNEXPECTED_ASSERT( rc); + goto Exit; + } + bHadAtLeastOneString = TRUE; + } + + uiKeyLen += uiElmLen; + + // Save the key component length + + if (!bDataTruncated) + { + UW2FBA( (FLMUINT16)uiElmLen, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + } + else + { + UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG), + &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + + // If we are deleting, save the node into the list of "old" nodes + // We do this so that the compare routine can look for the node + // if it has to compare the full value. + + if (!m_keyGenInfo.bAddKeys) + { + if (!m_pOldNodeList) + { + if ((m_pOldNodeList = f_new F_OldNodeList) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen))) + { + goto Exit; + } + + if (bWholeString) + { + break; + } + } + +Exit: + + if (pIStream) + { + pIStream->Release(); + pIStream = NULL; + } + + return( rc); +} + +/**************************************************************************** +Desc: Generate the keys for other data types besides text. +****************************************************************************/ +RCODE F_Db::genOtherKeyComponent( + F_DOMNode * pNode, + ICD * pIcd, + FLMUINT uiKeyLen) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiElmLen; + FLMUINT uiKeyLenPos = uiKeyLen; + FLMBOOL bDataTruncated; + IF_PosIStream * pIStream = NULL; + F_NodeBufferIStream bufferIStream; + + uiKeyLen += 2; + if (!pNode) + { + +No_Data: + + // Save the key component length + + UW2FBA( 0, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + + rc = finishKeyComponent( pIcd, uiKeyLen); + goto Exit; + } + + if (pIcd->uiFlags & ICD_PRESENCE) + { + FLMUINT uiNameId = pIcd->uiDictNum; + + // If we are indexing ELM_ROOT_TAG, we + // need to get the name id from the node. + + if (uiNameId == ELM_ROOT_TAG) + { + if (RC_BAD( rc = pNode->getNameId( this, &uiNameId))) + { + goto Exit; + } + } + longToByte( (FLMUINT32)uiNameId, &m_keyGenInfo.pucKeyBuf [uiKeyLen]); + uiKeyLen += 4; + + // Save the key component length. + + UW2FBA( 4, &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + } + else + { + // If it is not a presence index, we cannot be indexing + // ELM_ROOT_TAG on an element node. + + flmAssert( pIcd->uiDictNum != ELM_ROOT_TAG); + + if (RC_BAD( rc = pNode->getIStream( this, &bufferIStream, &pIStream))) + { + goto Exit; + } + + if (!pIStream->remainingSize()) + { + goto No_Data; + } + + // Compute number of bytes left + + uiElmLen = MAX_KEY_SIZ - uiKeyLen; + bDataTruncated = FALSE; + + // Pass zero for compare rules - these are not strings. + + if( RC_BAD( rc = KYCollateValue( &m_keyGenInfo.pucKeyBuf [uiKeyLen], + &uiElmLen, pIStream, icdGetDataType( pIcd), pIcd->uiFlags, + 0, pIcd->uiLimit, NULL, NULL, + m_keyGenInfo.pIxd->uiLanguage, + FALSE, FALSE, + &bDataTruncated, NULL))) + { + goto Exit; + } + uiKeyLen += uiElmLen; + + // Save the key component length. + + if (!bDataTruncated) + { + UW2FBA( (FLMUINT16)uiElmLen, + &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + } + else + { + UW2FBA( (FLMUINT16)(uiElmLen | TRUNCATED_FLAG), + &m_keyGenInfo.pucKeyBuf [uiKeyLenPos]); + + // If we are deleting, save the node into the list of "old" nodes + // We do this so that the compare routine can look for the node + // if it has to compare the full value. + + if (!m_keyGenInfo.bAddKeys) + { + if (!m_pOldNodeList) + { + if ((m_pOldNodeList = f_new F_OldNodeList) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if (RC_BAD( rc = m_pOldNodeList->addNodeToList( this, pNode))) + { + goto Exit; + } + } + } + + // Better have an F_IStream at this point! + + flmAssert( pIStream); + pIStream->Release(); + pIStream = NULL; + } + + if (RC_BAD( rc = finishKeyComponent( pIcd, uiKeyLen))) + { + goto Exit; + } + +Exit: + + if (pIStream) + { + pIStream->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Build all compound keys from the CDL table. +Notes: This routine is recursive in nature. Will recurse the number of + key components defined in the index. +****************************************************************************/ +RCODE F_Db::buildKeys( + ICD * pIcd, + FLMUINT uiKeyLen + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCdl = pIcd->uiCdl; + CDL * pFirstCdl = m_keyGenInfo.pCdlTbl [uiCdl].pCdlList; + CDL * pCdl = pFirstCdl; + FLMBYTE * pucTmpBuf = NULL; + void * pvMark = NULL; + FLMUINT uiTmpBufSize = 0; + F_DOMNode * pNode = NULL; + + flmAssert( m_keyGenInfo.bIsCompound || pIcd->uiKeyComponent == 1); + + // Do each CDL. If there is no CDL for this level, must + // still do at least once so that if there are other + // components or data after this component, we will + // do a recursive call or generate a key. + + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // Skip any CDLs in the subtree if we are not using + // sub-tree nodes at this point. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + for (;;) + { + + // Need to set the current CDL into the table so that + // when we append the IDs we will have the right ones. + // This will be restored to the first item in the + // table at Exit. + + if (pNode) + { + pNode->Release(); + pNode = NULL; + } + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pCdl; + if (pCdl) + { + + // pNode could be NULL because it is a placeholder for a + // "missing" value + + if ((pNode = pCdl->pNode) != NULL) + { + pNode->AddRef(); + } + } + + // Generate the key component + + if (icdGetDataType( pIcd) == XFLM_TEXT_TYPE && + !(pIcd->uiFlags & ICD_PRESENCE)) + { + if (RC_BAD( rc = genTextKeyComponents( pNode, pIcd, uiKeyLen, + &pucTmpBuf, &uiTmpBufSize, &pvMark))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = genOtherKeyComponent( pNode, pIcd, uiKeyLen))) + { + goto Exit; + } + } + + // Go to the next CDL, if any + + if (pCdl) + { + pCdl = pCdl->pNext; + if (!m_keyGenInfo.bUseSubtreeNodes) + { + + // Skip any CDLs in the subtree if we are not using + // sub-tree nodes at this point. + + while (pCdl && pCdl->bInNodeSubtree) + { + pCdl = pCdl->pNext; + } + } + } + if (!pCdl) + { + break; + } + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + if (pvMark) + { + m_TempPool.poolReset( pvMark); + } + + // Restore the CDL table entry to point to the + // beginning of the list + + m_keyGenInfo.pCdlTbl [uiCdl].pCdlList = pFirstCdl; + return( rc); +} + +/**************************************************************************** +Desc: Build all keys from combinations of CDLs. Add keys to KREF table. +****************************************************************************/ +RCODE F_Db::buildKeys( + FLMUINT64 ui64DocumentID, + IXD * pIxd, + CDL_HDR * pCdlTbl, + FLMBOOL bUseSubtreeNodes, + FLMBOOL bAddKeys) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNumKeysHavingCdl; + CDL * pCdl; + ICD * pIcd; + FLMBYTE ucDataBuf [STACK_DATA_BUF_SIZE]; + + m_keyGenInfo.bDataBufAllocated = FALSE; + + // Do a quick check to make sure we have all required pieces + + uiNumKeysHavingCdl = 0; + pIcd = pIxd->pFirstKey; + while (pIcd) + { + pCdl = pCdlTbl [pIcd->uiCdl].pCdlList; + if (!bUseSubtreeNodes) + { + + // Skip any nodes in the subtree if we are not using + // sub-tree nodes at this point. Also skip any + // "missing" placeholders. + + while (pCdl && (pCdl->bInNodeSubtree || !pCdl->pNode)) + { + pCdl = pCdl->pNext; + } + } + else + { + + // Skip any "missing" placeholders. + + while (pCdl && !pCdl->pNode) + { + pCdl = pCdl->pNext; + } + } + if (!pCdl) + { + if (pIcd->uiFlags & ICD_REQUIRED_PIECE) + { + goto Exit; // Nothing to generate, a required piece is missing. + } + } + else + { + uiNumKeysHavingCdl++; + } + pIcd = pIcd->pNextKeyComponent; + } + + // If none of the key pieces had a CDL, we cannot generate a key either. + + if (!uiNumKeysHavingCdl) + { + goto Exit; + } + + // Build all of the keys + + + m_keyGenInfo.ui64DocumentID = ui64DocumentID; + m_keyGenInfo.pIxd = pIxd; + m_keyGenInfo.bIsAsia = (FLMBOOL)(pIxd->uiLanguage >= FIRST_DBCS_LANG && + pIxd->uiLanguage <= LAST_DBCS_LANG) + ? TRUE + : FALSE; + m_keyGenInfo.bIsCompound = pIxd->uiNumKeyComponents > 1 ? TRUE : FALSE; + m_keyGenInfo.pCdlTbl = pCdlTbl; + m_keyGenInfo.bUseSubtreeNodes = bUseSubtreeNodes; + m_keyGenInfo.bAddKeys = bAddKeys; + m_keyGenInfo.pucKeyBuf = m_pucKrefKeyBuf; + m_keyGenInfo.pucData = &ucDataBuf [0]; + m_keyGenInfo.uiDataBufSize = sizeof( ucDataBuf); + m_keyGenInfo.bDataBufAllocated = FALSE; + if (RC_BAD( rc = buildKeys( pIxd->pFirstKey, 0))) + { + goto Exit; + } + +Exit: + + if (m_keyGenInfo.bDataBufAllocated) + { + f_free( &m_keyGenInfo.pucData); + m_keyGenInfo.bDataBufAllocated = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: See if the passed in node has any other siblings with the same ID. + The passed in node must be an element node. +****************************************************************************/ +FSTATIC RCODE kySeeIfRepeatingSibs( + F_Db * pDb, + F_DOMNode * pNode, + FLMBOOL * pbHadRepeatingSib) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pTmpNode = NULL; + FLMUINT uiNameId; + FLMUINT uiTmpNameId; + + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + + if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) + { + goto Exit; + } + + // Traverse next siblings + + pTmpNode = pNode; + pTmpNode->AddRef(); + for (;;) + { + if( RC_BAD( rc = pTmpNode->getNextSibling( pDb, + (IF_DOMNode **)&pTmpNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + break; + } + + if( pTmpNode->getNodeType() == ELEMENT_NODE) + { + if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId))) + { + goto Exit; + } + if (uiTmpNameId == uiNameId) + { + *pbHadRepeatingSib = TRUE; + goto Exit; + } + } + } + + // Traverse previous siblings + + pTmpNode->Release(); + pTmpNode = pNode; + pTmpNode->AddRef(); + + for (;;) + { + if( RC_BAD( rc = pTmpNode->getPreviousSibling( pDb, + (IF_DOMNode **)&pTmpNode))) + { + if( rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + if (pTmpNode->getNodeType() == ELEMENT_NODE) + { + if (RC_BAD( rc = pTmpNode->getNameId( pDb, &uiTmpNameId))) + { + goto Exit; + } + + if (uiTmpNameId == uiNameId) + { + *pbHadRepeatingSib = TRUE; + goto Exit; + } + } + } + +Exit: + + if (pTmpNode) + { + pTmpNode->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Get a child node for current traversal node. +****************************************************************************/ +FSTATIC RCODE kyFindChildNode( + F_Db * pDb, + F_Pool * pPool, + NODE_TRAV ** ppTrav, + FLMBOOL * pbGotChild, + FLMBOOL * pbHadRepeatingSib) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + ICD * pIcd; + FLMUINT uiNameId; + NODE_TRAV * pTrav = *ppTrav; + FLMUINT uiElmIcdCount = 0; + FLMUINT uiAttrIcdCount = 0; + FLMBOOL bTraverseElms; + FLMBOOL bTraverseAttrs; + ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode; + FLMUINT uiAttrNameId = 0; + + if( pAnchorNode && + pTrav->pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId) + { + pAnchorNode = pAnchorNode->pNext; + } + else + { + pAnchorNode = NULL; + } + + *pbGotChild = FALSE; + + // Determine if we need to traverse elements, attributes, + // or both. + + pIcd = pTrav->pIcd->pFirstChild; + while (pIcd) + { + if (pIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + uiAttrNameId = pIcd->uiDictNum; + uiAttrIcdCount++; + } + else + { + uiElmIcdCount++; + } + pIcd = pIcd->pNextSibling; + + } + bTraverseElms = (uiElmIcdCount) ? TRUE : FALSE; + bTraverseAttrs = (uiAttrIcdCount) ? TRUE : FALSE; + + flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE); + + if (bTraverseElms) + { + bTraverseElms = FALSE; + if (RC_BAD( rc = pTrav->pNode->getFirstChild( pDb, + (IF_DOMNode **)&pNode)) && rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + if (bTraverseAttrs) + { + bTraverseAttrs = FALSE; + rc = pTrav->pNode->getFirstAttribute( pDb, + (IF_DOMNode **)&pNode); + } + else + { + rc = NE_XFLM_OK; + goto Exit; + } + } + } + else + { + flmAssert( bTraverseAttrs); + bTraverseAttrs = FALSE; + + // If we only have a single attribute we are searching for, + // it will be quicker to call getAttribute than to cycle through + // all of the attributes. + + if (uiAttrIcdCount == 1) + { + rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId, + (IF_DOMNode **)&pNode); + } + else + { + rc = pTrav->pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); + } + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // Follow siblings until we have a match to any of the sibling ICDs + // Attribute nodes are also considered siblings. + + for (;;) + { + eDomNodeType eNodeType = pNode->getNodeType(); + + // Skip any nodes that are not element or attribute nodes. + + if( eNodeType != ELEMENT_NODE && eNodeType != ATTRIBUTE_NODE) + { + goto Next_Node; + } + + if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) + { + goto Exit; + } + + // If the node has the same name id as the anchor node, + // we need to skip it, unless it is the anchor node itself + + if( pAnchorNode && + uiNameId == pAnchorNode->uiAnchorNameId && + pNode->getNodeType() == pAnchorNode->eNodeType && + pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId) + { + + // Better not be repeated attribute nodes + + flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE); + if (pAnchorNode->bSeeIfRepeatingSibs) + { + *pbHadRepeatingSib = TRUE; + } + pIcd = NULL; + } + else + { + pIcd = pTrav->pIcd->pFirstChild; + if (pNode->getNodeType() == ELEMENT_NODE) + { + while (pIcd && + (pIcd->uiDictNum != uiNameId || + (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + } + else // pNode->getNodeType() == ATTRIBUTE_NODE + { + while (pIcd && + (pIcd->uiDictNum != uiNameId || + !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + } + } + if (pIcd) + { + if (!pTrav->pChild) + { + NODE_TRAV * pNewTrav; + + if (RC_BAD( rc = pPool->poolCalloc( sizeof( NODE_TRAV), + (void **)&pNewTrav))) + { + goto Exit; + } + pNewTrav->pParent = pTrav; + pTrav->pChild = pNewTrav; + } + else + { + f_memset( pTrav->pChild, 0, sizeof( NODE_TRAV)); + pTrav->pChild->pParent = pTrav; + } + + *ppTrav = pTrav = pTrav->pChild; + pTrav->pNode = pNode; + pTrav->pNode->AddRef(); + pTrav->pAnchorNode = pAnchorNode; + pTrav->uiSibIcdElms = uiElmIcdCount; + pTrav->uiSibIcdAttrs = uiAttrIcdCount; + pTrav->pIcd = pIcd; + if (pNode->getNodeType() == ATTRIBUTE_NODE) + { + + // There will only be one occurrence of any given + // attribute node, so once we have found it, there + // is no need to traverse any other siblings if + // there are no other sibling ICDs that are + // attributes. At this point, the number of ICDs + // that are elements is irrelevant, because we would + // have already traversed through all of them. + + pTrav->bTraverseSibs = uiAttrIcdCount > 1 ? TRUE : FALSE; + + // Attributes don't have children + + pTrav->bTraverseChildren = FALSE; + + flmAssert( !pIcd->pFirstChild); + } + else + { + FLMBOOL bIsAnchor = FALSE; + + if( pAnchorNode) + { + if( pNode->getNodeId() == pAnchorNode->ui64AnchorNodeId) + { + bIsAnchor = TRUE; + } + } + + // If there are attribute ICDs or more than one + // element ICD, or there is exactly one element + // ICD, but this is the anchor node, then no + // need to traverse siblings. + + if (uiAttrIcdCount || uiElmIcdCount > 1 || !bIsAnchor) + { + pTrav->bTraverseSibs = TRUE; + } + else + { + pTrav->bTraverseSibs = FALSE; + + // No need to specially traverse siblings searching + // for repeating siblings if bTraverseSibs is set + // to TRUE. + + if (bIsAnchor && pAnchorNode->bSeeIfRepeatingSibs && + !(*pbHadRepeatingSib)) + { + if (RC_BAD( rc = kySeeIfRepeatingSibs( pDb, pNode, + pbHadRepeatingSib))) + { + goto Exit; + } + } + } + + // If we have a child ICD, we need to traverse child nodes. + + pTrav->bTraverseChildren = pIcd->pFirstChild + ? TRUE + : FALSE; + } + *pbGotChild = TRUE; + goto Exit; // Will return NE_XFLM_OK + } + +Next_Node: + + // Try the next sibling node + + if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // If we have not yet traversed attributes, do it now. + + if (!bTraverseAttrs) + { + break; + } + bTraverseAttrs = FALSE; + + // If there is only one attribute, it is quicker to call + // getAttribute than it is to cycle through the attributes. + + if (uiAttrIcdCount == 1) + { + rc = pTrav->pNode->getAttribute( (IF_Db *)pDb, uiAttrNameId, + (IF_DOMNode **)&pNode); + } + else + { + rc = pTrav->pNode->getFirstAttribute( pDb, + (IF_DOMNode **)&pNode); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + } + +Exit: + + if (!(*pbGotChild)) + { + pTrav->bTraverseChildren = FALSE; + } + + if (pNode) + { + pNode->Release(); + } + return( rc); +} + +/**************************************************************************** +Desc: Get a sibling node for current traversal node. +****************************************************************************/ +FSTATIC RCODE kyFindSibNode( + F_Db * pDb, + NODE_TRAV * pTrav, + FLMBOOL bTestFirstNode, + FLMBOOL * pbGotSib, + FLMBOOL * pbHadRepeatingSib + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + ICD * pIcd; + FLMUINT uiNameId; + FLMBOOL bTraverseAttrs = pTrav->uiSibIcdAttrs ? TRUE : FALSE; + FLMBOOL bTraverseElms = pTrav->uiSibIcdElms ? TRUE : FALSE; + FLMBOOL bGetNextNode; + ANCHOR_NODE * pAnchorNode = pTrav->pAnchorNode; + eDomNodeType eNodeType; + + *pbGotSib = FALSE; + + pNode = pTrav->pNode; + pNode->AddRef(); + eNodeType = pNode->getNodeType(); + + if( eNodeType == ELEMENT_NODE) + { + flmAssert( bTraverseElms); + } + else + { + flmAssert( eNodeType != DATA_NODE); + flmAssert( bTraverseAttrs); + bTraverseAttrs = FALSE; + } + bTraverseElms = FALSE; + + // Follow siblings until we have a match to any of the sibling ICDs + // Attribute nodes are also considered siblings. + + bGetNextNode = bTestFirstNode ? FALSE : TRUE; + for (;;) + { + if (bGetNextNode) + { + if (RC_BAD( rc = pNode->getNextSibling( pDb, (IF_DOMNode **)&pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // If we have not yet traversed attributes, do it now. + + if (!bTraverseAttrs) + { + break; + } + bTraverseAttrs = FALSE; + if (RC_OK( rc = pTrav->pNode->getParentNode( pDb, + (IF_DOMNode **)&pNode))) + { + rc = pNode->getFirstAttribute( pDb, (IF_DOMNode **)&pNode); + } + if (RC_BAD( rc)) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + } + } + else + { + + // Set to TRUE for next time around. + + bGetNextNode = TRUE; + } + + // Skip any nodes that are not element or attribute nodes + + eNodeType = pNode->getNodeType(); + + if( eNodeType != ELEMENT_NODE && + eNodeType != ATTRIBUTE_NODE) + { + continue; + } + + if (RC_BAD( rc = pNode->getNameId( pDb, &uiNameId))) + { + goto Exit; + } + + // If the node has the same name id as the anchor node, + // we need to skip it, unless it is the anchor node itself + + if (pAnchorNode && + eNodeType == pAnchorNode->eNodeType && + uiNameId == pAnchorNode->uiAnchorNameId && + pNode->getIxNodeId() != pAnchorNode->ui64AnchorNodeId) + { + + // Better not be repeated attribute nodes + + flmAssert( pAnchorNode->eNodeType != ATTRIBUTE_NODE); + if (pAnchorNode->bSeeIfRepeatingSibs) + { + *pbHadRepeatingSib = TRUE; + } + pIcd = NULL; + } + else if( eNodeType == ELEMENT_NODE) + { + + // Search forward for a matching ICD. + + pIcd = pTrav->pIcd; + while (pIcd && + (pIcd->uiDictNum != uiNameId || + (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + + // If didn't find a matching ICD in the forward direction, + // search backward. + + if (!pIcd) + { + pIcd = pTrav->pIcd->pPrevSibling; + while (pIcd && + (pIcd->uiDictNum != uiNameId || + (pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pPrevSibling; + } + } + } + else + { + flmAssert( eNodeType == ATTRIBUTE_NODE); + + // Search forward for a matching ICD. + + pIcd = pTrav->pIcd; + while (pIcd && + (pIcd->uiDictNum != uiNameId || + !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pNextSibling; + } + + // If didn't find a matching ICD in the forward direction, + // search backward. + + if (!pIcd) + { + pIcd = pTrav->pIcd->pPrevSibling; + while (pIcd && + (pIcd->uiDictNum != uiNameId || + !(pIcd->uiFlags & ICD_IS_ATTRIBUTE))) + { + pIcd = pIcd->pPrevSibling; + } + } + } + + if (pIcd) + { + pTrav->pNode->Release(); + pTrav->pNode = pNode; + pTrav->pNode->AddRef(); + pTrav->pIcd = pIcd; + + if( eNodeType == ATTRIBUTE_NODE) + { + + // Attributes don't have children + + pTrav->bTraverseChildren = FALSE; + flmAssert( !pIcd->pFirstChild); + } + else + { + // If we have a child ICD, we need to traverse child nodes. + + pTrav->bTraverseChildren = pIcd->pFirstChild + ? TRUE + : FALSE; + } + + *pbGotSib = TRUE; + goto Exit; + } + } + +Exit: + + if (!(*pbGotSib)) + { + pTrav->bTraverseSibs = FALSE; + } + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Release all nodes a CDL table is pointing to. +****************************************************************************/ +void kyReleaseCdls( + IXD * pIxd, + CDL_HDR * pCdlTbl + ) +{ + FLMUINT uiLoop; + CDL * pCdl; + + if (pCdlTbl) + { + for (uiLoop = 0; uiLoop < pIxd->uiNumIcds; uiLoop++) + { + pCdl = pCdlTbl [uiLoop].pCdlList; + while (pCdl) + { + + // NOTE: pNode can be NULL because it may be a + // placeholder for a "missing" component + + if (pCdl->pNode) + { + pCdl->pNode->Release(); + } + pCdl = pCdl->pNext; + } + pCdlTbl [uiLoop].pCdlList = NULL; + } + } +} + +/**************************************************************************** +Desc: Generate index keys from a given node for a particular + index. +****************************************************************************/ +RCODE F_Db::genIndexKeys( + FLMUINT64 ui64DocumentID, + F_DOMNode * pIxNode, + IXD * pIxd, + ICD * pIcd, + IxAction eAction) +{ + RCODE rc = NE_XFLM_OK; + ICD * pTmpIcd; + ICD * pChildIcd; + CDL_HDR * pCdlTbl = NULL; + CDL * pCdl; + ANCHOR_NODE * pAnchorNodeList; + ANCHOR_NODE * pAnchorNode; + NODE_TRAV * pTrav = NULL; + FLMBOOL bGotNode; + F_DOMNode * pParent = NULL; + F_DOMNode * pTmpNode = NULL; + FLMUINT uiParentNameId; + FLMUINT uiIxNodeName; + FLMBOOL bInNodeSubtree; + FLMBOOL bHadRepeatingSib = FALSE; + eDomNodeType eIxNodeType = pIxNode->getNodeType(); + FLMUINT64 ui64IxNodeId = pIxNode->getIxNodeId(); + + pAnchorNodeList = NULL; + + if( RC_BAD( rc = pIxNode->getNameId( this, &uiIxNodeName))) + { + goto Exit; + } + + // Follow links back up to highest parent. If the path doesn't + // match what we have in the index definition, then this node + // is irrelevant to generating keys in this index. + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( ANCHOR_NODE), + (void **)&pAnchorNode))) + { + goto Exit; + } + + pAnchorNode->eNodeType = eIxNodeType; + pAnchorNode->ui64AnchorNodeId = ui64IxNodeId; + pAnchorNode->uiAnchorNameId = uiIxNodeName; + + // If the action is link/unlink, and the node is an element node + // because it is possible for elements to be repeated, we need + // to see if there is another sibling node of this same name. + // If there is another sibling with this same name, we do not + // need to add the "without" keys back in on the unlink action. + // Nor do we need to delete the "without" keys on the link + // action. + + if ((eAction == IX_UNLINK_NODE || eAction == IX_LINK_NODE) && + eIxNodeType == ELEMENT_NODE) + { + pAnchorNode->bSeeIfRepeatingSibs = TRUE; + } + + pAnchorNodeList = pAnchorNode; + pTmpIcd = pIcd; + pParent = pIxNode; + pParent->AddRef(); + + while (pTmpIcd->pParent) + { + pTmpIcd = pTmpIcd->pParent; + if (RC_BAD( rc = pParent->getParentNode( this, (IF_DOMNode **)&pParent))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if (RC_BAD( rc = pParent->getNameId( this, &uiParentNameId))) + { + goto Exit; + } + + if (pTmpIcd->uiDictNum == ELM_ROOT_TAG) + { + // If this node has a parent, it is not the root node, so + // we don't match the ICD. + + if (pParent->getParentId()) + { + goto Exit; // Will return NE_XFLM_OK + } + + // Better not be any more parent ICDs. + + flmAssert( !pTmpIcd->pParent); + } + else + { + if (pTmpIcd->uiDictNum != uiParentNameId) + { + goto Exit; // Will return NE_XFLM_OK + } + } + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( ANCHOR_NODE), + (void **)&pAnchorNode))) + { + goto Exit; + } + + pAnchorNode->eNodeType = pParent->getNodeType(); + flmAssert( pAnchorNode->eNodeType == ELEMENT_NODE); + pAnchorNode->ui64AnchorNodeId = pParent->getNodeId(); + pAnchorNode->uiAnchorNameId = uiParentNameId; + pAnchorNode->pNext = pAnchorNodeList; + pAnchorNodeList = pAnchorNode; + } + + // Allocate a CDL table for the index. + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( CDL_HDR) * + pIxd->uiNumIcds, (void **)&pCdlTbl))) + { + goto Exit; + } + + // Create a traversal node for the root node we arrived at. + + if (RC_BAD( rc = m_TempPool.poolCalloc( sizeof( NODE_TRAV), + (void **)&pTrav))) + { + goto Exit; + } + + pTrav->pNode = pParent; + pTrav->pNode->AddRef(); + pTrav->pIcd = pTmpIcd; + pTrav->pAnchorNode = pAnchorNodeList; + + // Count the number of sibling ICDs that are attributes versus elements + + while (pTmpIcd) + { + if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + pTrav->uiSibIcdAttrs++; + } + else + { + pTrav->uiSibIcdElms++; + } + pTmpIcd = pTmpIcd->pNextSibling; + } + + pTmpIcd = pTrav->pIcd->pPrevSibling; + + while (pTmpIcd) + { + if (pTmpIcd->uiFlags & ICD_IS_ATTRIBUTE) + { + pTrav->uiSibIcdAttrs++; + } + else + { + pTrav->uiSibIcdElms++; + } + + pTmpIcd = pTmpIcd->pPrevSibling; + } + +#ifdef FLM_DEBUG + if (pTrav->pIcd->uiDictNum == ELM_ROOT_TAG) + { + flmAssert( !pTrav->uiSibIcdAttrs && pTrav->uiSibIcdElms == 1); + flmAssert( pTrav->pNode->getNodeType() == ELEMENT_NODE); + } +#endif + + // See if we need to do this node's siblings. If it is an + // attribute node and there are no other attribute ICDs and + // no element ICDs, or if it is an element node and there are + // no other element ICDs and no attribute ICDs, we don't need + // to do the node's siblings. Otherwise, we do, so we will + // go up to the node's parent and back down to the first + // child element or first attribute. + + pTrav->bTraverseSibs = FALSE; + if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && + (pTrav->uiSibIcdAttrs > 1 || pTrav->uiSibIcdElms)) || + (pTrav->pNode->getNodeType() == ELEMENT_NODE && + (pTrav->uiSibIcdAttrs || pTrav->uiSibIcdElms > 1))) + { + if (RC_BAD( rc = pTrav->pNode->getParentNode( this, + (IF_DOMNode **)&pTmpNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && + pTrav->uiSibIcdElms) || + (pTrav->pNode->getNodeType() == ELEMENT_NODE && + pTrav->uiSibIcdElms > 1)) + { + if (RC_BAD( rc = pTmpNode->getFirstChild( this, + (IF_DOMNode **)&pTmpNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + rc = NE_XFLM_OK; + + // No sibling elements, do we need to do attributes? + + if ((pTrav->pNode->getNodeType() == ATTRIBUTE_NODE && + pTrav->uiSibIcdAttrs > 1) || + (pTrav->pNode->getNodeType() == ELEMENT_NODE && + pTrav->uiSibIcdAttrs)) + { + goto Get_First_Attribute; + } + } + else + { + pTrav->pNode->Release(); + pTrav->pNode = pTmpNode; + pTrav->pNode->AddRef(); + pTmpNode->Release(); + pTmpNode = NULL; + pTrav->bTraverseSibs = TRUE; + } + } + else + { +Get_First_Attribute: + if (RC_BAD( rc = pTmpNode->getFirstAttribute( this, + (IF_DOMNode **)&pTmpNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + } + else + { + pTrav->pNode->Release(); + pTrav->pNode = pTmpNode; + pTrav->pNode->AddRef(); + pTmpNode->Release(); + pTmpNode = NULL; + pTrav->bTraverseSibs = TRUE; + } + } + } + + // Position to a sibling node to start on. At this point, we should + // be guaranteed to at least find the node we started on + + if (pTrav->bTraverseSibs) + { + if (RC_BAD( rc = kyFindSibNode( this, pTrav, TRUE, &bGotNode, + &bHadRepeatingSib))) + { + goto Exit; + } + flmAssert( bGotNode); + } + else if (pTrav->pNode->getNodeType() == ATTRIBUTE_NODE) + { + pTrav->bTraverseChildren = FALSE; + } + else // ELEMENT_NODE + { + + // If we have a child ICD, we need to traverse child nodes. + + pTrav->bTraverseChildren = pTrav->pIcd->pFirstChild + ? TRUE + : FALSE; + + // If this is our primary anchor node, we need to see + // if there are repeating siblings. No need to do it + // in the other above cases, because if bTraversSibs is + // TRUE, it will happen inside kyFindSibNode, and if + // the node type is an attribute node, they should never + // have repeated siblings. + + if (pTrav->pAnchorNode && + pTrav->pAnchorNode->bSeeIfRepeatingSibs) + { + if (RC_BAD( rc = kySeeIfRepeatingSibs( this, pTrav->pNode, + &bHadRepeatingSib))) + { + goto Exit; + } + } + } + + // Follow all links from the current node and ICD, + // populating CDL tables as we go. + + if( pTrav->pNode->getNodeType() == eIxNodeType && + pTrav->pNode->getNameId() == uiIxNodeName && + pTrav->pNode->getIxNodeId() == ui64IxNodeId) + { + pTrav->bInNodeSubtree = TRUE; + } + + for (;;) + { + + pCdl = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList; + if (pCdl && !pCdl->pNode && + pTrav->pNode->getParentId() && + pCdl->ui64ParentId == pTrav->pNode->getParentId()) + { + pCdl->pNode = pTrav->pNode; + pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; + pCdl->pNode->AddRef(); + } + else + { + if (RC_BAD( rc = m_TempPool.poolAlloc( sizeof( CDL), + (void **)&pCdl))) + { + goto Exit; + } + + pCdl->pNode = pTrav->pNode; + pCdl->ui64ParentId = pTrav->pNode->getParentId(); + pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; + pCdl->pNode->AddRef(); + pCdl->pNext = pCdlTbl [pTrav->pIcd->uiCdl].pCdlList; + pCdlTbl [pTrav->pIcd->uiCdl].pCdlList = pCdl; + } + + // Add "missing" place-holders for any child ICDs + + pChildIcd = pTrav->pIcd->pFirstChild; + while (pChildIcd) + { + if (RC_BAD( rc = m_TempPool.poolAlloc( + sizeof( CDL), (void **)&pCdl))) + { + goto Exit; + } + + pCdl->pNode = NULL; + pCdl->bInNodeSubtree = pTrav->bInNodeSubtree; + pCdl->ui64ParentId = pTrav->pNode->getNodeId(); + pCdl->pNext = pCdlTbl [pChildIcd->uiCdl].pCdlList; + pCdlTbl [pChildIcd->uiCdl].pCdlList = pCdl; + pChildIcd = pChildIcd->pNextSibling; + } + +Next_Node: + + if (pTrav->bTraverseChildren) + { + bInNodeSubtree = pTrav->bInNodeSubtree; + if (RC_BAD( rc = kyFindChildNode( this, &m_TempPool, + &pTrav, &bGotNode, &bHadRepeatingSib))) + { + goto Exit; + } + + if (!bGotNode) + { + goto Next_Node; + } + + if( bInNodeSubtree || + (pTrav->pNode->getNodeType() == eIxNodeType && + pTrav->pNode->getNameId() == uiIxNodeName && + pTrav->pNode->getIxNodeId() == ui64IxNodeId)) + { + pTrav->bInNodeSubtree = TRUE; + } + } + else if (pTrav->bTraverseSibs) + { + // Here we are using bInNodeSubtree to indicate whether the entire + // sibling list is in the node's subtree. It could be that the + // current node is the subtree, but that does not mean that the + // entire sibling list is in the sub-tree. + + if (!pTrav->bInNodeSubtree) + { + bInNodeSubtree = FALSE; + } + else + { + // Is this sibling list in the node subtree? + // If bInNodeSubtree is TRUE, and this is not the original + // sub-tree node, then this sibling list has to be subordinate + // to the original sub-tree node. + + if( pTrav->pNode->getNodeType() != eIxNodeType || + pTrav->pNode->getNameId() != uiIxNodeName || + pTrav->pNode->getIxNodeId() != ui64IxNodeId) + { + bInNodeSubtree = TRUE; + } + else + { + bInNodeSubtree = FALSE; + } + } + + if (RC_BAD( rc = kyFindSibNode( this, pTrav, FALSE, &bGotNode, + &bHadRepeatingSib))) + { + goto Exit; + } + + if (!bGotNode) + { + goto Next_Node; + } + + // If the entire sibling list was in the node sub-tree or this + // node is the subtree, then we are in the subtree. + + if( bInNodeSubtree || + (pTrav->pNode->getNodeType() == eIxNodeType && + pTrav->pNode->getNameId() == uiIxNodeName && + pTrav->pNode->getIxNodeId() == ui64IxNodeId)) + { + pTrav->bInNodeSubtree = TRUE; + } + else + { + pTrav->bInNodeSubtree = FALSE; + } + } + else + { + if (pTrav->pParent) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + pTrav = pTrav->pParent; + pTrav->bTraverseChildren = FALSE; + goto Next_Node; + } + else + { + break; + } + } + } + + // Generate the keys from the combinations of CDLs. + + switch (eAction) + { + case IX_UNLINK_NODE: + { + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + TRUE, FALSE))) + { + goto Exit; + } + + if (!bHadRepeatingSib) + { + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + FALSE, TRUE))) + { + goto Exit; + } + } + break; + } + + case IX_LINK_NODE: + { + if( !bHadRepeatingSib) + { + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + FALSE, FALSE))) + { + goto Exit; + } + } + + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + TRUE, TRUE))) + { + goto Exit; + } + break; + } + + case IX_DEL_NODE_VALUE: + { + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + TRUE, FALSE))) + { + goto Exit; + } + break; + } + + case IX_ADD_NODE_VALUE: + { + if (RC_BAD( rc = buildKeys( ui64DocumentID, pIxd, pCdlTbl, + TRUE, TRUE))) + { + goto Exit; + } + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + +Exit: + + // Release the nodes CDL table + + kyReleaseCdls( pIxd, pCdlTbl); + + // Release any nodes in the traversal list. + + if (pTrav) + { + + // Go to top of list + + while (pTrav->pParent) + { + pTrav = pTrav->pParent; + } + + // Visit each NODE_TRAV node and release whatever + // DOM node it is pointing to. + + while (pTrav) + { + if (pTrav->pNode) + { + pTrav->pNode->Release(); + pTrav->pNode = NULL; + } + pTrav = pTrav->pChild; + } + } + + if (pParent) + { + pParent->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Routine that is called before and after changing a node - so we can + delete and add any keys from the database. +****************************************************************************/ +RCODE F_Db::updateIndexKeys( + FLMUINT uiCollectionNum, + F_DOMNode * pIxNode, + IxAction eAction, + FLMBOOL bStartOfUpdate, + FLMBOOL * pbIsIndexed) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bIsRoot; + FLMBOOL bIsIndexed = FALSE; + ICD * pIcd; + FLMUINT uiIcdDictNum; + IXD * pIxd; + void * pvMark = NULL; + FLMUINT64 ui64DocumentID; + FLMUINT uiNameId; + F_DOMNode * pNode = NULL; + F_AttrElmInfo defInfo; + IxAction eTmpAction; + + if( bStartOfUpdate) + { + if (RC_BAD( rc = krefCntrlCheck())) + { + goto Exit; + } + if (m_pOldNodeList) + { + m_pOldNodeList->resetList(); + } + } + + if( RC_BAD( rc = pIxNode->getNameId( this, &uiNameId))) + { + goto Exit; + } + + if( !uiNameId) + { + goto Exit; + } + + pNode = pIxNode; + pNode->AddRef(); + + // Lookup the node's ICD list + + switch( pNode->getNodeType()) + { + case ELEMENT_NODE: + { + if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) + { + goto Exit; + } + + break; + } + + case ATTRIBUTE_NODE: + { + if (RC_BAD( rc = m_pDict->getAttribute( this, uiNameId, &defInfo))) + { + goto Exit; + } + + break; + } + + case DATA_NODE: + { + // Need to operate on the parent node, which should be an element + // node. + + if (RC_BAD( rc = m_pDict->getElement( this, uiNameId, &defInfo))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getParentNode( this, (IF_DOMNode **)&pNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + // Data node not yet linked to a parent node, so + // it won't affect indexing. + + rc = NE_XFLM_OK; + } + + goto Exit; + } + + // Name ID of element better be the same as what we found in the + // data node, and node type better be an element node. + + flmAssert( pNode->getNameId() == uiNameId); + flmAssert( pNode->getNodeType() == ELEMENT_NODE); + + // Action cannot be link/unlink on data nodes. Higher level needs + // to take care of. They should first delete keys on the + // parent node, then unlink/link the node, then call add keys on + // the parent node. + + flmAssert( eAction != IX_LINK_NODE && eAction != IX_UNLINK_NODE); + break; + } + + default: + { + + // If the node is not an element or an attribute, it will not have + // any effect on indexing. + + goto Exit; + } + } + + bIsRoot = pNode->isRootNode(); + + if( (pIcd = defInfo.m_pFirstIcd) == NULL) + { + if( !bIsRoot || !m_pDict->m_pRootIcdList) + { + goto Exit; + } + else + { + pIcd = m_pDict->m_pRootIcdList; + } + } + + // The node may be indexed so we need to process the ICD list + + pvMark = m_TempPool.poolMark(); + bIsIndexed = TRUE; + ui64DocumentID = pNode->getDocumentId(); + + // Process each index + + for (;;) + { + pIxd = pIcd->pIxd; + uiIcdDictNum = pIcd->uiDictNum; + + // See if this ICD's index is on the collection we are doing. + // Also, make sure if we are doing a specific index, that this is + // that index. + + if (pIxd->uiCollectionNum != uiCollectionNum) + { + goto Next_Index; + } + + // See if this document has been indexed for this index. + + if (ui64DocumentID > pIxd->ui64LastDocIndexed) + { + goto Next_Index; + } + + if ((eTmpAction = eAction) == IX_LINK_AND_ADD_NODE) + { + if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling) + { + if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent) + { + + // If this ICD is not a key or data component, it has no + // effect on index keys if we are only modifying its value. + + goto Next_Index; + } + else + { + eTmpAction = IX_ADD_NODE_VALUE; + } + } + else + { + eTmpAction = IX_LINK_NODE; + } + } + else if (eTmpAction == IX_LINK_NODE || eTmpAction == IX_UNLINK_NODE) + { + + // IX_LINK_NODE and IX_UNLINK_NODE are called when a node is linked + // to its parent. Thus, if this ICD is the root ICD, it will have + // no effect on index keys. + + if (!pIcd->pParent && !pIcd->pNextSibling && !pIcd->pPrevSibling) + { + goto Next_Index; + } + } + else + { + if (!pIcd->uiKeyComponent && !pIcd->uiDataComponent) + { + + // If this ICD is not a key or data component, it has no + // effect on index keys if we are only modifying its value. + + goto Next_Index; + } + } + + // Generate index keys for this index. + + if (RC_BAD( rc = genIndexKeys( ui64DocumentID, pNode, pIxd, + pIcd, eTmpAction))) + { + goto Exit; + } + +Next_Index: + + m_TempPool.poolReset( pvMark); + if ((pIcd = pIcd->pNextInChain) == NULL) + { + if (!bIsRoot || uiIcdDictNum == ELM_ROOT_TAG) + { + break; + } + + // When done processing regular ICDs, process the + // root ICD list, if this node is a root node. + + if ((pIcd = m_pDict->m_pRootIcdList) == NULL) + { + break; + } + } + } + +Exit: + + if( pvMark) + { + m_TempPool.poolReset( pvMark); + } + + if (pNode) + { + pNode->Release(); + } + + if( pbIsIndexed) + { + *pbIsIndexed = bIsIndexed; + } + + return( rc); +} diff --git a/version5/src/kycollat.cpp b/version5/src/kycollat.cpp new file mode 100644 index 0000000..96afd6c --- /dev/null +++ b/version5/src/kycollat.cpp @@ -0,0 +1,2407 @@ +//------------------------------------------------------------------------------ +// Desc: Index collation routines +// +// 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: kycollat.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE KYFormatUTF8Text( + IF_PosIStream * pIStream, + FLMUINT uiFlags, + FLMUINT uiCompareRules, + F_DynaBuf * pDynaBuf); + +FSTATIC FLMBOOL flmAddMetaphone( + const char * pszStr, + const char * pszAltStr, + FLMBYTE * pszMeta, + FLMUINT * puiMetaOffset, + FLMBYTE * pszAltMeta, + FLMUINT * puiAltMetaOffset); + +FSTATIC void flmMetaStrToNum( + 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 + +/**************************************************************************** +Desc: Build a collated key value piece. +****************************************************************************/ +RCODE KYCollateValue( + FLMBYTE * pucDest, + FLMUINT * puiDestLen, + IF_PosIStream * pIStream, + FLMUINT uiDataType, + FLMUINT uiFlags, + FLMUINT uiCompareRules, + FLMUINT uiLimit, + FLMUINT * puiCollationLen, + FLMUINT * puiLuLen, + FLMUINT uiLanguage, + FLMBOOL bFirstSubstring, + FLMBOOL bDataTruncated, + FLMBOOL * pbDataTruncated, + FLMBOOL * pbOriginalCharsLost) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDestLen; + F_BufferIStream bufferStream; + FLMUINT uiCharLimit; + FLMUINT uiLength; + FLMBYTE * pucTmpDest; + FLMUINT uiBytesRead; + FLMBOOL bHaveData = TRUE; + FLMUNICODE uChar; + FLMBYTE ucDynaBuf[ 64]; + F_DynaBuf dynaBuf( ucDynaBuf, sizeof( ucDynaBuf)); + + if (puiLuLen) + { + *puiLuLen = 0; + } + + if ((uiDestLen = *puiDestLen) == 0) + { + rc = RC_SET( NE_XFLM_KEY_OVERFLOW); + goto Exit; + } + + if (uiDataType != XFLM_TEXT_TYPE) + { + if( !pIStream->remainingSize()) + { + bHaveData = FALSE; + } + } + else + { + FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); + + if( RC_BAD( rc = flmReadUTF8CharAsUnicode( + pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + bHaveData = FALSE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if( RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) + { + goto Exit; + } + + // The text is expected to be 0-terminated UTF-8 + + if ((uiFlags & ICD_ESC_CHAR) || + (uiCompareRules & + (XFLM_COMP_COMPRESS_WHITESPACE | + XFLM_COMP_NO_WHITESPACE | + XFLM_COMP_NO_UNDERSCORES | + XFLM_COMP_NO_DASHES | + XFLM_COMP_WHITESPACE_AS_SPACE | + XFLM_COMP_IGNORE_LEADING_SPACE | + XFLM_COMP_IGNORE_TRAILING_SPACE))) + { + dynaBuf.truncateData( 0); + if (RC_BAD( rc = KYFormatUTF8Text( pIStream, + uiFlags, uiCompareRules, &dynaBuf))) + { + goto Exit; + } + if (RC_BAD( rc = bufferStream.open( dynaBuf.getBufferPtr(), + dynaBuf.getDataLength()))) + { + goto Exit; + } + pIStream = &bufferStream; + } + + uiCharLimit = uiLimit ? uiLimit : ICD_DEFAULT_LIMIT; + + if( (uiLanguage >= FIRST_DBCS_LANG ) && (uiLanguage <= LAST_DBCS_LANG)) + { + if( RC_BAD( rc = flmAsiaUTF8ToColText( pIStream, pucDest, &uiDestLen, + (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) + ? TRUE + : FALSE, + puiCollationLen, puiLuLen, + uiCharLimit, bFirstSubstring, + bDataTruncated, pbDataTruncated))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = flmUTF8ToColText( pIStream, pucDest, &uiDestLen, + (uiCompareRules & XFLM_COMP_CASE_INSENSITIVE) + ? TRUE + : FALSE, + puiCollationLen, puiLuLen, + uiLanguage, uiCharLimit, bFirstSubstring, + bDataTruncated, + pbOriginalCharsLost, pbDataTruncated))) + { + goto Exit; + } + } + } + + // TRICKY: uiDestLen could be set to zero if text and no value. + + if (!bHaveData || !uiDestLen) + { + uiDestLen = 0; + goto Exit; + } + + switch (uiDataType) + { + case XFLM_TEXT_TYPE: + break; + + case XFLM_NUMBER_TYPE: + { + FLMBYTE ucTmpBuf [FLM_MAX_NUM_BUF_SIZE]; + + uiLength = (FLMUINT)pIStream->remainingSize(); + + flmAssert( uiLength <= sizeof( ucTmpBuf)); + + if (RC_BAD( rc = pIStream->read( ucTmpBuf, uiLength, &uiBytesRead))) + { + goto Exit; + } + flmAssert( uiBytesRead == uiLength); + if (RC_BAD( rc = flmStorageNum2CollationNum( ucTmpBuf, + uiBytesRead, pucDest, &uiDestLen))) + { + goto Exit; + } + break; + } + + case XFLM_BINARY_TYPE: + { + uiLength = (FLMUINT)pIStream->remainingSize(); + pucTmpDest = pucDest; + + if (uiLength >= uiLimit) + { + uiLength = uiLimit; + bDataTruncated = TRUE; + } + + // We don't want any single key piece to "pig out" more + // than 256 bytes of the key + + if (uiDestLen > 256) + { + uiDestLen = 256; + } + + if (uiLength > uiDestLen) + { + + // Compute length so will not overflow + + uiLength = uiDestLen; + bDataTruncated = TRUE; + } + else + { + uiDestLen = uiLength; + } + + // Store as is. + + if (RC_BAD( rc = pIStream->read( pucTmpDest, uiDestLen, &uiBytesRead))) + { + goto Exit; + } + + if (bDataTruncated && pbDataTruncated) + { + *pbDataTruncated = TRUE; + } + break; + } + + default: + { + rc = RC_SET( NE_XFLM_CANNOT_INDEX_DATA_TYPE); + break; + } + } + +Exit: + + *puiDestLen = uiDestLen; + return( rc); +} + +/**************************************************************************** +Desc: Format text removing leading and trailing spaces. Treat + underscores as spaces. As options, remove all spaces and dashes. +Ret: NE_XFLM_OK always. WIll truncate so text will fill MAX_KEY_SIZ. + Allocate 8 more than MAX_KEY_SIZ for psDestBuf. +Visit: Pass in uiLimit and pass back a truncated flag when the + string is truncated. This was not done because we will have + to get the exact truncated count that is done in f_tocoll.cpp + and that could introduce some bugs. +****************************************************************************/ +FSTATIC RCODE KYFormatUTF8Text( + IF_PosIStream * pIStream, + FLMUINT uiFlags, // ICD flags + FLMUINT uiCompareRules, // ICD compare rules + F_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_XFLM_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 = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + if ((uChar = flmConvertChar( uChar, uiCompareRules)) == 0) + { + continue; + } + + if (uChar == ASCII_SPACE) + { + if (uiCompareRules & + (XFLM_COMP_COMPRESS_WHITESPACE | + XFLM_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 &= (~(XFLM_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 & XFLM_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 && (uiFlags & ICD_ESC_CHAR)) + { + if (RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_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 = flmUni2UTF8( 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 & XFLM_COMP_IGNORE_TRAILING_SPACE) + { + uiStrSize = uiFirstSpaceCharPos; + } + else + { + flmAssert( uiCompareRules & XFLM_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 flmAddMetaphone( + 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 flmMetaStrToNum( + 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 flmGetNextMetaphone( + IF_IStream * pIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone) +{ + RCODE rc = NE_XFLM_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 = flmReadUTF8CharAsUnicode( + pIStream, &uChar))) + { + if (rc == NE_XFLM_EOF_HIT) + { + if( uiInputLen) + { + rc = NE_XFLM_OK; + break; + } + } + + goto Exit; + } + + if( gv_XFlmSysData.pXml->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( flmAddMetaphone( "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( flmAddMetaphone( "A", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_b: + { + //"-mb", e.g", "dumb", already skipped over... + + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset +=2; + break; + } + + // Italian "chianti" + + if( f_uninativencmp( &uzInput[ uiInputOffset], "chia", 4) == 0) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( uiInputOffset) + { + if( f_uninativencmp( &uzInput[ 0], "mc", 2) == 0) + { + // Names such as "McHugh" + + if( flmAddMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "X", "K", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "KS", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // "bacci", "bertucci", and other Italian words + + if( flmAddMetaphone( "X", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset += 3; + break; + } + else + { + // Pierce's rule + + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 2; + break; + } + + // else + + if( flmAddMetaphone( "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( flmAddMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + else + { + // "edgar" + + if( flmAddMetaphone( "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( flmAddMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + + // else + + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else if( uiInputOffset && uzInput[ uiInputOffset - 1] != FLM_UNICODE_i) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "N", "KN", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "KN", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + } + + uiInputOffset += 2; + break; + } + + // "tagliaro" + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "li", 2) == 0 && + !bSlavoGermanic) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "K", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // Always soft if French ending + + if( f_uninativencmp( &uzInput[ uiInputOffset + 1], "ier ", 4) == 0) + { + if( flmAddMetaphone( "J", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "J", "K", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset += 2; + break; + } + } + + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_g) + { + uiInputOffset += 2; + } + else + { + uiInputOffset += 1; + } + + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "H", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "J", "H", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + uiInputOffset++; + break; + } + + if( !uiInputOffset && + f_uninativencmp( &uzInput[ uiInputOffset], "jose", 4) != 0) + { + // Yankelovich / Jankelowicz + + if( flmAddMetaphone( "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( flmAddMetaphone( "J", "H", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( uiInputOffset == uiLast) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "L", NULL, ucMeta, &uiMetaOffset, + NULL, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 2; + break; + } + uiInputOffset += 2; + } + else + { + uiInputOffset++; + } + + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "N", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_n_TILDE: + { + if( flmAddMetaphone( "N", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset++; + break; + } + + case FLM_UNICODE_p: + { + if( uzInput[ uiInputOffset + 1] == FLM_UNICODE_h) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( NULL, "R", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "S", "X", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "X", "SK", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "SK", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + + uiInputOffset += 3; + break; + } + else + { + if( !uiInputOffset && !f_isvowel( uzInput[ 3]) && + uzInput[ 3] != FLM_UNICODE_w) + { + if( flmAddMetaphone( "X", "S", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "S", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + uiInputOffset += 3; + break; + } + + if( flmAddMetaphone( "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( flmAddMetaphone( NULL, "S", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "T", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "F", NULL, ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + + break; + } + + case FLM_UNICODE_w: + { + if( f_uninativencmp( &uzInput[ uiInputOffset], "wr", 2) == 0) + { + if( flmAddMetaphone( "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( flmAddMetaphone( "A", "F", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + // "Uomo" should match "Womo" + + if( flmAddMetaphone( "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( flmAddMetaphone( 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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "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( flmAddMetaphone( "S", "TS", ucMeta, &uiMetaOffset, + ucAltMeta, &uiAltMetaOffset)) + { + goto Done; + } + } + else + { + if( flmAddMetaphone( "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; + flmMetaStrToNum( ucMeta, puiMetaphone); + + if( puiAltMetaphone) + { + ucAltMeta[ uiAltMetaOffset] = 0; + flmMetaStrToNum( 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 flmVerifyMetaphoneRoutines( void) +{ + RCODE rc = NE_XFLM_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 = flmGetNextMetaphone( &bufferStream, + &uiMeta, &uiAltMeta))) + { + goto Exit; + } + + if( uiMeta != pMetaMap->uiMeta || + uiAltMeta != pMetaMap->uiAltMeta) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + bufferStream.close(); + pMetaMap++; + } + +Exit: + + flmAssert( RC_OK( rc)); + return( rc); +} +#endif diff --git a/version5/src/kyeword.cpp b/version5/src/kyeword.cpp new file mode 100644 index 0000000..00c8646 --- /dev/null +++ b/version5/src/kyeword.cpp @@ -0,0 +1,425 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the code to parse out individual words and +// substrings in a text string. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: kyeword.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC RCODE flmGetCharacter( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUINT16 * pui16WPValue, + FLMUNICODE * puUniValue); + +FSTATIC RCODE flmTextGetCharType( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUNICODE * puUniValue, + FLMUINT * puiType); + +/***************************************************************************** +Desc: +*****************************************************************************/ +FINLINE FLMUINT flmCharTypeAnsi7( + FLMUINT16 ui16Char) +{ + if( (ui16Char >= ASCII_LOWER_A && ui16Char <= ASCII_LOWER_Z) || + (ui16Char >= ASCII_UPPER_A && ui16Char <= ASCII_UPPER_Z) || + (ui16Char >= ASCII_ZERO && ui16Char <= ASCII_NINE)) + { + return SDWD_CHR; + } + + if( ui16Char == 0x27) + { + return WDJN_CHR; + } + + if( ui16Char <= 0x2B) + { + return DELI_CHR; + } + + if( ui16Char == ASCII_COMMA || + ui16Char == ASCII_DASH || + ui16Char == ASCII_DOT || + ui16Char == ASCII_SLASH || + ui16Char == ASCII_COLON || + ui16Char == ASCII_AT || + ui16Char == ASCII_BACKSLASH || + ui16Char == ASCII_UNDERSCORE) + { + return WDJN_CHR; + } + + return DELI_CHR; +} + +/***************************************************************************** +Desc: Return the next WP or unicode character value. +Return: Number of bytes formatted to return the character value. +*****************************************************************************/ +FSTATIC RCODE flmGetCharacter( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUINT16 * pui16WPValue, + FLMUNICODE * puUniValue) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE uChar = 0; + FLMUINT64 ui64AfterLastSpacePos = 0; + FLMBOOL bLastCharWasSpace = FALSE; + FLMUINT uiCompareRules = *puiCompareRules; + + for( ;;) + { + if (RC_BAD( rc = flmReadUTF8CharAsUnicode( pIStream, &uChar))) + { + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + if (bLastCharWasSpace && + !(uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE)) + { + // bLastCharWasSpace flag can only be TRUE if either + // XFLM_COMP_IGNORE_TRAILING_SPACE is set or + // XFLM_COMP_COMPRESS_WHITESPACE is set. + + flmAssert( uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE); + uChar = ASCII_SPACE; + } + else + { + uChar = 0; + } + break; + } + + if ((uChar = flmConvertChar( uChar, uiCompareRules)) == 0) + { + continue; + } + + if (uChar == ASCII_SPACE) + { + if (uiCompareRules & XFLM_COMP_COMPRESS_WHITESPACE) + { + bLastCharWasSpace = TRUE; + ui64AfterLastSpacePos = pIStream->getCurrPosition(); + } + else if (uiCompareRules & XFLM_COMP_IGNORE_TRAILING_SPACE) + { + + // If the ignore trailing space flag is set, but the compress + // space flag is not set, remember the position of the + // first space character. If we hit a non-space character, + // we will reposition to after this space character. + + if (!bLastCharWasSpace) + { + bLastCharWasSpace = TRUE; + ui64AfterLastSpacePos = pIStream->getCurrPosition(); + } + } + else + { + break; + } + } + else + { + + // Disable the ignore leading space flag, because we are now + // past all leading space, and we don't want spaces ignored + // now on account of that flag. + + uiCompareRules &= (~(XFLM_COMP_IGNORE_LEADING_SPACE)); + if (bLastCharWasSpace) + { + + // Position to after the last space + + if (RC_BAD( rc = pIStream->positionTo( ui64AfterLastSpacePos))) + { + goto Exit; + } + uChar = ASCII_SPACE; + bLastCharWasSpace = FALSE; + } + break; + } + } + + if (pui16WPValue) + { + if (!flmUnicodeToWP( uChar, pui16WPValue)) + { + *pui16WPValue = 0; + } + } + + if (puUniValue) + { + *puUniValue = uChar; + } + +Exit: + + *puiCompareRules = uiCompareRules; + + return( rc); +} + +/**************************************************************************** +Desc: Substring-ize the string in a node. Normalize spaces and hyphens if + told to. Example: ABC DEF + ABC DEF + BC DEF + C DEF + DEF +****************************************************************************/ +RCODE KYSubstringParse( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, // [in/out] comparison rules + FLMUINT uiLimitParm, // [in] Max characters + FLMBYTE * pucSubstrBuf, // [out] buffer to fill + FLMUINT * puiSubstrBytes, // [out] returns length + FLMUINT * puiSubstrChars) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDestOffset = 0; + FLMUINT uiDestSize = *puiSubstrBytes; + FLMUINT uiLimit = uiLimitParm ? uiLimitParm : ICD_DEFAULT_SUBSTRING_LIMIT; + FLMUINT uiCharCnt = 0; + FLMUINT uiSize; + FLMBOOL bFirstCharacter = TRUE; + FLMUINT64 ui64SavePosition = pIStream->getCurrPosition(); + + // The limit must return one more than requested in order + // for the text to collation routine to set the truncated flag. + + uiLimit++; + + while (uiLimit--) + { + FLMUNICODE uChar; + + if( RC_BAD( rc = flmGetCharacter( pIStream, puiCompareRules, NULL, &uChar))) + { + goto Exit; + } + + if (!uChar) + { + break; + } + + uiCharCnt++; + + uiSize = uiDestSize - uiDestOffset; + if (RC_BAD( rc = flmUni2UTF8( uChar, &pucSubstrBuf[ uiDestOffset], &uiSize))) + { + goto Exit; + } + uiDestOffset += uiSize; + + // If on the first word, position to start on next character + // for the next call. + + if (bFirstCharacter) + { + bFirstCharacter = FALSE; + + // First character - save position so we can restore it + // upon leaving the routine. + + ui64SavePosition = pIStream->getCurrPosition(); + } + } + + if (uiDestOffset) + { + pucSubstrBuf[ uiDestOffset++] = 0; + } + + *puiSubstrBytes = (FLMUINT)uiDestOffset; + *puiSubstrChars = uiCharCnt; + + // Restore position of stream to first character after the first + // character we found - to ready for next call. + + if (RC_BAD( rc = pIStream->positionTo( ui64SavePosition))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE KYEachWordParse( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUINT uiLimit, // [in] Max characters + FLMBYTE * pucWordBuf, // [out] Buffer of at least MAX_KEY_SIZ + FLMUINT * puiWordLen) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bSkippingDelim = TRUE; + FLMUINT uiWordLen = 0; + FLMUINT uiWordBufSize = *puiWordLen; + FLMUNICODE uChar; + FLMUINT uiType; + FLMUINT uiSize; + + if (!uiLimit) + { + uiLimit = ICD_DEFAULT_SUBSTRING_LIMIT; + } + + while (uiLimit) + { + if (RC_BAD( rc = flmTextGetCharType( pIStream, puiCompareRules, &uChar, &uiType))) + { + goto Exit; + } + if (!uChar) + { + break; + } + + // Determine how to handle what we got. + + if (bSkippingDelim) + { + // If we were skipping delimiters, and we run into a non-delimiter + // character, set the bSkippingDelim flag to FALSE to indicate the + // beginning of a word. + + if (uiType & SDWD_CHR) + { + bSkippingDelim = FALSE; + uiLimit--; + uiSize = uiWordBufSize - uiWordLen; + if (RC_BAD( rc = flmUni2UTF8( uChar, &pucWordBuf [uiWordLen], + &uiSize))) + { + goto Exit; + } + uiWordLen += uiSize; + } + } + else + { + + // If we were NOT skipping delimiters, and we run into a delimiter + // output the word. + + if (uiType & (DELI_CHR | WDJN_CHR)) + { + break; + } + uiSize = uiWordBufSize - uiWordLen; + if (RC_BAD( rc = flmUni2UTF8( uChar, &pucWordBuf [uiWordLen], + &uiSize))) + { + goto Exit; + } + uiWordLen += uiSize; + } + } + + // Return the word, if any + + if (uiWordLen) + { + pucWordBuf [uiWordLen++] = 0; + } + *puiWordLen = uiWordLen; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Return the next WP or unicode character value and parsing type. +*****************************************************************************/ +FSTATIC RCODE flmTextGetCharType( + IF_PosIStream * pIStream, + FLMUINT * puiCompareRules, + FLMUNICODE * puUniValue, // [out] Unicode value + FLMUINT * puiType // Char attribute type. + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT16 ui16WPValue; + FLMUINT uiCharSet; + + // We add on compress white space flag because we really want to ignore + // spaces anyway - we are trying to get the "words" from this stream. + + if( RC_BAD( rc = flmGetCharacter( pIStream, puiCompareRules, + &ui16WPValue, puUniValue))) + { + goto Exit; + } + + if (ui16WPValue) + { + if (ui16WPValue < 0x080) + { + *puiType = flmCharTypeAnsi7( ui16WPValue); + goto Exit; + } + uiCharSet = (FLMUINT)(ui16WPValue >> 8); + + if (uiCharSet == 1 || uiCharSet == 2 || + (uiCharSet >= 8 && uiCharSet <= 11)) + { + *puiType = SDWD_CHR; + goto Exit; + } + + *puiType = DELI_CHR; + } + else + { + + // For now all unmapped unicode characters are treated + // as delimeters + + *puiType = DELI_CHR; + } + +Exit: + + return( rc); +} diff --git a/version5/src/kyqsort.cpp b/version5/src/kyqsort.cpp new file mode 100644 index 0000000..b798c11 --- /dev/null +++ b/version5/src/kyqsort.cpp @@ -0,0 +1,1400 @@ +//------------------------------------------------------------------------------ +// Desc: Contains specific q-sort code to sort FLAIM's KREF structures. +// +// Tabs: 3 +// +// Copyright (c) 1990-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: kyqsort.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define KY_SWAP( pKrefTbl, leftP, rightP) \ + pTempKref = pKrefTbl [leftP]; \ + pKrefTbl [leftP] = pKrefTbl [rightP]; \ + pKrefTbl [rightP] = pTempKref + +FSTATIC RCODE ixKeyGetNodeId( + IXD * pIxd, + const FLMBYTE * pucKey, + const FLMBYTE * pucKeyEnd, + FLMUINT uiKeyComponent, + FLMUINT64 * pui64NodeId); + +FSTATIC RCODE ixKeyCompareUnicode( + ICD * pIcd, + FLMUINT uiLanguage, + FLMUNICODE * puzStr1, + FLMUINT uiByteLen1, + FLMUNICODE * puzStr2, + FLMUINT uiByteLen2, + FLMINT * piCompare); + +FSTATIC RCODE ixKeyGetUnicode( + F_Db * pDb, + ICD * pIcd, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiKeyComponent, + F_OldNodeList * pOldNodeList, + F_DataVector * pSearchKey, + F_DynaBuf * pDynaBuf); + +FSTATIC RCODE ixKeyGetBinary( + F_Db * pDb, + ICD * pIcd, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiKeyComponent, + F_OldNodeList * pOldNodeList, + F_DataVector * pSearchKey, + F_DynaBuf * pDynaBuf); + +FSTATIC RCODE krefQuickSort( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY ** pEntryTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds); + +FSTATIC RCODE krefKillDups( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY ** pKrefTbl, + FLMUINT * puiKrefTotal); + +/*************************************************************************** +Desc: Compares result set entries during the finalization stage to allow + the result set to be sorted and to remove duplicates. +*****************************************************************************/ +FSTATIC RCODE ixKeyGetNodeId( + IXD * pIxd, + const FLMBYTE * pucKey, + const FLMBYTE * pucKeyEnd, + FLMUINT uiKeyComponent, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiComponent; + FLMUINT uiComponentLen; + + // Skip past all of the remaining key components so we can get to the + // node ID list. We are currently positioned on the key component + // specified in uiKeyComponent. NOTE: uiKeyComponent is zero-based, + // 0=1st component, 1=2nd component, etc. + + uiComponent = uiKeyComponent; + while (pucKey < pucKeyEnd && uiComponent < pIxd->uiNumKeyComponents) + { + uiComponentLen = getKeyComponentLength( pucKey); + if (uiComponentLen != KEY_HIGH_VALUE && uiComponentLen != KEY_LOW_VALUE) + { + pucKey += (uiComponentLen + 2); + } + else + { + pucKey += 2; + } + uiComponent++; + } + + // See if there are node IDs in the key. A 0xFF could be present if + // we have set a "high" node ID. + + if (pucKey >= pucKeyEnd || *pucKey == 0xFF) + { + *pui64NodeId = 0; + goto Exit; + } + + // At this point, we better have all of the node ids, including + // document ID. + + // Skip past the document ID + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, NULL))) + { + goto Exit; + } + if (pucKey >= pucKeyEnd) + { + *pui64NodeId = 0; + goto Exit; + } + + // Skip all of the node ids up to the one we want + + for (uiComponent = 0; uiComponent < uiKeyComponent; uiComponent++) + { + // Skip the component node ID - passing a NULL for the last + // parameter is cheaper than getting it out. + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, NULL))) + { + goto Exit; + } + if (pucKey >= pucKeyEnd) + { + *pui64NodeId = 0; + goto Exit; + } + } + + // Should now be positioned on the one we want, extract it. + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey, pucKeyEnd, pui64NodeId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Compares result set entries during the finalization stage to allow + the result set to be sorted and to remove duplicates. +*****************************************************************************/ +FSTATIC RCODE ixKeyCompareUnicode( + ICD * pIcd, + FLMUINT uiLanguage, + FLMUNICODE * puzStr1, + FLMUINT uiByteLen1, + FLMUNICODE * puzStr2, + FLMUINT uiByteLen2, + FLMINT * piCompare) +{ + RCODE rc = NE_XFLM_OK; + F_BufferIStream bufferLStream; + F_BufferIStream bufferRStream; + F_CollIStream lStream; + F_CollIStream rStream; + + if (RC_BAD( rc = bufferLStream.open( (FLMBYTE *)puzStr1, uiByteLen1))) + { + goto Exit; + } + + if (RC_BAD( rc = bufferRStream.open( (FLMBYTE *)puzStr2, uiByteLen2))) + { + goto Exit; + } + + if (RC_BAD( rc = lStream.open( &bufferLStream, TRUE, uiLanguage, + pIcd->uiCompareRules, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = rStream.open( &bufferRStream, TRUE, uiLanguage, + pIcd->uiCompareRules, FALSE))) + { + goto Exit; + } + + if (RC_BAD( rc = fqCompareCollStreams( &lStream, &rStream, FALSE, + uiLanguage, piCompare))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Do binary comparison. +*****************************************************************************/ +FINLINE FLMINT ixKeyCompareBinary( + const void * pvData1, + FLMUINT uiLen1, + const void * pvData2, + FLMUINT uiLen2, + FLMBOOL bSortAscending) +{ + FLMINT iCompare; + + if (uiLen1 > uiLen2) + { + if ((iCompare = f_memcmp( pvData1, pvData2, uiLen2)) >= 0) + { + return( bSortAscending ? 1 : -1); + } + else + { + return( bSortAscending ? -1 : 1); + } + } + else if (uiLen1 < uiLen2) + { + if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) <= 0) + { + return( bSortAscending ? -1 : 1); + } + else + { + return( bSortAscending ? 1 : -1); + } + } + else + { + if ((iCompare = f_memcmp( pvData1, pvData2, uiLen1)) != 0) + { + if (iCompare < 0) + { + return( bSortAscending ? -1 : 1); + } + else + { + return( bSortAscending ? 1 : -1); + } + } + } + + return( 0); +} + +/*************************************************************************** +Desc: Get the unicode value for a particular key component. +*****************************************************************************/ +FSTATIC RCODE ixKeyGetUnicode( + F_Db * pDb, + ICD * pIcd, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiKeyComponent, + F_OldNodeList * pOldNodeList, + F_DataVector * pSearchKey, + F_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + IF_PosIStream * pIStream = NULL; + eDomNodeType eNodeType; + + if (ui64NodeId) + { + eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? ATTRIBUTE_NODE + : ELEMENT_NODE; + + // If there is an old-node list, first see if we can get the data + // from there. If it is not in there, see if we can get it from + // the database. + + if (pOldNodeList) + { + FLMBYTE * pucData; + FLMUINT uiDataLen; + FLMUINT uiDummy; + void * pvBuffer; + + if( pOldNodeList->findNodeInList( eNodeType, uiCollection, + ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy)) + { + + // Allocate the space needed. + + if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer))) + { + goto Exit; + } + + f_memcpy( pvBuffer, pucData, uiDataLen); + goto Exit; + } + } + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, + pIcd->uiDictNum, &pNode))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + } + + if (RC_BAD( rc = pNode->getUnicode( (IF_Db *)pDb, pDynaBuf))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pSearchKey->getUnicode( uiKeyComponent, pDynaBuf))) + { + goto Exit; + } + } + +Exit: + + if (pIStream) + { + pIStream->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Get the binary value for a particular key component. +*****************************************************************************/ +FSTATIC RCODE ixKeyGetBinary( + F_Db * pDb, + ICD * pIcd, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiKeyComponent, + F_OldNodeList * pOldNodeList, + F_DataVector * pSearchKey, + F_DynaBuf * pDynaBuf) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + IF_PosIStream * pIStream = NULL; + eDomNodeType eNodeType; + + if (ui64NodeId) + { + eNodeType = (pIcd->uiFlags & ICD_IS_ATTRIBUTE) + ? ATTRIBUTE_NODE + : ELEMENT_NODE; + + // If there is an old-node list, first see if we can get the data + // from there. If it is not in there, see if we can get it from + // the database. + + if (pOldNodeList) + { + FLMBYTE * pucData; + FLMUINT uiDataLen; + FLMUINT uiDummy; + void * pvBuffer; + + if( pOldNodeList->findNodeInList( eNodeType, uiCollection, + ui64NodeId, pIcd->uiDictNum, &pucData, &uiDataLen, &uiDummy)) + { + + // Allocate the space needed. + + if (RC_BAD( rc = pDynaBuf->allocSpace( uiDataLen, &pvBuffer))) + { + goto Exit; + } + + f_memcpy( pvBuffer, pucData, uiDataLen); + goto Exit; + } + } + + if( eNodeType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = pDb->getAttribute( uiCollection, ui64NodeId, + pIcd->uiDictNum, &pNode))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, &pNode))) + { + flmAssert( rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + } + + if( RC_BAD( rc = pNode->getBinary( (IF_Db *)pDb, pDynaBuf))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = pSearchKey->getBinary( uiKeyComponent, pDynaBuf))) + { + goto Exit; + } + } + +Exit: + + if (pIStream) + { + pIStream->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/*************************************************************************** +Desc: Compares result set entries during the finalization stage to allow + the result set to be sorted and to remove duplicates. +*****************************************************************************/ +RCODE ixKeyCompare( + F_Db * pDb, + IXD * pIxd, + F_DataVector * pSearchKey, + F_OldNodeList * pOldNodeList1, + F_OldNodeList * pOldNodeList2, + FLMBOOL bCompareDocId, + FLMBOOL bCompareNodeIds, + const void * pvKey1, + FLMUINT uiKeyLen1, + const void * pvKey2, + FLMUINT uiKeyLen2, + FLMINT * piCompare) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiKeyComponent; + ICD * pIcd; + FLMUINT uiComponentLen1; + FLMUINT uiComponentLen2; + FLMBOOL bTruncated1; + FLMBOOL bTruncated2; + const FLMBYTE * pucKey1 = (const FLMBYTE *)pvKey1; + const FLMBYTE * pucKey2 = (const FLMBYTE *)pvKey2; + const FLMBYTE * pucKeyEnd1 = pucKey1 + uiKeyLen1; + const FLMBYTE * pucKeyEnd2 = pucKey2 + uiKeyLen2; + FLMBOOL bSortAscending; + FLMBOOL bSortMissingHigh; + FLMUINT64 ui64NodeId1; + FLMUINT64 ui64NodeId2; + + flmAssert( uiKeyLen1 && uiKeyLen2); + + // Loop for each compound piece of key + + uiKeyComponent = 0; + pIcd = pIxd->pFirstKey; + for (;;) + { + bSortAscending = (pIcd->uiFlags & ICD_DESCENDING) ? FALSE : TRUE; + bSortMissingHigh = (pIcd->uiFlags & ICD_MISSING_HIGH) ? TRUE : FALSE; + uiComponentLen1 = getKeyComponentLength( pucKey1); + uiComponentLen2 = getKeyComponentLength( pucKey2); + + // See if either component is a "high" key + // NOTE: KEY_HIGH_VALUE always sorts highest, regardless of + // ascending or descending. It is never actually stored. It is + // only passed in for searching. + + if (uiComponentLen1 == KEY_HIGH_VALUE) + { + if (uiComponentLen2 == KEY_HIGH_VALUE) + { + uiComponentLen1 = uiComponentLen2 = 0; + goto Test_Exclusive; + } + else + { + *piCompare = 1; + goto Exit; + } + } + else if (uiComponentLen2 == KEY_HIGH_VALUE) + { + *piCompare = -1; + goto Exit; + } + + // See if either component is a "low" key + // NOTE: KEY_LOW_VALUE always sorts lowest, regardless of + // ascending or descending. It is never actually stored. It is + // only passed in for searching. + + if (uiComponentLen1 == KEY_LOW_VALUE) + { + if (uiComponentLen2 == KEY_LOW_VALUE) + { + uiComponentLen1 = uiComponentLen2 = 0; + goto Test_Exclusive; + } + else + { + *piCompare = -1; + goto Exit; + } + } + else if (uiComponentLen2 == KEY_LOW_VALUE) + { + *piCompare = 1; + goto Exit; + } + + // See if either component is missing. Need to apply the rules for + // sorting missing components in that case. + + if (!uiComponentLen1) + { + if (uiComponentLen2) + { + if (bSortMissingHigh) + { + *piCompare = bSortAscending ? 1 : -1; + } + else + { + *piCompare = bSortAscending ? -1 : 1; + } + goto Exit; + } + else + { + goto Test_Exclusive; + } + } + else if (!uiComponentLen2) + { + if (bSortMissingHigh) + { + *piCompare = bSortAscending ? -1 : 1; + } + else + { + *piCompare = bSortAscending ? 1 : -1; + } + goto Exit; + } + else + { + + // Component length must not exceed remaining length of key. + + flmAssert( pucKey1 + 2 + uiComponentLen1 <= pucKeyEnd1 && + pucKey2 + 2 + uiComponentLen2 <= pucKeyEnd2); + + if ((*piCompare = ixKeyCompareBinary( pucKey1 + 2, uiComponentLen1, + pucKey2 + 2, uiComponentLen2, bSortAscending)) != 0) + { + goto Exit; + } + + // Data is equal, see if one or ther other is truncated. + + bTruncated1 = isKeyComponentTruncated( pucKey1); + bTruncated2 = isKeyComponentTruncated( pucKey2); + + if (bTruncated1 || bTruncated2) + { + if (!bTruncated2) + { + *piCompare = bSortAscending ? 1 : -1; + goto Exit; + } + else if (!bTruncated1) + { + *piCompare = bSortAscending ? -1 : 1; + goto Exit; + } + + if (isSearchKeyComponent( pucKey1)) + { + flmAssert( pSearchKey); + ui64NodeId1 = pSearchKey->getID( uiKeyComponent); + + // The search key better have a node ID or the untruncated + // value. + + flmAssert( ui64NodeId1 || + !pSearchKey->isRightTruncated( uiKeyComponent)); + } + else + { + + // Need to read the data from the nodes and do a comparison. + // Get each node ID. + + if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey1, pucKeyEnd1, + uiKeyComponent, &ui64NodeId1))) + { + goto Exit; + } + flmAssert( ui64NodeId1); + } + + if (isSearchKeyComponent( pucKey2)) + { + flmAssert( pSearchKey); + ui64NodeId2 = pSearchKey->getID( uiKeyComponent); + + // The search key better have a node ID or the untruncated + // value. + + flmAssert( ui64NodeId2 || + !pSearchKey->isRightTruncated( uiKeyComponent)); + } + else + { + // Need to read the data from the nodes and do a comparison. + // Get each node ID. + + if (RC_BAD( rc = ixKeyGetNodeId( pIxd, pucKey2, pucKeyEnd2, + uiKeyComponent, &ui64NodeId2))) + { + goto Exit; + } + flmAssert( ui64NodeId2); + } + + // If the node IDs are equal, we can skip fetching the data, because + // it will be the same. + + if (ui64NodeId1 != ui64NodeId2) + { + FLMBYTE ucDynaBuf1[ 64]; + FLMBYTE ucDynaBuf2[ 64]; + F_DynaBuf dynaBuf1( ucDynaBuf1, sizeof( ucDynaBuf1)); + F_DynaBuf dynaBuf2( ucDynaBuf2, sizeof( ucDynaBuf2)); + + // Better be binary data or text data. + + switch (icdGetDataType( pIcd)) + { + case XFLM_TEXT_TYPE: + { + if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd, + pIxd->uiCollectionNum, + ui64NodeId1, uiKeyComponent, + pOldNodeList1, pSearchKey, &dynaBuf1))) + { + goto Exit; + } + if (RC_BAD( rc = ixKeyGetUnicode( pDb, pIcd, + pIxd->uiCollectionNum, + ui64NodeId2, uiKeyComponent, + pOldNodeList2, pSearchKey, &dynaBuf2))) + { + goto Exit; + } + if (RC_BAD( rc = ixKeyCompareUnicode( pIcd, + pIxd->uiLanguage, + dynaBuf1.getUnicodePtr(), + dynaBuf1.getDataLength(), + dynaBuf2.getUnicodePtr(), + dynaBuf2.getDataLength(), piCompare))) + { + goto Exit; + } + if (*piCompare < 0) + { + *piCompare = bSortAscending ? -1 : 1; + goto Exit; + } + else if (*piCompare > 0) + { + *piCompare = bSortAscending ? 1 : -1; + goto Exit; + } + break; + } + + case XFLM_BINARY_TYPE: + { + if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd, + pIxd->uiCollectionNum, + ui64NodeId1, uiKeyComponent, + pOldNodeList1, pSearchKey, &dynaBuf1))) + { + goto Exit; + } + + if (RC_BAD( rc = ixKeyGetBinary( pDb, pIcd, + pIxd->uiCollectionNum, ui64NodeId2, uiKeyComponent, + pOldNodeList2, pSearchKey, &dynaBuf2))) + { + goto Exit; + } + + if ((*piCompare = ixKeyCompareBinary( + dynaBuf1.getBufferPtr(), + dynaBuf1.getDataLength(), + dynaBuf2.getBufferPtr(), + dynaBuf2.getDataLength(), + bSortAscending)) != 0) + { + goto Exit; + } + break; + } + + default: + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + } + } + } + +Test_Exclusive: + + // See if either component is exclusive - everything else is + // equal up to this point. + + if (isKeyComponentLTExclusive( pucKey1)) + { + if (!isKeyComponentLTExclusive( pucKey2)) + { + *piCompare = bSortAscending ? -1 : 1; + goto Exit; + } + } + else if (isKeyComponentGTExclusive( pucKey1)) + { + if (!isKeyComponentGTExclusive( pucKey2)) + { + *piCompare = bSortAscending ? 1 : -1; + goto Exit; + } + } + else if (isKeyComponentLTExclusive( pucKey2)) + { + *piCompare = bSortAscending ? 1 : -1; + goto Exit; + } + else if (isKeyComponentGTExclusive( pucKey2)) + { + *piCompare = bSortAscending ? -1 : 1; + goto Exit; + } + + // Position to the end of this component + + pucKey1 += (2 + uiComponentLen1); + pucKey2 += (2 + uiComponentLen2); + + pIcd = pIcd->pNextKeyComponent; + uiKeyComponent++; + + // If there are no more ICDs, we are done with the key + // components. + + if (!pIcd) + { + break; + } + + // See if we are out of key components - this may be a search that + // passed in only a partial key. + + if (pucKey1 >= pucKeyEnd1) + { + *piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1; + goto Exit; + } + else if (pucKey2 >= pucKeyEnd2) + { + *piCompare = 1; + goto Exit; + } + } + + // Compare the node ID list, if being requested to. Includes comparing of the + // last byte, which is the total number of bytes in the node ID list. + + if (bCompareDocId || bCompareNodeIds) + { + + // See if we have a node IDs - this may be a search that + // passed in only a partial key and there are no NODE ids on it. + + if (pucKey1 >= pucKeyEnd1) + { + *piCompare = (pucKey2 >= pucKeyEnd2) ? 0 : -1; + goto Exit; + } + else if (pucKey2 >= pucKeyEnd2) + { + *piCompare = 1; + goto Exit; + } + + // See if either one has an ID buffer of "high" + + if (*pucKey1 == 0xFF) + { + + // Key1 has a "high" set of node IDs, see what key2 has. + + *piCompare = (*pucKey2 == 0xFF) ? 0 : 1; + goto Exit; + } + else if (*pucKey2 == 0xFF) + { + // Key2 has a "high" set of node IDs, key1 does not. + + *piCompare = -1; + goto Exit; + } + else if (bCompareNodeIds) + { + FLMUINT uiNodeIDLen1 = (FLMUINT)(pucKeyEnd1 - pucKey1); + FLMUINT uiNodeIDLen2 = (FLMUINT)(pucKeyEnd2 - pucKey2); + + // We will always compare doc ID if we are also comparing + // node ID. Hence, although it is probably not entirely + // necessary, we require the caller to also set bCompareDocId + // to TRUE, just so that he doesn't think it is possible to + // compare the node ids without comparing the document id. + + flmAssert( bCompareDocId); + + *piCompare = ixKeyCompareBinary( pucKey1, uiNodeIDLen1, + pucKey2, uiNodeIDLen2, TRUE); + } + else + { + FLMUINT64 ui64DocId1; + FLMUINT64 ui64DocId2; + + // Get the document ID and compare it, and only it. + // At this point, both keys should be positioned to + // get the document ID. + + if (RC_BAD( rc = flmDecodeSEN64( &pucKey1, pucKeyEnd1, &ui64DocId1))) + { + goto Exit; + } + if (RC_BAD( rc = flmDecodeSEN64( &pucKey2, pucKeyEnd2, &ui64DocId2))) + { + goto Exit; + } + if (ui64DocId1 == ui64DocId2) + { + *piCompare = 0; + } + else if (ui64DocId1 < ui64DocId2) + { + *piCompare = -1; + } + else + { + *piCompare = 1; + } + } + } + else + { + *piCompare = 0; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compare function used to compare index number and key +****************************************************************************/ +FINLINE RCODE krefCompareIxAndKey( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY * pKrefA, + KREF_ENTRY * pKrefB, + FLMINT * piCompare) +{ + RCODE rc = NE_XFLM_OK; + + // Compare index numbers + + if ((*piCompare = ((FLMINT) pKrefA->ui16IxNum) - + ((FLMINT) pKrefB->ui16IxNum)) != 0) + { + goto Exit; + } + + if (!pIxd || pIxd->uiIndexNum != (FLMUINT)pKrefA->ui16IxNum) + { + if (RC_BAD( rc = pDb->getDict()->getIndex( (FLMUINT)pKrefA->ui16IxNum, + NULL, &pIxd, TRUE))) + { + goto Exit; + } + } + + if (RC_BAD( rc = ixKeyCompare( pDb, pIxd, NULL, + pKrefA->bDelete ? pDb->getOldNodeList() : NULL, + pKrefB->bDelete ? pDb->getOldNodeList() : NULL, + TRUE, TRUE, + &pKrefA [1], (FLMUINT)pKrefA->ui16KeyLen, + &pKrefB [1], (FLMUINT)pKrefB->ui16KeyLen, piCompare))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Compare function used to compare key data +****************************************************************************/ +FINLINE FLMBOOL krefIsKeyDataEqual( + KREF_ENTRY * pKrefA, + KREF_ENTRY * pKrefB) +{ + if( pKrefA->uiDataLen != pKrefB->uiDataLen) + { + return( FALSE); + } + + if( pKrefA->uiDataLen) + { + if( f_memcmp( (FLMBYTE *)(&pKrefA [1]) + + pKrefA->ui16KeyLen + 1, + (FLMBYTE *)(&pKrefB [1]) + + pKrefB->ui16KeyLen + 1, + pKrefA->uiDataLen) != 0) + { + return( FALSE); + } + } + + return( TRUE); +} + +/**************************************************************************** +Desc: Compare function used to compare two keys. +****************************************************************************/ +FINLINE RCODE krefSortCompare( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY * pKrefA, + KREF_ENTRY * pKrefB, + FLMINT * piCompare) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefA, pKrefB, piCompare))) + { + goto Exit; + } + + if (*piCompare == 0) + { + *piCompare = (pKrefA->uiSequence < pKrefB->uiSequence) ? -1 : 1; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Checks if the current database has any UNIQUE indexes that need + to checked. Also does duplicate processing for the record. +****************************************************************************/ +RCODE F_Db::processDupKeys( + IXD * pIxd) +{ + RCODE rc = NE_XFLM_OK; + + // Sort and remove duplicates + + if (m_uiKrefCount > 1) + { + if (RC_BAD( rc = krefQuickSort( this, pIxd, m_pKrefTbl, + 0, m_uiKrefCount - 1))) + { + goto Exit; + } + if (RC_BAD( rc = krefKillDups( this, pIxd, + m_pKrefTbl, &m_uiKrefCount))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Commit (write out) all keys that have built up in the KREF table. +****************************************************************************/ +RCODE F_Db::keysCommit( + FLMBOOL bCommittingTrans, + FLMBOOL bSortKeys) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiRflToken = 0; + + // If the Kref has not been initialized, there is no + // work to do. + + if (m_bKrefSetup) + { + LFILE * pLFile = NULL; + IXD * pIxd; + FLMUINT uiTotal = m_uiKrefCount; + KREF_ENTRY * pKref; + KREF_ENTRY ** pKrefTbl = m_pKrefTbl; + FLMUINT uiKrefNum; + FLMUINT uiLastIxNum; + + // We should not have reached this point if bAbortTrans is TRUE + + if( RC_BAD( m_AbortRc)) + { + rc = RC_SET_AND_ASSERT( m_AbortRc); + goto Exit; + } + + // Sort the KREF table, if it contains more than one key. + // This will sort all keys from the same index the same. + + if (uiTotal > 1 && bSortKeys) + { + processDupKeys( NULL); + uiTotal = m_uiKrefCount; + } + + // Disable RFL logging + + if( uiTotal) + { + m_pDatabase->m_pRfl->disableLogging( &uiRflToken); + } + + // Loop through the KREF table outputting all keys + + uiLastIxNum = 0; + for (uiKrefNum = 0; uiKrefNum < uiTotal; uiKrefNum++) + { + pKref = pKrefTbl [uiKrefNum]; + + // See if the LFILE changed + + flmAssert( pKref->ui16IxNum); + + if (pKref->ui16IxNum != uiLastIxNum) + { + uiLastIxNum = pKref->ui16IxNum; + if (RC_BAD( rc = m_pDict->getIndex( uiLastIxNum, + &pLFile, &pIxd, TRUE))) + { + goto Exit; + } + } + + // Flush the key to the index + if (m_pKeyColl) + { + m_pKeyColl->addKey( this, pIxd, pKref); + } + else + { + if (RC_BAD(rc = refUpdate( pLFile, pIxd, pKref, TRUE))) + { + if (rc != NE_XFLM_NOT_UNIQUE) + { + RC_UNEXPECTED_ASSERT( rc); + } + goto Exit; + } + } + } + + if (bCommittingTrans) + { + krefCntrlFree(); + } + else + { + // Empty the table out so we can add more keys in this trans. + + m_pKrefPool->poolReset( NULL, TRUE); + m_uiKrefCount = 0; + m_uiTotalKrefBytes = 0; + } + } + +Exit: + + if( RC_BAD( rc)) + { + setMustAbortTrans( rc); + } + + if( uiRflToken) + { + m_pDatabase->m_pRfl->enableLogging( &uiRflToken); + } + + return( rc); +} + +/*************************************************************************** +Desc: Quick sort an array of KREF_ENTRY * values. +****************************************************************************/ +FSTATIC RCODE krefQuickSort( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY ** pEntryTbl, + FLMUINT uiLowerBounds, + FLMUINT uiUpperBounds) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLBPos; + FLMUINT uiUBPos; + FLMUINT uiMIDPos; + FLMUINT uiLeftItems; + FLMUINT uiRightItems; + KREF_ENTRY * pCurEntry; + KREF_ENTRY * pTempKref; + FLMINT iCompare; + +Iterate_Larger_Half: + + uiUBPos = uiUpperBounds; + uiLBPos = uiLowerBounds; + uiMIDPos = (uiUpperBounds + uiLowerBounds + 1) / 2; + pCurEntry = pEntryTbl[ uiMIDPos ]; + for( ;;) + { + for (;;) + { + if (uiLBPos != uiMIDPos) + { + if (RC_BAD( rc = krefSortCompare( pDb, pIxd, + pEntryTbl[ uiLBPos], pCurEntry, &iCompare))) + { + goto Exit; + } + if (iCompare >= 0) + { + break; + } + } + if (uiLBPos >= uiUpperBounds) + { + break; + } + uiLBPos++; + } + + for (;;) + { + if (uiUBPos != uiMIDPos) + { + if (RC_BAD( rc = krefSortCompare( pDb, pIxd, pCurEntry, + pEntryTbl[ uiUBPos], &iCompare))) + { + goto Exit; + } + if (iCompare >= 0) + { + break; + } + } + if (!uiUBPos) + { + break; + } + uiUBPos--; + } + + if (uiLBPos < uiUBPos) // Interchange and continue loop. + { + + // Interchange [uiLBPos] with [uiUBPos]. + + KY_SWAP( pEntryTbl, 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) + { + + // Interchange [uiLBPos] with [uiMIDPos] + + KY_SWAP( pEntryTbl, uiMIDPos, uiLBPos ); + uiMIDPos = uiLBPos; + } + else if (uiMIDPos < uiUBPos) + { + + // Interchange [uUBPos] with [uiMIDPos] + + KY_SWAP( pEntryTbl, 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) + { + if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl, + uiLowerBounds, uiMIDPos - 1))) + { + goto Exit; + } + } + 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) + { + if (RC_BAD( rc = krefQuickSort( pDb, pIxd, pEntryTbl, + uiMIDPos + 1, uiUpperBounds))) + { + goto Exit; + } + } + uiUpperBounds = uiMIDPos - 1; + goto Iterate_Larger_Half; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Kill all duplicate keys in the KREF table. +****************************************************************************/ +FSTATIC RCODE krefKillDups( + F_Db * pDb, + IXD * pIxd, + KREF_ENTRY ** pKrefTbl, + FLMUINT * puiKrefTotal) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurKref = 0; + FLMUINT uiLastKref = *puiKrefTotal; + FLMUINT uiFirstForKey; + FLMUINT uiLastForKey; + FLMUINT uiNewPosOffset = 0; + FLMINT iCompare; + + while( uiCurKref < uiLastKref) + { + uiFirstForKey = uiLastForKey = uiCurKref; + uiCurKref = uiFirstForKey + 1; + + while( uiCurKref < uiLastKref) + { + if (RC_BAD( rc = krefCompareIxAndKey( pDb, pIxd, pKrefTbl[ uiFirstForKey], + pKrefTbl[ uiCurKref], &iCompare))) + { + goto Exit; + } + + if (iCompare) + { + break; + } + + uiLastForKey = uiCurKref++; + } + + if( uiFirstForKey == uiLastForKey) + { + pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; + continue; + } + + if( pKrefTbl[ uiFirstForKey]->bDelete) + { + if( pKrefTbl[ uiLastForKey]->bDelete) + { + pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; + } + else + { +TestCancel: + // See if the operations cancel each other. If they don't, we + // need to keep both operations + + if( !krefIsKeyDataEqual( pKrefTbl[ uiFirstForKey], pKrefTbl[ uiLastForKey])) + { + pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiFirstForKey]; + pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey]; + } + } + } + else + { + if( pKrefTbl[ uiLastForKey]->bDelete) + { + goto TestCancel; + } + else + { + pKrefTbl[ uiNewPosOffset++] = pKrefTbl[ uiLastForKey]; + } + } + } + + *puiKrefTotal = uiNewPosOffset; + +Exit: + + return( rc); +} diff --git a/version5/src/kyunlock.cpp b/version5/src/kyunlock.cpp new file mode 100644 index 0000000..ca9bbec --- /dev/null +++ b/version5/src/kyunlock.cpp @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains the routines to initialize and set up +// structures for indexing. +// +// Tabs: 3 +// +// Copyright (c) 1992-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: kyunlock.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: Setup routine for the KREF_CNTRL structure for record updates. +****************************************************************************/ +RCODE F_Db::krefCntrlCheck( void) +{ + RCODE rc = NE_XFLM_OK; + + // Check if we need to flush keys between updates, but not during the + // processing of an update. + + if( m_bKrefSetup) + { + if( isKrefOverThreshold() || + (m_pOldNodeList && m_pOldNodeList->getNodeCount())) + { + if (RC_BAD( rc = keysCommit( FALSE))) + { + goto Exit; + } + } + } + else + { + m_uiKrefCount = 0; + m_uiTotalKrefBytes = 0; + m_pKrefPool = NULL; + m_bReuseKrefPool = FALSE; + m_bKrefCompoundKey = FALSE; + m_pKrefReset = NULL; + m_bKrefSetup = TRUE; + + if (m_eTransType == XFLM_UPDATE_TRANS) + { + m_pKrefPool = &m_pDatabase->m_krefPool; + m_bReuseKrefPool = TRUE; + m_pKrefPool->poolReset( NULL, TRUE); + } + else + { + m_pKrefPool = &m_tmpKrefPool; + m_bReuseKrefPool = FALSE; + m_pKrefPool->poolInit( DEFAULT_KREF_POOL_BLOCK_SIZE); + } + + if( !m_pKrefTbl) + { + if( RC_BAD( rc = f_alloc( + DEFAULT_KREF_TBL_SIZE * sizeof( KREF_ENTRY *), &m_pKrefTbl))) + { + goto Exit; + } + + m_uiKrefTblSize = DEFAULT_KREF_TBL_SIZE; + } + + if( !m_pucKrefKeyBuf) + { + if (RC_BAD( rc = f_alloc( MAX_KEY_SIZ, &m_pucKrefKeyBuf))) + { + goto Exit; + } + } + } + + m_pKrefReset = m_pKrefPool->poolMark(); + flmAssert( m_pucKrefKeyBuf); + +Exit: + + if (RC_BAD( rc)) + { + krefCntrlFree(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Frees the memory associated with the KREF +****************************************************************************/ +void F_Db::krefCntrlFree( void) +{ + if( m_bKrefSetup) + { + if( m_bReuseKrefPool) + { + m_pKrefPool->poolReset( NULL, TRUE); + } + else + { + m_pKrefPool->poolFree(); + } + + if( m_pKrefTbl && m_uiKrefTblSize != DEFAULT_KREF_TBL_SIZE) + { + f_free( &m_pKrefTbl); + m_uiKrefTblSize = 0; + } + + m_uiKrefCount = 0; + m_uiTotalKrefBytes = 0; + m_pKrefPool = NULL; + m_bReuseKrefPool = FALSE; + m_bKrefCompoundKey = FALSE; + m_pKrefReset = NULL; + m_bKrefSetup = FALSE; + + if (m_pOldNodeList) + { + m_pOldNodeList->resetList(); + } + } +} diff --git a/version5/src/ncache.cpp b/version5/src/ncache.cpp new file mode 100644 index 0000000..49ee1ba --- /dev/null +++ b/version5/src/ncache.cpp @@ -0,0 +1,5610 @@ +//------------------------------------------------------------------------------ +// Desc: This is the DOM Node cache for XFLAIM +// +// 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: ncache.cpp 3115 2006-01-19 13:24:39 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#if defined( FLM_NLM) && !defined( __MWERKS__) +// Disable "Warning! W549: col(XX) 'sizeof' operand contains +// compiler generated information" + #pragma warning 549 9 +#endif + +/**************************************************************************** +Desc: Constructor +****************************************************************************/ +F_NodeCacheMgr::F_NodeCacheMgr() +{ + m_pPurgeList = NULL; + m_pHeapList = NULL; + m_pOldList = NULL; + f_memset( &m_Usage, 0, sizeof( m_Usage)); + m_ppHashBuckets = NULL; + m_uiNumBuckets = 0; + m_uiHashFailTime = 0; + m_uiHashMask = 0; + m_uiPendingReads = 0; + m_uiIoWaits = 0; + m_pFirstNode = NULL; + m_bReduceInProgress = FALSE; +#ifdef FLM_DEBUG + m_bDebug = FALSE; +#endif +} + +/**************************************************************************** +Desc: Constructor for F_CachedNode +****************************************************************************/ +F_CachedNode::F_CachedNode() +{ + m_pPrevInBucket = NULL; + m_pNextInBucket = NULL; + m_pPrevInDatabase = NULL; + m_pNextInDatabase = NULL; + m_pOlderVersion = NULL; + m_pNewerVersion = NULL; + m_pPrevInHeapList = NULL; + m_pNextInHeapList = NULL; + m_pPrevInOldList = NULL; + m_pNextInOldList = NULL; + m_ui64LowTransId = 0; + + // Set the high transaction ID to FLM_MAX_UINT64 so that this will NOT + // be treated as one that had memory assigned to the old version nodes. + + m_ui64HighTransId = FLM_MAX_UINT64; + m_pNotifyList = NULL; + m_uiCacheFlags = 0; + m_uiStreamUseCount = 0; + + // Items initialized in constructor + + m_uiDataBufSize = 0; + m_pucData = NULL; + m_pNodeList = NULL; + m_ppAttrList = NULL; + m_uiAttrCount = 0; + m_uiTotalAttrSize = 0; + m_uiFlags = 0; + + f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/**************************************************************************** +Desc: Destructor for F_CachedNode object. + This routine assumes the global mutex is already locked. +****************************************************************************/ +F_CachedNode::~F_CachedNode() +{ + // Don't include attribute size, because it will be subtracted out + // when we delete the attr items. + + FLMUINT uiSize = memSize() - m_uiTotalAttrSize; + FLMBYTE * pucActualAlloc; + + flmAssert( !m_uiStreamUseCount); + f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); + + // If this is an old version, decrement the old version counters. + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize && + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount--; + unlinkFromOldList(); + } + + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize && + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount--; + + if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + + // Free the m_pucData, if any + + if (m_pucData) + { + pucActualAlloc = getActualPointer( m_pucData); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + m_uiDataBufSize, &pucActualAlloc); + m_pucData = NULL; + } + + if (m_pNodeList) + { + pucActualAlloc = getActualPointer( m_pNodeList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcNodeListBufSize( m_nodeInfo.uiChildElmCount), + &pucActualAlloc); + m_pNodeList = NULL; + } + + if (m_uiAttrCount) + { + FLMUINT uiLoop; + + for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) + { + delete m_ppAttrList [uiLoop]; + } + + flmAssert( !m_uiTotalAttrSize); + + pucActualAlloc = getActualPointer( m_ppAttrList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcAttrListBufSize( m_uiAttrCount), + &pucActualAlloc); + m_ppAttrList = NULL; + m_uiAttrCount = 0; + } + + if (shouldRehash( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount, + gv_XFlmSysData.pNodeCacheMgr->m_uiNumBuckets)) + { + if (checkHashFailTime( &gv_XFlmSysData.pNodeCacheMgr->m_uiHashFailTime)) + { + (void)gv_XFlmSysData.pNodeCacheMgr->rehash(); + } + } +} + +/**************************************************************************** +Desc: This routine frees a purged node from node cache. This routine assumes + that the node cache mutex has already been locked. +****************************************************************************/ +void F_CachedNode::freePurged( void) +{ + + // Unlink the node from the purged list. + + unlinkFromPurged(); + + // Free the F_CachedNode object. + + unsetPurged(); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + delete this; +} + +/**************************************************************************** +Desc: This routine frees a node in the node cache. This routine assumes + that the node cache mutex has already been locked. +****************************************************************************/ +void F_CachedNode::freeCache( + FLMBOOL bPutInPurgeList) +{ + FLMBOOL bOldVersion; + + bOldVersion = (FLMBOOL)((m_ui64HighTransId != FLM_MAX_UINT64) + ? TRUE + : FALSE); + + // Unlink the node from its various lists. + + gv_XFlmSysData.pNodeCacheMgr->m_MRUList.unlinkGlobal( + (F_CachedItem *)this); + unlinkFromDatabase(); + + if (!m_pNewerVersion) + { + F_CachedNode * pOlderVersion = m_pOlderVersion; + + unlinkFromHashBucket(); + + // If there was an older version, it now needs to be + // put into the hash bucket. + + if (pOlderVersion) + { + unlinkFromVerList(); + pOlderVersion->linkToHashBucket(); + } + } + else + { + unlinkFromVerList(); + } + + if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + + // Free the F_CachedNode structure if not putting in purge list. + + if (!bPutInPurgeList) + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + delete this; + } + else + { +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + if ((m_pNextInGlobal = gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInGlobal->unprotectCachedItem(); +#endif + m_pNextInGlobal->m_pPrevInGlobal = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInGlobal->protectCachedItem(); +#endif + } + gv_XFlmSysData.pNodeCacheMgr->m_pPurgeList = this; + + // Unset the dirty flags - don't want anything in the purge list + // to be dirty. + + m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW); + setPurged(); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + flmAssert( !m_pPrevInGlobal); + } +} + +/**************************************************************************** +Desc: This routine initializes node cache manager. +****************************************************************************/ +RCODE F_NodeCacheMgr::initCache( void) +{ + RCODE rc = NE_XFLM_OK; + + // Allocate the hash buckets. + + if (RC_BAD( rc = f_calloc( + (FLMUINT)sizeof( F_CachedNode *) * + (FLMUINT)MIN_HASH_BUCKETS, + &m_ppHashBuckets))) + { + goto Exit; + } + m_uiNumBuckets = MIN_HASH_BUCKETS; + m_uiHashMask = m_uiNumBuckets - 1; + gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); + + // Set up the F_CachedNode object allocator + + if (RC_BAD( rc = m_nodeAllocator.setup( &m_nodeRelocator, + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, + TRUE, sizeof( F_CachedNode), &m_Usage.slabUsage))) + { + goto Exit; + } + + // Set up the buffer allocator for F_CachedNode objects + + if (RC_BAD( rc = m_bufAllocator.setup( + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, FALSE, + &m_Usage.slabUsage))) + { + goto Exit; + } + + // Set up the allocator for attribute items + + if( RC_BAD( rc = m_attrItemAllocator.setup( &m_attrItemRelocator, + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, FALSE, + sizeof( F_AttrItem), &m_Usage.slabUsage))) + { + goto Exit; + } + +#ifdef FLM_DEBUG + m_bDebug = TRUE; +#endif + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Determine if a node can be moved. +Notes: This routine assumes the node cache mutex is locked + This is a static method, so there is no "this" pointer to the + F_NodeCacheMgr object. +****************************************************************************/ +FLMBOOL F_NodeRelocator::canRelocate( + void * pvAlloc) +{ + return( ((F_CachedNode *)pvAlloc)->nodeInUse() ? FALSE : TRUE); +} + +/**************************************************************************** +Desc: Fixes up all pointers needed to allow an F_CachedNode object to be + moved to a different location in memory +Notes: This routine assumes the node cache mutex is locked. + This is a static method, so there is no "this" pointer to the + F_NodeCacheMgr object. +****************************************************************************/ +void F_NodeRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_CachedNode * pOldNode = (F_CachedNode *)pvOldAlloc; + F_CachedNode * pNewNode = (F_CachedNode *)pvNewAlloc; + F_CachedNode ** ppBucket; + F_Database * pDatabase = pOldNode->m_pDatabase; + F_NodeCacheMgr * pNodeCacheMgr = gv_XFlmSysData.pNodeCacheMgr; + FLMBYTE * pucActualAlloc; + + flmAssert( !pOldNode->nodeInUse()); + flmAssert( pvNewAlloc < pvOldAlloc); + + // Update the F_CachedNode pointer in the data buffer + + if( pNewNode->m_pucData) + { + pucActualAlloc = getActualPointer( pNewNode->m_pucData); + flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); + pNewNode->setNodeAndDataPtr( pucActualAlloc); + } + + if( pNewNode->m_pNodeList) + { + pucActualAlloc = getActualPointer( pNewNode->m_pNodeList); + flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); + pNewNode->setNodeListPtr( pucActualAlloc); + } + + if( pNewNode->m_ppAttrList) + { + FLMUINT uiLoop; + + pucActualAlloc = getActualPointer( pNewNode->m_ppAttrList); + flmAssert( *((F_CachedNode **)(pucActualAlloc)) == pOldNode); + pNewNode->setAttrListPtr( pucActualAlloc); + + for (uiLoop = 0; uiLoop < pNewNode->m_uiAttrCount; uiLoop++) + { + pNewNode->m_ppAttrList [uiLoop]->m_pCachedNode = pNewNode; + } + } + + if (pNewNode->m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInDatabase->unprotectCachedItem(); +#endif + pNewNode->m_pPrevInDatabase->m_pNextInDatabase = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInDatabase->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInDatabase->unprotectCachedItem(); +#endif + pNewNode->m_pNextInDatabase->m_pPrevInDatabase = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInDatabase->protectCachedItem(); +#endif + } + + if (pNewNode->m_pPrevInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInGlobal->unprotectCachedItem(); +#endif + pNewNode->m_pPrevInGlobal->m_pNextInGlobal = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInGlobal->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNextInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInGlobal->unprotectCachedItem(); +#endif + pNewNode->m_pNextInGlobal->m_pPrevInGlobal = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInGlobal->protectCachedItem(); +#endif + } + + if (pNewNode->m_pPrevInBucket) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInBucket->unprotectCachedItem(); +#endif + pNewNode->m_pPrevInBucket->m_pNextInBucket = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInBucket->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNextInBucket) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInBucket->unprotectCachedItem(); +#endif + pNewNode->m_pNextInBucket->m_pPrevInBucket = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInBucket->protectCachedItem(); +#endif + } + + if (pNewNode->m_pOlderVersion) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pOlderVersion->unprotectCachedItem(); +#endif + pNewNode->m_pOlderVersion->m_pNewerVersion = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pOlderVersion->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNewerVersion) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNewerVersion->unprotectCachedItem(); +#endif + pNewNode->m_pNewerVersion->m_pOlderVersion = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNewerVersion->protectCachedItem(); +#endif + } + + if (pNewNode->m_pPrevInHeapList) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInHeapList->unprotectCachedItem(); +#endif + pNewNode->m_pPrevInHeapList->m_pNextInHeapList = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInHeapList->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNextInHeapList) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInHeapList->unprotectCachedItem(); +#endif + pNewNode->m_pNextInHeapList->m_pPrevInHeapList = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInHeapList->protectCachedItem(); +#endif + } + + if (pNewNode->m_pPrevInOldList) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInOldList->unprotectCachedItem(); +#endif + pNewNode->m_pPrevInOldList->m_pNextInOldList = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pPrevInOldList->protectCachedItem(); +#endif + } + + if (pNewNode->m_pNextInOldList) + { +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInOldList->unprotectCachedItem(); +#endif + pNewNode->m_pNextInOldList->m_pPrevInOldList = pNewNode; +#ifdef FLM_CACHE_PROTECT + pNewNode->m_pNextInOldList->protectCachedItem(); +#endif + } + + if( pDatabase) + { + if (pDatabase->m_pFirstNode == pOldNode) + { + pDatabase->m_pFirstNode = pNewNode; + } + + if( pDatabase->m_pLastNode == pOldNode) + { + pDatabase->m_pLastNode = pNewNode; + } + + if( pDatabase->m_pLastDirtyNode == pOldNode) + { + pDatabase->m_pLastDirtyNode = pNewNode; + } + } + + ppBucket = pNodeCacheMgr->nodeHash( pOldNode->m_nodeInfo.ui64NodeId); + if( *ppBucket == pOldNode) + { + *ppBucket = pNewNode; + } + + if (pNodeCacheMgr->m_MRUList.m_pMRUItem == (F_CachedItem *)pOldNode) + { + pNodeCacheMgr->m_MRUList.m_pMRUItem = pNewNode; + } + + if (pNodeCacheMgr->m_MRUList.m_pLRUItem == (F_CachedItem *)pOldNode) + { + pNodeCacheMgr->m_MRUList.m_pLRUItem = pNewNode; + } + + if (pNodeCacheMgr->m_pHeapList == pOldNode) + { + pNodeCacheMgr->m_pHeapList = pNewNode; + } + + if (pNodeCacheMgr->m_pOldList == pOldNode) + { + pNodeCacheMgr->m_pOldList = pNewNode; + } + + if (pNodeCacheMgr->m_pPurgeList == pOldNode) + { + pNodeCacheMgr->m_pPurgeList = pNewNode; + } +} + +/**************************************************************************** +Desc: Determine if a data buffer of an F_CachedNode object can be moved. + This routine assumes that the node cache mutex is locked. +****************************************************************************/ +FLMBOOL F_NodeDataRelocator::canRelocate( + void * pvAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); + + if( pNode->nodeInUse()) + { + return( FALSE); + } + else + { + flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvAlloc); + return( TRUE); + } +} + +/**************************************************************************** +Desc: Relocate the data buffer of an F_CachedNode object. This routine assumes + that the node cache mutex is locked. +****************************************************************************/ +void F_NodeDataRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); + + flmAssert( !pNode->nodeInUse()); + flmAssert( pvNewAlloc < pvOldAlloc); + flmAssert( getActualPointer( pNode->m_pucData) == (FLMBYTE *)pvOldAlloc); + + pNode->setNodeAndDataPtr( (FLMBYTE *)pvNewAlloc); +} + +/**************************************************************************** +Desc: Determine if a node list of an F_CachedNode object can be moved. + This routine assumes that the node cache mutex is locked. +****************************************************************************/ +FLMBOOL F_NodeListRelocator::canRelocate( + void * pvAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); + + if( pNode->nodeInUse()) + { + return( FALSE); + } + else + { + flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvAlloc); + return( TRUE); + } +} + +/**************************************************************************** +Desc: Relocate the node list of an F_CachedNode object. This routine assumes + that the node cache mutex is locked. +****************************************************************************/ +void F_NodeListRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); + + flmAssert( !pNode->nodeInUse()); + flmAssert( pvNewAlloc < pvOldAlloc); + flmAssert( getActualPointer( pNode->m_pNodeList) == (FLMBYTE *)pvOldAlloc); + + pNode->setNodeListPtr( (FLMBYTE *)pvNewAlloc); +} + +/**************************************************************************** +Desc: Determine if an attr list of an F_CachedNode object can be moved. + This routine assumes that the node cache mutex is locked. +****************************************************************************/ +FLMBOOL F_AttrListRelocator::canRelocate( + void * pvAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvAlloc); + + if( pNode->nodeInUse()) + { + return( FALSE); + } + else + { + flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvAlloc); + return( TRUE); + } +} + +/**************************************************************************** +Desc: Relocate the attr list of an F_CachedNode object. This routine assumes + that the node cache mutex is locked. +****************************************************************************/ +void F_AttrListRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_CachedNode * pNode = *((F_CachedNode **)pvOldAlloc); + + flmAssert( !pNode->nodeInUse()); + flmAssert( pvNewAlloc < pvOldAlloc); + flmAssert( getActualPointer( pNode->m_ppAttrList) == (FLMBYTE *)pvOldAlloc); + + pNode->setAttrListPtr( (FLMBYTE *)pvNewAlloc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_AttrItemRelocator::canRelocate( + void * pvAlloc) +{ + F_AttrItem * pAttrItem = (F_AttrItem *)pvAlloc; + + if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_AttrItemRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_AttrItem * pOldAttrItem = (F_AttrItem *)pvOldAlloc; + F_AttrItem * pNewAttrItem = (F_AttrItem *)pvNewAlloc; + F_CachedNode * pCachedNode = pNewAttrItem->m_pCachedNode; + FLMUINT uiPos; + + flmAssert( !pCachedNode->nodeInUse()); + + // Find the new attr item slot + + if (pCachedNode->getAttribute( pNewAttrItem->m_uiNameId, &uiPos) == + pOldAttrItem) + { + pCachedNode->m_ppAttrList [uiPos] = pNewAttrItem; + } + else + { + flmAssert( 0); + } + + if( pOldAttrItem->m_uiPayloadLen > sizeof( FLMBYTE *)) + { + *((F_AttrItem **)(pNewAttrItem->m_pucPayload - sizeof( F_AttrItem *))) = + pNewAttrItem; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_AttrBufferRelocator::canRelocate( + void * pvAlloc) +{ + F_AttrItem * pAttrItem = *((F_AttrItem **)pvAlloc); + + flmAssert( pAttrItem->m_pucPayload == + (FLMBYTE *)pvAlloc + sizeof( F_AttrItem *)); + + if( pAttrItem->m_pCachedNode && !pAttrItem->m_pCachedNode->nodeInUse()) + { + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_AttrBufferRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_AttrItem * pAttrItem = *((F_AttrItem **)pvOldAlloc); + + flmAssert( !pAttrItem->m_pCachedNode->nodeInUse()); + flmAssert( pvNewAlloc < pvOldAlloc); + flmAssert( pAttrItem->m_pucPayload == + (FLMBYTE *)pvOldAlloc + sizeof( F_AttrItem *)); + + pAttrItem->m_pucPayload = + ((FLMBYTE *)pvNewAlloc) + sizeof( F_AttrItem *); +} + +/**************************************************************************** +Desc: This routine resizes the hash table for the cache manager. + NOTE: This routine assumes that the node cache mutex has been locked. +****************************************************************************/ +RCODE F_NodeCacheMgr::rehash( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNewHashTblSize; + F_CachedNode ** ppOldHashTbl; + FLMUINT uiOldHashTblSize; + F_CachedNode ** ppBucket; + FLMUINT uiLoop; + F_CachedNode * pTmpNode; + F_CachedNode * pTmpNextNode; + FLMUINT uiOldMemSize; + + uiNewHashTblSize = caGetBestHashTblSize( m_Usage.uiCount); + + // At this point we better have a different hash table size + // or something is mucked up! + + flmAssert( uiNewHashTblSize != m_uiNumBuckets); + + // Save the old hash table and its size. + + if ((ppOldHashTbl = m_ppHashBuckets) != NULL) + { + uiOldMemSize = f_msize( ppOldHashTbl); + } + else + { + uiOldMemSize = 0; + } + uiOldHashTblSize = m_uiNumBuckets; + + // Allocate a new hash table. + + if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedNode *) * + (FLMUINT)uiNewHashTblSize, &m_ppHashBuckets))) + { + m_uiHashFailTime = FLM_GET_TIMER(); + m_ppHashBuckets = ppOldHashTbl; + goto Exit; + } + + // Subtract off old size and add in new size. + + gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiOldMemSize); + gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); + + m_uiNumBuckets = uiNewHashTblSize; + m_uiHashMask = uiNewHashTblSize - 1; + + // Relink all of the nodes into the new hash table. + + for (uiLoop = 0, ppBucket = ppOldHashTbl; + uiLoop < uiOldHashTblSize; + uiLoop++, ppBucket++) + { + pTmpNode = *ppBucket; + while (pTmpNode) + { + pTmpNextNode = pTmpNode->m_pNextInBucket; + pTmpNode->linkToHashBucket(); + pTmpNode = pTmpNextNode; + } + } + + // Throw away the old hash table. + + f_free( &ppOldHashTbl); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine shuts down the node cache manager and frees all + resources allocated by it. NOTE: Node cache mutex must be locked + already, or we must be shutting down so that only one thread is + calling this routine. +****************************************************************************/ +F_NodeCacheMgr::~F_NodeCacheMgr() +{ + F_CachedItem * pItem; + F_CachedItem * pNextItem; + F_DOMNode * pTmp; + + // Free the DOM Node Pool + + while( (pTmp = m_pFirstNode) != NULL) + { + m_pFirstNode = m_pFirstNode->m_pNextInPool; + pTmp->m_ui32RefCnt = 0; + pTmp->m_pNextInPool = NULL; + pTmp->m_pCachedNode = NULL; + delete pTmp; + } + + // Free all of the node cache objects. + + pItem = m_MRUList.m_pMRUItem; + while (pItem) + { + pNextItem = pItem->m_pNextInGlobal; + ((F_CachedNode *)pItem)->freeCache( FALSE); + pItem = pNextItem; + } + flmAssert( !m_MRUList.m_pMRUItem && !m_MRUList.m_pLRUItem); + + // Must free those in the purge list too. + + while (m_pPurgeList) + { + m_pPurgeList->freePurged(); + } + + // The math better be consistent! + + flmAssert( m_Usage.uiCount == 0); + flmAssert( m_Usage.uiOldVerCount == 0); + flmAssert( m_Usage.uiOldVerBytes == 0); + + // Free the hash bucket array + + if (m_ppHashBuckets) + { + FLMUINT uiTotalMemory = f_msize( m_ppHashBuckets); + + f_free( &m_ppHashBuckets); + gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiTotalMemory); + } +} + +/**************************************************************************** +Desc: This routine links a notify request into a node's notification list and + then waits to be notified that the event has occurred. + NOTE: This routine assumes that the node cache mutex is locked and that + it is supposed to unlock it. It will relock the mutex on its way out. +****************************************************************************/ +RCODE F_NodeCacheMgr::waitNotify( + F_Db * pDb, + F_CachedNode ** ppNode) +{ + return( flmWaitNotifyReq( gv_XFlmSysData.hNodeCacheMutex, + pDb->m_hWaitSem, &((*ppNode)->m_pNotifyList), ppNode)); +} + +/**************************************************************************** +Desc: This routine notifies threads waiting for a pending read to complete. + NOTE: This routine assumes that the node cache mutex is already locked. +****************************************************************************/ +void F_NodeCacheMgr::notifyWaiters( + FNOTIFY * pNotify, + F_CachedNode * pUseNode, + RCODE NotifyRc) +{ + while (pNotify) + { + F_SEM hSem; + + *(pNotify->pRc) = NotifyRc; + if (RC_OK( NotifyRc)) + { + *((F_CachedNode **)pNotify->pvUserData) = pUseNode; + pUseNode->incrNodeUseCount(); + } + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } +} + +/**************************************************************************** +Desc: Allocate an F_CachedNode object. +****************************************************************************/ +RCODE F_NodeCacheMgr::allocNode( + F_CachedNode ** ppNode, + FLMBOOL bMutexLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bUnlockMutex = FALSE; + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bUnlockMutex = TRUE; + } + + if ((*ppNode = new F_CachedNode) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Increment statistics. + + m_Usage.uiCount++; + m_Usage.uiByteCount += (*ppNode)->memSize(); + if (shouldRehash( m_Usage.uiCount, m_uiNumBuckets)) + { + if (checkHashFailTime( &m_uiHashFailTime)) + { + if (RC_BAD( rc = rehash())) + { + goto Exit; + } + } + } + +Exit: + + if( bUnlockMutex) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Cleanup old nodes in cache that are no longer needed by any + transaction. This routine assumes that the node cache mutex + has been locked. +****************************************************************************/ +void F_NodeCacheMgr::cleanupOldCache( void) +{ + F_CachedNode * pCurNode; + F_CachedNode * pNextNode; + + pCurNode = m_pOldList; + + // Stay in the loop until we have freed all old nodes, or + // we have run through the entire list. + + while( pCurNode) + { + flmAssert( pCurNode->m_ui64HighTransId != FLM_MAX_UINT64); + + // Save the pointer to the next entry in the list because + // we may end up unlinking pCurNode below, in which case we would + // have lost the next node. + + pNextNode = pCurNode->m_pNextInOldList; + if (!pCurNode->nodeInUse() && + !pCurNode->readingInNode() && + (!pCurNode->nodeLinkedToDatabase() || + !pCurNode->m_pDatabase->neededByReadTrans( + pCurNode->m_ui64LowTransId, pCurNode->m_ui64HighTransId))) + { + pCurNode->freeNode(); + } + pCurNode = pNextNode; + } +} + +/**************************************************************************** +Desc: Cleanup nodes that have been purged. This routine assumes that the + node cache mutex has been locked. +****************************************************************************/ +void F_NodeCacheMgr::cleanupPurgedCache( void) +{ + F_CachedNode * pCurNode; + F_CachedNode * pNextNode; + + pCurNode = m_pPurgeList; + + // Stay in the loop until we have freed all purged nodes, or + // we have run through the entire list. + + while( pCurNode) + { + // Save the pointer to the next entry in the list because + // we may end up unlinking pCurNode below, in which case we would + // have lost the next node. + + pNextNode = (F_CachedNode *)pCurNode->m_pNextInGlobal; + flmAssert( pCurNode->nodePurged()); + + if (!pCurNode->nodeInUse()) + { + pCurNode->freePurged(); + } + pCurNode = pNextNode; + } +} + +/**************************************************************************** +Desc: Reduce node cache to below the cache limit. NOTE: This routine assumes + that the node cache mutex is locked upon entering the routine, but + it may unlock and re-lock the mutex. +****************************************************************************/ +void F_NodeCacheMgr::reduceCache( void) +{ + F_CachedNode * pTmpNode; + F_CachedNode * pPrevNode; + F_CachedNode * pNextNode; + FLMUINT uiSlabSize; + FLMUINT uiByteThreshold; + FLMUINT uiSlabThreshold; + FLMBOOL bDoingReduce = FALSE; + + // Discard items that are allocated on the heap. These are large + // allocations that could not be satisfied by the buffer allocator and have + // the side effect of causing memory fragmentation. + + pTmpNode = m_pHeapList; + while( pTmpNode) + { + // Need to save the pointer to the next entry in the list because + // we may end up freeing pTmpNode below. + + pNextNode = pTmpNode->m_pNextInHeapList; + + // See if the item can be freed. + + if( pTmpNode->canBeFreed()) + { + // NOTE: This call will free the memory pointed to by + // pTmpNode. Hence, pTmpNode should NOT be used after + // this point. + + pTmpNode->freeNode(); + } + + pTmpNode = pNextNode; + } + + // If cache is not full, we are done. + + if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || m_bReduceInProgress) + { + goto Exit; + } + + m_bReduceInProgress = TRUE; + bDoingReduce = TRUE; + + // Cleanup cache that is no longer needed by anyone + + cleanupOldCache(); + cleanupPurgedCache(); + + // Determine the cache threshold + + uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; + uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); + + // Are we over the threshold? + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) + { + goto Exit; + } + + // Remove items from cache starting from the LRU + + pTmpNode = (F_CachedNode *)m_MRUList.m_pLRUItem; + uiByteThreshold = m_Usage.uiByteCount > uiSlabSize + ? m_Usage.uiByteCount - uiSlabSize + : 0; + + while( pTmpNode) + { + // Need to save the pointer to the next entry in the list because + // we may end up freeing pTmpNode below. + + pPrevNode = (F_CachedNode *)pTmpNode->m_pPrevInGlobal; + + // See if the item can be freed. + + if( pTmpNode->canBeFreed()) + { + pTmpNode->freeNode(); + + if( m_Usage.uiByteCount <= uiByteThreshold) + { + if( pPrevNode) + { + pPrevNode->incrNodeUseCount(); + } + + gv_XFlmSysData.pNodeCacheMgr->defragmentMemory( TRUE); + + if( !pPrevNode) + { + break; + } + + pPrevNode->decrNodeUseCount(); + + // We're going to quit when we get under 50 percent for node cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving node + // cache because block cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + + uiByteThreshold = uiByteThreshold > uiSlabSize + ? uiByteThreshold - uiSlabSize + : 0; + } + } + + pTmpNode = pPrevNode; + } + +Exit: + + if( bDoingReduce) + { + m_bReduceInProgress = FALSE; + } + + return; +} + +/**************************************************************************** +Desc: This routine finds a node in the node cache. If it cannot + find the node, it will return the position where the node should + be inserted. + NOTE: This routine assumes that the node cache mutex has been locked. +****************************************************************************/ +void F_NodeCacheMgr::findNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64VersionNeeded, + FLMBOOL bDontPoisonCache, + FLMUINT * puiNumLooks, + F_CachedNode ** ppNode, + F_CachedNode ** ppNewerNode, + F_CachedNode ** ppOlderNode) +{ + F_CachedNode * pNode; + FLMUINT uiNumLooks = 0; + FLMBOOL bFound; + F_CachedNode * pNewerNode; + F_CachedNode * pOlderNode; + F_Database * pDatabase = pDb->m_pDatabase; + + // Search down the hash bucket for the matching item. + +Start_Find: + + // NOTE: Need to always calculate hash bucket because + // the hash table may have been changed while we + // were waiting to be notified below - mutex can + // be unlocked, but it is guaranteed to be locked + // here. + + pNode = *(nodeHash( ui64NodeId)); + bFound = FALSE; + uiNumLooks = 1; + while (pNode && + (pNode->m_nodeInfo.ui64NodeId != ui64NodeId || + pNode->m_nodeInfo.uiCollection != uiCollection || + pNode->m_pDatabase != pDatabase)) + { + if ((pNode = pNode->m_pNextInBucket) != NULL) + { + uiNumLooks++; + } + } + + // If we found the node, see if we have the right version. + + if (!pNode) + { + pNewerNode = pOlderNode = NULL; + } + else + { + pNewerNode = NULL; + pOlderNode = pNode; + for (;;) + { + + // If this one is being read in, we need to wait on it. + + if (pNode->readingInNode()) + { + // We need to wait for this record to be read in + // in case it coalesces with other versions, resulting + // in a version that satisfies our request. + + m_uiIoWaits++; + if (RC_BAD( waitNotify( pDb, &pNode))) + { + + // Don't care what error the other thread had reading + // the thing in from disk - we'll bail out and start + // our find again. + + goto Start_Find; + } + + // The thread doing the notify "uses" the record cache + // on behalf of this thread to prevent the record + // from being replaced after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the record - because we + // will put a "use" on it below. + + pNode->decrNodeUseCount(); + + if (pNode->nodePurged()) + { + if (!pNode->nodeInUse()) + { + pNode->freePurged(); + } + } + + // Start over with the find because the list + // structure has changed. + + goto Start_Find; + } + + // See if this record version is the one we need. + + if (ui64VersionNeeded < pNode->m_ui64LowTransId) + { + pNewerNode = pNode; + if ((pOlderNode = pNode = pNode->m_pOlderVersion) == NULL) + { + break; + } + uiNumLooks++; + } + else if (ui64VersionNeeded <= pNode->m_ui64HighTransId) + { + + // Make this the MRU record. + + if (puiNumLooks) + { + if (bDontPoisonCache) + { + m_MRUList.stepUpInGlobal( (F_CachedItem *)pNode); + } + else if (pNode->m_pPrevInGlobal) + { + m_MRUList.unlinkGlobal( (F_CachedItem *)pNode); + m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode); + } + m_Usage.uiCacheHits++; + m_Usage.uiCacheHitLooks += uiNumLooks; + } + bFound = TRUE; + break; + } + else + { + pOlderNode = pNode; + pNewerNode = pNode->m_pNewerVersion; + + // Set pNode to NULL as an indicator that we did not + // find the version we needed. + + pNode = NULL; + break; + } + } + } + + *ppNode = pNode; + + if( ppOlderNode) + { + *ppOlderNode = pOlderNode; + } + + if( ppNewerNode) + { + *ppNewerNode = pNewerNode; + } + + if (puiNumLooks) + { + *puiNumLooks = uiNumLooks; + } +} + +/**************************************************************************** +Desc: This routine links a new node into the global list and + into the correct place in its hash bucket. This routine assumes that + the node cache mutex is already locked. +****************************************************************************/ +void F_NodeCacheMgr::linkIntoNodeCache( + F_CachedNode * pNewerNode, + F_CachedNode * pOlderNode, + F_CachedNode * pNode, + FLMBOOL bLinkAsMRU + ) +{ + if( bLinkAsMRU) + { + m_MRUList.linkGlobalAsMRU( (F_CachedItem *)pNode); + } + else + { + m_MRUList.linkGlobalAsLRU( (F_CachedItem *)pNode); + } + + if (pNewerNode) + { + pNode->linkToVerList( pNewerNode, pOlderNode); + } + else + { + if (pOlderNode) + { + pOlderNode->unlinkFromHashBucket(); + } + pNode->linkToHashBucket(); + pNode->linkToVerList( NULL, pOlderNode); + } +} + +/**************************************************************************** +Desc: This routine links a new node to its F_Database according to whether + or not it is an update transaction or a read transaction. + It coalesces out any unnecessary versions. This routine assumes + that the node cache mutex is already locked. +****************************************************************************/ +void F_CachedNode::linkToDatabase( + F_Database * pDatabase, + F_Db * pDb, + FLMUINT64 ui64LowTransId, + FLMBOOL bMostCurrent) +{ + F_CachedNode * pTmpNode; + + m_ui64LowTransId = ui64LowTransId; + + // Before coalescing, link to F_Database. + // The following test determines if the node is an + // uncommitted version generated by the update transaction. + // If so, we mark it as such, and link it at the head of the + // F_Database list - so we can get rid of it quickly if we abort + // the transaction. + + if (pDb->getTransType() == XFLM_UPDATE_TRANS) + { + + // If we are in an update transaction, there better not + // be any newer versions in the list and the high + // transaction ID returned better be FLM_MAX_UINT64. + + flmAssert( m_pNewerVersion == NULL); + setTransID( FLM_MAX_UINT64); + + // If the low transaction ID is the same as the transaction, + // we may have modified this node during the transaction. + // Unfortunately, there is no sure way to tell, so we are + // forced to assume it may have been modified. If the + // transaction aborts, we will get rid if this version out + // of cache. + + if (ui64LowTransId == pDb->getTransID()) + { + setUncommitted(); + linkToDatabaseAtHead( pDatabase); + } + else + { + unsetUncommitted(); + linkToDatabaseAtEnd( pDatabase); + } + } + else + { + FLMUINT64 ui64HighTransId; + + // Adjust the high transaction ID to be the same as + // the transaction ID - we may have gotten a FLM_MAX_UINT64 + // back, but that is possible even if the node is + // not the most current version. Besides that, it is + // possible that in the mean time one or more update + // transactions have come along and created one or + // more newer versions of the node. + + if (bMostCurrent) + { + // This may be showing up as most current simply because we have + // a newer node that was dirty - meaning it would not have been + // written to block cache yet - so our read operation would have + // read the "most current" version of the block that contains + // this node - but it isn't really the most current version of + // the node. + + if (m_pNewerVersion && !m_pNewerVersion->readingInNode()) + { + ui64HighTransId = m_pNewerVersion->getLowTransId() - 1; + } + else + { + ui64HighTransId = FLM_MAX_UINT64; + } + } + else + { + ui64HighTransId = pDb->getTransID(); + } + + setTransID( ui64HighTransId); + + // For a read transaction, if there is a newer version, + // it better have a higher "low transaction ID" + +#ifdef FLM_DEBUG + if (m_pNewerVersion && !m_pNewerVersion->readingInNode()) + { + flmAssert( m_ui64HighTransId < m_pNewerVersion->m_ui64LowTransId); + if( m_ui64HighTransId >= m_pNewerVersion->m_ui64LowTransId) + { + checkReadFromDisk( pDb); + } + } +#endif + unsetUncommitted(); + linkToDatabaseAtEnd( pDatabase); + } + + // Coalesce any versions that overlap - can only + // coalesce older versions. For an updater, there + // should not be any newer versions. For a reader, it + // is impossible to know how high up it can coalesce. + // The read operation that read the node may have + // gotten back a FLM_MAX_UINT64 for its high transaction + // ID - but after that point in time, it is possible + // that one or more update transactions may have come + // along and created one or more newer versions that + // it would be incorrect to coalesce with. + // In reality, a read transaction has to ignore the + // FLM_MAX_UINT64 in the high transaction ID anyway + // because there is no way to know if it is correct. + + // Coalesce older versions. + + for (;;) + { + if ((pTmpNode = m_pOlderVersion) == NULL) + { + break; + } + + // Stop if we encounter one that is being read in. + + if (pTmpNode->readingInNode()) + { + break; + } + + // If there is no overlap between these two, there is + // nothing more to coalesce. + + if (m_ui64LowTransId > pTmpNode->m_ui64HighTransId) + { + break; + } + + if (m_ui64HighTransId <= pTmpNode->m_ui64HighTransId) + { + // This assert represents the following case, + // which should not be possible to hit: + + // pOlder->m_ui64HighTransId > m_ui64HighTransId. + // This cannot be, because if pOlder has a higher + // transaction ID, we would have found it up above and + // not tried to have read it in. + + flmAssert( 0); +#ifdef FLM_DEBUG + checkReadFromDisk( pDb); +#endif + } + else if (m_ui64LowTransId >= pTmpNode->m_ui64LowTransId) + { + m_ui64LowTransId = pTmpNode->m_ui64LowTransId; + pTmpNode->freeCache( + (FLMBOOL)((pTmpNode->nodeInUse() || + pTmpNode->readingInNode()) + ? TRUE + : FALSE)); + } + else + { + // This assert represents the following case, + // which should not be possible to hit: + + // m_ui64LowTransId < pOlder->m_ui64LowTransId. + // This cannot be, because pOlder has to have been read + // in to memory by a transaction whose transaction ID is + // less than or equal to our own. That being the case, + // it would be impossible for our transaction to have + // found a version of the node that is older than pOlder. + + flmAssert( 0); +#ifdef FLM_DEBUG + checkReadFromDisk( pDb); +#endif + } + } +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::resizeDataBuffer( + FLMUINT uiSize, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOldSize; + FLMUINT uiNewSize; + FLMUINT uiDataBufSize = calcDataBufSize( uiSize); + FLMBYTE * pucActualAlloc; + FLMBOOL bHeapAlloc = FALSE; + void * pvThis = this; + FLMBOOL bLockedMutex = FALSE; + + flmAssert( !m_uiDataBufSize || m_pucData); + flmAssert( !m_uiStreamUseCount); + + if( uiDataBufSize == m_uiDataBufSize) + { + goto Exit; + } + + if( !bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bLockedMutex = TRUE; + } + + uiOldSize = memSize(); + + if (!m_pucData) + { + pucActualAlloc = NULL; + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator, + uiDataBufSize, &pvThis, sizeof( void *), + &pucActualAlloc, &bHeapAlloc))) + { + goto Exit; + } + } + else + { + pucActualAlloc = getActualPointer( m_pucData); + if( RC_BAD( rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_nodeDataRelocator, + m_uiDataBufSize, uiDataBufSize, &pvThis, sizeof( void *), + &pucActualAlloc, &bHeapAlloc))) + { + goto Exit; + } + } + + flmAssert( *((F_CachedNode **)pucActualAlloc) == this); + setNodeAndDataPtr( pucActualAlloc); + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_uiDataBufSize = uiDataBufSize; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + uiNewSize = memSize(); + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; + } + + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; + + if( bHeapAlloc) + { + linkToHeapList(); + } + else if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + +Exit: + + if( bLockedMutex) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + flmAssert( !m_uiDataBufSize || m_pucData); + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::resizeChildElmList( + FLMUINT uiChildElmCount, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOldSize; + FLMBYTE * pucActualAlloc; + FLMBOOL bHeapAlloc = FALSE; + void * pvThis = this; +#ifdef FLM_CACHE_PROTECT + FLMBOOL bProtectNode = FALSE; +#endif + + if( uiChildElmCount == m_nodeInfo.uiChildElmCount) + { + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); + bProtectNode = TRUE; +#endif + + if( !bMutexAlreadyLocked) + { + flmAssert( nodeInUse()); + } + + uiOldSize = memSize(); + + if( !uiChildElmCount) + { + // The only thing we better be doing if we pass in a zero, is + // reducing the number of child elements. Hence, the current + // child element count better be non-zero. + + flmAssert( m_nodeInfo.uiChildElmCount); + + pucActualAlloc = getActualPointer( m_pNodeList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcNodeListBufSize( m_nodeInfo.uiChildElmCount), + &pucActualAlloc); + } + else + { + if( !m_nodeInfo.uiChildElmCount) + { + pucActualAlloc = NULL; + rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator, + calcNodeListBufSize( uiChildElmCount), + &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); + } + else + { + pucActualAlloc = getActualPointer( m_pNodeList); + rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_nodeListRelocator, + calcNodeListBufSize( m_nodeInfo.uiChildElmCount), + calcNodeListBufSize( uiChildElmCount), + &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); + } + + flmAssert( *((F_CachedNode **)pucActualAlloc) == this); + } + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + + if (RC_OK( rc)) + { + FLMUINT uiNewSize; + + m_nodeInfo.uiChildElmCount = uiChildElmCount; + if (m_nodeInfo.uiChildElmCount) + { + setNodeListPtr( pucActualAlloc); + } + else + { + m_pNodeList = NULL; + } + + uiNewSize = memSize(); + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; + } + + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; + + if( bHeapAlloc) + { + linkToHeapList(); + } + else if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + } + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + +Exit: + +#ifdef FLM_CACHE_PROTECT + if( bProtectNode) + { + protectCachedItem(); + } +#endif + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::resizeAttrList( + FLMUINT uiAttrCount, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOldSize; + FLMBYTE * pucActualAlloc; + FLMBOOL bHeapAlloc = FALSE; + void * pvThis = this; +#ifdef FLM_CACHE_PROTECT + FLMBOOL bProtectNode = FALSE; +#endif + + if( uiAttrCount == m_uiAttrCount) + { + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); + bProtectNode = TRUE; +#endif + + if( !bMutexAlreadyLocked) + { + flmAssert( nodeInUse()); + } + + uiOldSize = memSize(); + + if( !uiAttrCount) + { + // The only thing we better be doing if we pass in a zero, is + // reducing the number of attributes. Hence, the current + // attribute count better be non-zero. + + flmAssert( m_uiAttrCount); + pucActualAlloc = getActualPointer( m_ppAttrList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcAttrListBufSize( m_uiAttrCount), + &pucActualAlloc); + } + else + { + if( !m_uiAttrCount) + { + pucActualAlloc = NULL; + rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.allocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator, + calcAttrListBufSize( uiAttrCount), + &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); + } + else + { + pucActualAlloc = getActualPointer( m_ppAttrList); + rc = gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.reallocBuf( + &gv_XFlmSysData.pNodeCacheMgr->m_attrListRelocator, + calcAttrListBufSize( m_uiAttrCount), + calcAttrListBufSize( uiAttrCount), + &pvThis, sizeof( void *), &pucActualAlloc, &bHeapAlloc); + } + + flmAssert( *((F_CachedNode **)pucActualAlloc) == this); + } + + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + + if (RC_OK( rc)) + { + FLMUINT uiNewSize; + + m_uiAttrCount = uiAttrCount; + if (m_uiAttrCount) + { + setAttrListPtr( pucActualAlloc); + } + else + { + m_ppAttrList = NULL; + } + + uiNewSize = memSize(); + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiNewSize; + } + + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiOldSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiOldSize; + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiNewSize; + + if( bHeapAlloc) + { + linkToHeapList(); + } + else if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + } + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + +Exit: + +#ifdef FLM_CACHE_PROTECT + if( bProtectNode) + { + protectCachedItem(); + } +#endif + + return( rc); +} + +/**************************************************************************** +Desc: This routine retrieves a node from disk. +****************************************************************************/ +RCODE F_NodeCacheMgr::readNodeFromDisk( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_CachedNode * pNode, + FLMUINT64 * pui64LowTransId, + FLMBOOL * pbMostCurrent) +{ + RCODE rc = NE_XFLM_OK; + F_Btree * pBTree = NULL; + FLMBOOL bCloseIStream = FALSE; + F_BTreeIStream btreeIStream; + + if( RC_BAD( rc = pDb->getCachedBTree( uiCollection, &pBTree))) + { + goto Exit; + } + if (RC_BAD( rc = btreeIStream.open( pDb, pBTree, XFLM_EXACT, + uiCollection, ui64NodeId, 0, 0))) + { + goto Exit; + } + bCloseIStream = TRUE; + + // Read the node from the B-Tree + + if (RC_BAD( rc = pNode->readNode( pDb, uiCollection, + ui64NodeId, &btreeIStream, (FLMUINT)btreeIStream.remainingSize(), + NULL))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + pNode->m_uiOffsetIndex = btreeIStream.getOffsetIndex(); + pNode->m_ui32BlkAddr = btreeIStream.getBlkAddr(); + + pBTree->btGetTransInfo( pui64LowTransId, pbMostCurrent); + +Exit: + + if (bCloseIStream) + { + btreeIStream.close(); + } + + if (pBTree) + { + pBTree->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine retrieves a node from the node cache. +****************************************************************************/ +RCODE F_NodeCacheMgr::retrieveNode( + F_Db * pDb, + FLMUINT uiCollection, // Collection node is in. + FLMUINT64 ui64NodeId, // Node ID + F_DOMNode ** ppDOMNode + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + F_Database * pDatabase = pDb->m_pDatabase; + F_CachedNode * pNode; + F_CachedNode * pNewerNode; + F_CachedNode * pOlderNode; + FLMUINT64 ui64LowTransId; + FLMBOOL bMostCurrent; + FLMUINT64 ui64CurrTransId; + FNOTIFY * pNotify; + FLMUINT uiNumLooks; + FLMBOOL bDontPoisonCache = pDb->m_uiFlags & FDB_DONT_POISON_CACHE + ? TRUE + : FALSE; + + if (RC_BAD( rc = pDb->checkState( __FILE__, __LINE__))) + { + goto Exit; + } + + // Get the current transaction ID + + flmAssert( pDb->m_eTransType != XFLM_NO_TRANS); + ui64CurrTransId = pDb->getTransID(); + flmAssert( ui64NodeId != 0); + + // Lock the node cache mutex + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + + // Reset the DB's inactive time + + pDb->m_uiInactiveTime = 0; + +Start_Find: + + findNode( pDb, uiCollection, ui64NodeId, + ui64CurrTransId, bDontPoisonCache, + &uiNumLooks, &pNode, + &pNewerNode, &pOlderNode); + + if (pNode) + { + // Have the DOM Node point to the node we found + goto Exit1; + } + + // Did not find the node, fetch from disk + // Increment the number of faults only if we retrieve the record from disk. + + m_Usage.uiCacheFaults++; + m_Usage.uiCacheFaultLooks += uiNumLooks; + + // Create a place holder for the object. + + if (RC_BAD( rc = allocNode( &pNode, TRUE))) + { + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + pNode->unprotectCachedItem(); +#endif + + pNode->m_nodeInfo.ui64NodeId = ui64NodeId; + pNode->m_nodeInfo.uiCollection = uiCollection; + + // Set the F_Database so that other threads looking for this node in + // cache will find it and wait until the read has completed. If + // the F_Database is not set, other threads will attempt their own read, + // because they won't match a NULL F_Database. The result of not setting + // the F_Database is that multiple copies of the same version of a particular + // node could end up in cache. + + pNode->m_pDatabase = pDatabase; + + linkIntoNodeCache( pNewerNode, pOlderNode, pNode, !bDontPoisonCache); + + pNode->setReadingIn(); + pNode->incrNodeUseCount(); + pNode->m_pNotifyList = NULL; + + // Unlock mutex before reading in from disk. + + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = FALSE; + + // Read node from disk. + + rc = readNodeFromDisk( pDb, uiCollection, ui64NodeId, pNode, + &ui64LowTransId, &bMostCurrent); + + // Relock mutex + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + + // If read was successful, link the node to its place in + // the F_Database list and coalesce any versions that overlap + // this one. + + if (RC_OK( rc)) + { + pNode->linkToDatabase( + pDb->m_pDatabase, pDb, ui64LowTransId, bMostCurrent); + } + + pNode->unsetReadingIn(); + + // Notify any threads waiting for the read to complete. + + pNotify = pNode->m_pNotifyList; + pNode->m_pNotifyList = NULL; + if (pNotify) + { + notifyWaiters( pNotify, + (F_CachedNode *)((RC_BAD( rc)) + ? (F_CachedNode *)NULL + : pNode), rc); + } + pNode->decrNodeUseCount(); +#ifdef FLM_CACHE_PROTECT + pNode->protectCachedItem(); +#endif + + // If we did not succeed, free the F_CachedNode structure. + + if (RC_BAD( rc)) + { + pNode->freeCache( FALSE); + goto Exit; + } + + // If this item was purged while we were reading it in, + // start over with the search. + + if (pNode->nodePurged()) + { + if (!pNode->nodeInUse()) + { + pNode->freePurged(); + } + + // Start over with the find - this one has + // been marked for purging. + + goto Start_Find; + } + +Exit1: + + // Have the DOM Node point to the node we read in from disk + + if( *ppDOMNode == NULL) + { + if( RC_BAD( rc = allocDOMNode( ppDOMNode))) + { + goto Exit; + } + } + + if ( (*ppDOMNode)->m_pCachedNode) + { + (*ppDOMNode)->m_pCachedNode->decrNodeUseCount(); + } + (*ppDOMNode)->m_pCachedNode = pNode; + pNode->incrNodeUseCount(); + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine creates a node into the node cache. This is ONLY + called when a new node is being created. +****************************************************************************/ +RCODE F_NodeCacheMgr::createNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + F_DOMNode ** ppDOMNode) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->m_pDatabase; + F_COLLECTION * pCollection; + F_CachedNode * pNode = NULL; + F_CachedNode * pNewerNode = NULL; + F_CachedNode * pOlderNode = NULL; + FLMBOOL bMutexLocked = FALSE; + + // A zero ui64NodeId means we are to use the next node ID for the + // collection. + + if( !ui64NodeId) + { + if (RC_BAD( rc = pDb->m_pDict->getCollection( uiCollection, &pCollection))) + { + goto Exit; + } + + ui64NodeId = pCollection->ui64NextNodeId; + + // Lock the node cache mutex + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + } + else + { + // Lock the node cache mutex + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + + // See if we can find the node in cache + + findNode( pDb, uiCollection, ui64NodeId, + pDb->m_ui64CurrTransID, TRUE, NULL, &pNode, + &pNewerNode, &pOlderNode); + + if (pNode) + { + // If we found the last committed version, instead of replacing it, + // we want to change its high transaction ID, and go create a new + // node to put in cache. + + if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID) + { + + // pOlderNode and pNode should be the same at this point if we + // found something. Furthermore, the high transaction ID on what + // we found better be -1 - most current version. + + flmAssert( pOlderNode == pNode); + flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64); + + pOlderNode->setTransID( (pDb->m_ui64CurrTransID - 1)); + + flmAssert( pOlderNode->m_ui64HighTransId >= + pOlderNode->m_ui64LowTransId); + + pOlderNode->setUncommitted(); + pOlderNode->setLatestVer(); + pOlderNode->unlinkFromDatabase(); + pOlderNode->linkToDatabaseAtHead( pDatabase); + } + else + { + // Found latest UNCOMMITTED VERSION + + pNode = NULL; + rc = RC_SET_AND_ASSERT( NE_XFLM_EXISTS); + goto Exit; + } + } + } + + // We are positioned to insert the new node. For an update, it + // must always be the newest version. + + flmAssert( !pNewerNode); + + // Create a new object. + + if (RC_BAD( rc = allocNode( &pNode, bMutexLocked))) + { + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + pNode->unprotectCachedItem(); +#endif + + pNode->m_nodeInfo.ui64NodeId = ui64NodeId; + pNode->m_nodeInfo.uiCollection = uiCollection; + pNode->m_uiOffsetIndex = 0; + pNode->m_ui32BlkAddr = 0; + + // NOTE: Not everything is initialized in pNode at this point, but + // no other thread should be accessing it anyway. The caller of this + // function must ensure that all of the necessary items get set before + // releasing the node. + + // Set the F_Database so that other threads looking for this node in + // cache will find it and wait until the read has completed. If + // the F_Database is not set, other threads will attempt their own read, + // because they won't match a NULL F_Database. The result of not setting + // the F_Database is that multiple copies of the same version of a particular + // node could end up in cache. + + pNode->m_pDatabase = pDatabase; + + linkIntoNodeCache( pNewerNode, pOlderNode, pNode, TRUE); + + // Link the node to its place in the F_Database list + + pNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE); + + // Have the DOM node point to the node we created + + flmAssert( *ppDOMNode == NULL); + + if( RC_BAD( rc = allocDOMNode( ppDOMNode))) + { + goto Exit; + } + + if ( (*ppDOMNode)->m_pCachedNode) + { + (*ppDOMNode)->m_pCachedNode->decrNodeUseCount(); + } + (*ppDOMNode)->m_pCachedNode = pNode; + pNode->incrNodeUseCount(); + +#ifdef FLM_CACHE_PROTECT + pNode->protectCachedItem(); +#endif + +Exit: + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine makes a writeable copy of the node pointed to by F_DOMNode. +****************************************************************************/ +RCODE F_NodeCacheMgr::_makeWriteCopy( + F_Db * pDb, + F_CachedNode ** ppCachedNode) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->m_pDatabase; + F_CachedNode * pNewerNode = NULL; + F_CachedNode * pOlderNode = *ppCachedNode; + FLMBOOL bMutexLocked = FALSE; +#ifdef FLM_CACHE_PROTECT + FLMBOOL bProtectNewerNode = FALSE; +#endif + + flmAssert( pOlderNode->m_ui64HighTransId == FLM_MAX_UINT64); + flmAssert( !pOlderNode->m_pNewerVersion); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + bMutexLocked = TRUE; + + // Create a new object. + + if (RC_BAD( rc = allocNode( &pNewerNode, TRUE))) + { + goto Exit; + } + + // If we found the last committed version, instead of replacing it, + // we want to change its high transaction ID, and go create a new + // node to put in cache. + + // Although this routine could be written to not do anything if we + // are already on the uncommitted version of the node, for performance + // reasons, we would prefer that they make the check on the outside + // before calling this routine. + + flmAssert( pOlderNode->m_ui64LowTransId < pDb->m_ui64CurrTransID); + pOlderNode->setTransID( pDb->m_ui64CurrTransID - 1); + flmAssert( pOlderNode->m_ui64HighTransId >= pOlderNode->m_ui64LowTransId); + + pOlderNode->setUncommitted(); + pOlderNode->setLatestVer(); + pOlderNode->unlinkFromDatabase(); + pOlderNode->linkToDatabaseAtHead( pDatabase); + +#ifdef FLM_CACHE_PROTECT + pNewerNode->unprotectCachedItem(); + bProtectNewerNode = TRUE; +#endif + + pNewerNode->m_pDatabase = pDatabase; + pNewerNode->m_uiFlags = pOlderNode->m_uiFlags; + pNewerNode->m_uiOffsetIndex = pOlderNode->m_uiOffsetIndex; + pNewerNode->m_ui32BlkAddr = pOlderNode->m_ui32BlkAddr; + + if( pNewerNode->m_uiFlags & FDOM_HEAP_ALLOC) + { + pNewerNode->m_uiFlags &= ~FDOM_HEAP_ALLOC; + } + + f_memcpy( &pNewerNode->m_nodeInfo, + &pOlderNode->m_nodeInfo, sizeof( F_NODE_INFO)); + + if (pNewerNode->m_uiFlags & (FDOM_SIGNED_QUICK_VAL | FDOM_UNSIGNED_QUICK_VAL)) + { + pNewerNode->m_numberVal = pOlderNode->m_numberVal; + } + + if( pNewerNode->m_uiFlags & FDOM_HAVE_CELM_LIST) + { + + // Need to set to zero, because we really haven't allocated + // space for it yet. + + pNewerNode->m_nodeInfo.uiChildElmCount = 0; + + if( pOlderNode->m_nodeInfo.uiChildElmCount) + { + if( RC_BAD( rc = pNewerNode->resizeChildElmList( + pOlderNode->m_nodeInfo.uiChildElmCount, TRUE))) + { + goto Exit; + } + + f_memcpy( pNewerNode->m_pNodeList, pOlderNode->m_pNodeList, + sizeof( NODE_ITEM) * pNewerNode->m_nodeInfo.uiChildElmCount); + } + } + else + { + flmAssert( !pOlderNode->m_nodeInfo.uiChildElmCount); + } + + if( !(pNewerNode->m_uiFlags & FDOM_VALUE_ON_DISK)) + { + if( pNewerNode->getDataLength()) + { + if (RC_BAD( rc = pNewerNode->resizeDataBuffer( + pNewerNode->getDataLength(), TRUE))) + { + goto Exit; + } + + f_memcpy( pNewerNode->getDataPtr(), pOlderNode->getDataPtr(), + pNewerNode->getDataLength()); + } + } + else + { + flmAssert( pNewerNode->getDataLength()); + flmAssert( !pNewerNode->m_nodeInfo.uiChildElmCount); + } + + if( pOlderNode->m_uiAttrCount) + { + if( RC_BAD( rc = pNewerNode->importAttributeList( + pDb, pOlderNode, TRUE))) + { + goto Exit; + } + } + + linkIntoNodeCache( NULL, pOlderNode, pNewerNode, TRUE); + + // Link the node to its place in the F_Database list + + pNewerNode->linkToDatabase( pDatabase, pDb, pDb->m_ui64CurrTransID, TRUE); + + // Update the node pointer passed into the routine + + if( *ppCachedNode) + { + (*ppCachedNode)->decrNodeUseCount(); + } + + *ppCachedNode = pNewerNode; + pNewerNode->incrNodeUseCount(); + +#ifdef FLM_CACHE_PROTECT + pNewerNode->protectCachedItem(); + bProtectNewerNode = FALSE; +#endif + + // Set pNewerNode to NULL so it won't get freed at Exit + + pNewerNode = NULL; + +Exit: + + // A non-NULL pNewerNode means there was an error of some kind where we will + // need to free up the cached item. + + if (pNewerNode) + { + flmAssert( RC_BAD( rc)); + delete pNewerNode; + } + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called to remove a node from cache. If this is + an uncommitted version of the node, it should remove that version + from cache. If the last committed version is in + cache, it should set the high transaction ID on that version to be + one less than the transaction ID of the update transaction. +****************************************************************************/ +void F_NodeCacheMgr::removeNode( + F_Db * pDb, + F_CachedNode * pNode, + FLMBOOL bDecrementUseCount, + FLMBOOL bMutexLocked) +{ + F_Database * pDatabase = pDb->m_pDatabase; + + flmAssert( pNode); + + // Lock the mutex + + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + + // Decrement the node use count if told to by the caller. + + if (bDecrementUseCount) + { + pNode->decrNodeUseCount(); + } + + // Unset the new and dirty flags + + pNode->unsetNodeDirtyAndNew( pDb, TRUE); + + // Determine if pNode is the last committed version + // or a node that was added by this same transaction. + // If it is the last committed version, set its high transaction ID. + // Otherwise, remove the node from cache. + + if (pNode->m_ui64LowTransId < pDb->m_ui64CurrTransID) + { + + // The high transaction ID on pNode better be -1 - most current version. + + flmAssert( pNode->m_ui64HighTransId == FLM_MAX_UINT64); + + pNode->setTransID( (pDb->m_ui64CurrTransID - 1)); + flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId); + pNode->setUncommitted(); + pNode->setLatestVer(); + pNode->unlinkFromDatabase(); + pNode->linkToDatabaseAtHead( pDatabase); + } + else + { + pNode->freeCache( pNode->nodeInUse()); + } + + if( !bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } +} + +/**************************************************************************** +Desc: This routine is called to remove a node from cache. +****************************************************************************/ +void F_NodeCacheMgr::removeNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) +{ + F_CachedNode * pNode; + + // Lock the mutex + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + + // Find the node in cache + + findNode( pDb, uiCollection, ui64NodeId, + pDb->m_ui64CurrTransID, TRUE, NULL, &pNode, NULL, NULL); + + if( pNode) + { + removeNode( pDb, pNode, FALSE, TRUE); + } + + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: This routine is called when an F_Database object is going to be removed + from the shared memory area. At that point, we also need to get rid + of all nodes that have been cached for that F_Database. +****************************************************************************/ +void F_Database::freeNodeCache( void) +{ + FLMUINT uiNumFreed = 0; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + while (m_pFirstNode) + { + m_pFirstNode->freeCache( m_pFirstNode->nodeInUse()); + + // Release the CPU every 100 nodes freed. + + if (++uiNumFreed == 100) + { + f_yieldCPU(); + uiNumFreed = 0; + } + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction aborts. At that + point, we need to get rid of any uncommitted versions of nodes in + the node cache. +****************************************************************************/ +void F_Database::freeModifiedNodes( + F_Db * pDb, + FLMUINT64 ui64OlderTransId) +{ + F_CachedNode * pNode; + F_CachedNode * pOlderVersion; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pNode = m_pFirstNode; + while (pNode) + { + if (pNode->nodeUncommitted()) + { + if (pNode->nodeIsLatestVer()) + { + pNode->setTransID( FLM_MAX_UINT64); + pNode->unsetUncommitted(); + pNode->unsetLatestVer(); + pNode->unlinkFromDatabase(); + pNode->linkToDatabaseAtEnd( this); + } + else + { + // Save the older version - we may be changing its + // high transaction ID back to FLM_MAX_UINT64 + + pOlderVersion = pNode->m_pOlderVersion; + + // Clear the dirty and new flags + + pNode->unsetNodeDirtyAndNew( pDb, TRUE); + + // Free the uncommitted version. + + pNode->freeCache( + (FLMBOOL)((pNode->nodeInUse() || + pNode->readingInNode()) + ? TRUE + : FALSE)); + + // If the older version has a high transaction ID that + // is exactly one less than our current transaction, + // it is the most current version. Hence, we need to + // change its high transaction ID back to FLM_MAX_UINT64. + + if (pOlderVersion && + pOlderVersion->m_ui64HighTransId == ui64OlderTransId) + { + pOlderVersion->setTransID( FLM_MAX_UINT64); + } + } + pNode = m_pFirstNode; + } + else + { + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: This routine is called when an update transaction commits. At that + point, we need to unset the "uncommitted" flag on any nodes + currently in node cache for the F_Database object. +****************************************************************************/ +void F_Database::commitNodeCache( void) +{ + F_CachedNode * pNode; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pNode = m_pFirstNode; + while (pNode) + { + if (pNode->nodeUncommitted()) + { + pNode->unsetUncommitted(); + pNode->unsetLatestVer(); + pNode = pNode->m_pNextInDatabase; + } + else + { + + // We can stop when we hit a committed version, because + // uncommitted versions are always linked in together at + // the head of the list. + + break; + } + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: This routine is called when a collection in the database is deleted. + All nodes in node cache that are in that collection must be + removed from cache. +****************************************************************************/ +void F_Db::removeCollectionNodes( + FLMUINT uiCollection, + FLMUINT64 ui64TransId) +{ + F_CachedNode * pNode; + F_CachedNode * pNextNode; + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + pNode = m_pDatabase->m_pFirstNode; + + // Stay in the loop until we have freed all nodes in the + // collection + + while (pNode) + { + + // Save the pointer to the previous entry in the list because + // we may end up unlinking pNode below, in which case we would + // have lost the previous entry. + + pNextNode = pNode->m_pNextInDatabase; + + // Only look at nodes in this collection + + if (pNode->m_nodeInfo.uiCollection == uiCollection) + { + flmAssert( pNode->m_pDatabase == m_pDatabase); + + // Only look at the most current versions. + + if (pNode->m_ui64HighTransId == FLM_MAX_UINT64) + { + + // Better not be a newer version. + + flmAssert( pNode->m_pNewerVersion == NULL); + + if (pNode->m_ui64LowTransId < ui64TransId) + { + + // This version was not added or modified by this + // transaction so it's high transaction ID should simply + // be set to one less than the current transaction ID. + + pNode->setTransID( ui64TransId - 1); + flmAssert( pNode->m_ui64HighTransId >= pNode->m_ui64LowTransId); + pNode->setUncommitted(); + pNode->setLatestVer(); + pNode->unlinkFromDatabase(); + pNode->linkToDatabaseAtHead( m_pDatabase); + } + else + { + + // The node was added or modified in this + // transaction. Simply remove it from cache. + + pNode->freeCache( pNode->nodeInUse()); + } + } + else + { + + // If not most current version, the node's high transaction + // ID better already be less than transaction ID. + + flmAssert( pNode->m_ui64HighTransId < ui64TransId); + } + } + pNode = pNextNode; + + } + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL F_CachedNode::findChildElm( + FLMUINT uiChildElmNameId, + FLMUINT * puiInsertPos) +{ + FLMBOOL bFound = FALSE; + FLMUINT uiLoop; + NODE_ITEM * pChildElmNode; + FLMUINT uiTblSize; + FLMUINT uiLow; + FLMUINT uiMid; + FLMUINT uiHigh; + FLMUINT uiTblNameId; + + // If the child element count is <= 4, do a sequential search through + // the array. Otherwise, do a binary search. + + if ((uiTblSize = m_nodeInfo.uiChildElmCount) <= 4) + { + for (uiLoop = 0, pChildElmNode = m_pNodeList; + uiLoop < m_nodeInfo.uiChildElmCount && + pChildElmNode->uiNameId < uiChildElmNameId; + uiLoop++, pChildElmNode++) + { + ; + } + if (uiLoop < m_nodeInfo.uiChildElmCount) + { + *puiInsertPos = uiLoop; + if (pChildElmNode->uiNameId == uiChildElmNameId) + { + bFound = TRUE; + } + } + else + { + *puiInsertPos = uiLoop; + } + } + else + { + uiHigh = --uiTblSize; + uiLow = 0; + for (;;) + { + uiMid = (uiLow + uiHigh) / 2; + uiTblNameId = m_pNodeList [uiMid].uiNameId; + if (uiTblNameId == uiChildElmNameId) + { + // Found Match + + *puiInsertPos = uiMid; + bFound = TRUE; + goto Exit; + } + + // Check if we are done + + if (uiLow >= uiHigh) + { + // Done, item not found + + *puiInsertPos = (uiChildElmNameId < uiTblNameId) + ? uiMid + : uiMid + 1; + goto Exit; + } + + if (uiChildElmNameId < uiTblNameId) + { + if (uiMid == 0) + { + *puiInsertPos = 0; + goto Exit; + } + uiHigh = uiMid - 1; + } + else + { + if (uiMid == uiTblSize) + { + *puiInsertPos = uiMid + 1; + goto Exit; + } + uiLow = uiMid + 1; + } + } + } + +Exit: + + return( bFound); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void F_CachedNode::checkReadFromDisk( + F_Db * pDb) +{ + FLMUINT64 ui64LowTransId; + FLMBOOL bMostCurrent; + RCODE rc; + + // Need to unlock the node cache mutex before doing the read. + + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + rc = gv_XFlmSysData.pNodeCacheMgr->readNodeFromDisk( pDb, + m_nodeInfo.uiCollection, m_nodeInfo.ui64NodeId, + this, &ui64LowTransId, &bMostCurrent); + + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedNode::setNodeDirty( + F_Db * pDb, + FLMBOOL bNew) +{ +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (!nodeIsDirty()) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + + // Should already be the uncommitted version. + + flmAssert( nodeUncommitted()); + + // Should NOT be the latest version - those cannot + // be set to dirty. - latest ver flag is only set + // for nodes that should be returned to being the + // latest version of the node if the transaction + // aborts. + + flmAssert( !nodeIsLatestVer()); + + // Unlink from its database, set the dirty flag, + // and relink at the head. + + unlinkFromDatabase(); + + if (bNew) + { + m_uiFlags |= (FDOM_DIRTY | FDOM_NEW); + } + else + { + m_uiFlags |= FDOM_DIRTY; + } + + linkToDatabaseAtHead( pDb->m_pDatabase); + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + + pDb->m_uiDirtyNodeCount++; + } + else if (bNew) + { + m_uiFlags |= FDOM_NEW; + } + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedNode::unsetNodeDirtyAndNew( + F_Db * pDb, + FLMBOOL bMutexAlreadyLocked) +{ +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + // When outputting a binary or text stream, it is possible that the + // dirty flag was unset when the last buffer was output + + if (nodeIsDirty()) + { + if( !bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hNodeCacheMutex); + } + + // Unlink from its database, unset the dirty flag, + // and relink at the head. + + unlinkFromDatabase(); + if( m_uiFlags & FDOM_DIRTY) + { + flmAssert( pDb->m_uiDirtyNodeCount); + pDb->m_uiDirtyNodeCount--; + } + + m_uiFlags &= ~(FDOM_DIRTY | FDOM_NEW); + linkToDatabaseAtHead( pDb->m_pDatabase); + + if( !bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hNodeCacheMutex); + } + } + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/***************************************************************************** +Desc: Calculate the SEN length of a number and add it to the node info item. +******************************************************************************/ +FINLINE void flmAddInfoSenLen( + XFLM_NODE_INFO_ITEM * pInfoItem, + FLMUINT64 ui64Num, + FLMUINT * puiTotalOverhead) +{ + FLMUINT uiSenLen = flmGetSENByteCount( ui64Num); + + pInfoItem->ui64Bytes += (FLMUINT64)uiSenLen; + pInfoItem->ui64Count++; + (*puiTotalOverhead) += uiSenLen; +} + +/***************************************************************************** +Desc: Node header format +******************************************************************************/ +// Header size 1 byte +// +// Node and data type 1 byte (bits = HDDDNNNN) +// H = Have data (1 bit) +// D = Data type (3 bits) +// N = Node type (4 bits) +// +// Storage Flags 1-5 bytes (typically 1 byte) +// NSF_HAVE_BASE_ID_BIT +// NSF_HAVE_META_VALUE_BIT +// NSF_HAVE_SIBLINGS_BIT +// NSF_HAVE_CHILDREN_BIT +// NSF_HAVE_ATTR_LIST_BIT +// NSF_HAVE_CELM_LIST_BIT +// NSF_HAVE_DATA_LEN_BIT +// +// NSF_EXT_HAVE_DCHILD_COUNT_BIT +// NSF_EXT_READ_ONLY_BIT +// NSF_EXT_CANNOT_DELETE_BIT +// NSF_EXT_PREFIX_BIT +// NSF_EXT_ENCRYPTED_BIT +// NSF_EXT_ANNOTATION_BIT +// NSF_EXT_QUARANTINED_BIT +// +// Document ID 0-9 byte SEN +// Base ID 1-9 byte SEN - if NSF_HAVE_BASE_ID_BIT is set +// Parent ID 0-9 byte SEN (offset from base) +// Name ID 0-5 byte SEN +// Prefix ID 0-5 byte SEN +// Meta value 0-9 byte SEN - if NSF_HAVE_META_VALUE_BIT is set +// Prev+Next Siblings 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_SIBLINGS_BIT is set +// First+Last Child ID 0-9 + 0-9 byte SEN (offset from base) - if NSF_HAVE_CHILDREN_BIT is set +// Data node child count 0-5 byte SEN - If NSF_HAVE_CHILDREN_BIT and NSF_EXT_HAVE_DCHILD_COUNT_BIT is set +// Child Element Count 0-5 byte SEN - if NSF_HAVE_CELM_LIST_BIT is set +// Encryption ID 0-5 byte SEN - if NSF_EXT_ENCRYPTED_BIT set +// Annotation node 0-9 byte SEN (offset from base) - if NSF_EXT_ANNOTATION_BIT is set +// Data length 0-5 byte SEN - if NSF_HAVE_DATA_LEN_BIT is set +// +// After the "core" header, one or more of the following may be present: +// +// If HAVE_CELM_LIST_BIT is set and we actually have a non-zero child element count: +// child element list [0-5 SEN for element ID + 0-9 SEN for node id]... +// +// IV 0 if no encryption, 8 or 16 bytes if encrypting +// Value 0+ bytes + +/***************************************************************************** +Desc: Creates a variable-sized header for the node and copies it into + the supplied buffer. The buffer must be sized to allow at least + MAX_DOM_HEADER_SIZE bytes. +******************************************************************************/ +RCODE F_CachedNode::headerToBuf( + FLMBOOL bFixedSizeHeader, + FLMBYTE * pucBuf, + FLMUINT * puiHeaderStorageSize, + XFLM_NODE_INFO * pNodeInfo, + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucStart = pucBuf; + FLMUINT uiLoop; + FLMUINT uiFlagsLen; + FLMUINT uiStorageFlags = 0; + FLMUINT uiDataChildCount = getDataChildCount(); + FLMUINT uiEncDefId = 0; + FLMUINT uiDataLength = getDataLength(); + FLMUINT uiNameId = getNameId(); + FLMUINT uiPrefixId = getPrefixId(); + FLMUINT uiFlags = m_uiFlags; + eDomNodeType eNodeType = getNodeType(); + FLMUINT64 ui64NodeId = m_nodeInfo.ui64NodeId; + FLMUINT64 ui64BaseId = ui64NodeId; + FLMUINT64 ui64DocId = getDocumentId(); + FLMUINT64 ui64ParentId = getParentId(); + FLMUINT64 ui64FirstChildId = getFirstChildId(); + FLMUINT64 ui64LastChildId = getLastChildId(); + FLMUINT64 ui64PrevSibId = getPrevSibId(); + FLMUINT64 ui64NextSibId = getNextSibId(); + FLMUINT64 ui64AnnotationId = getAnnotationId(); + FLMUINT64 ui64MetaValue = getMetaValue(); + FLMUINT64 ui64Tmp; + FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN]; + FLMBYTE * pucTmpSEN; + FLMUINT uiTotalOverhead = 0; + + if( !ui64NodeId) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Storage flags used by fixed and variable size headers + + if( uiFlags & FDOM_READ_ONLY) + { + uiStorageFlags |= NSF_EXT_READ_ONLY_BIT; + } + + if( uiFlags & FDOM_CANNOT_DELETE) + { + uiStorageFlags |= NSF_EXT_CANNOT_DELETE_BIT; + } + + if( uiFlags & FDOM_QUARANTINED) + { + uiStorageFlags |= NSF_EXT_QUARANTINED_BIT; + } + + if( uiFlags & FDOM_HAVE_CELM_LIST) + { + uiStorageFlags |= NSF_HAVE_CELM_LIST_BIT; + } + + if( uiFlags & FDOM_NAMESPACE_DECL) + { + uiStorageFlags |= NSF_EXT_NAMESPACE_DECL_BIT; + } + + if( m_uiAttrCount) + { + flmAssert( eNodeType == ELEMENT_NODE); + uiStorageFlags |= NSF_HAVE_ATTR_LIST_BIT; + } + + if( uiDataLength) + { + if( (uiEncDefId = getEncDefId()) != 0) + { + uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; + } + } + + // Output the header + + if( bFixedSizeHeader) + { + if (pucBuf) + { + flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize); + + // Set the header size bytes + + *pucBuf++ = XFLM_FIXED_SIZE_HEADER_TOKEN; + + // Encode the node type + + *pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) | + (((FLMBYTE)eNodeType) & 0x0F); + + // Document ID + + U642FBA( ui64DocId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // Parent ID + + U642FBA( ui64ParentId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // Name ID + + UD2FBA( uiNameId, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Prefix ID + + UD2FBA( uiPrefixId, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Metavalue + + U642FBA( ui64MetaValue, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // Previous and next siblings + + U642FBA( ui64PrevSibId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + U642FBA( ui64NextSibId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // First and last children + + U642FBA( ui64FirstChildId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + U642FBA( ui64LastChildId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // Data child count + + UD2FBA( uiDataChildCount, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Child element count + + UD2FBA( m_nodeInfo.uiChildElmCount, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Data length + + UD2FBA( uiDataLength, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Encryption definition ID + + UD2FBA( uiEncDefId, pucBuf); + pucBuf += sizeof( FLMUINT32); + + // Annotation ID + + U642FBA( ui64AnnotationId, pucBuf); + pucBuf += sizeof( FLMUINT64); + + // Storage flags + + UD2FBA( uiStorageFlags, pucBuf); + pucBuf += sizeof( FLMUINT32); + + flmAssert( (FLMUINT)(pucBuf - pucStart) == FIXED_DOM_HEADER_SIZE); + *puiHeaderStorageSize = FIXED_DOM_HEADER_SIZE; + } + else + { + flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize); + + pNodeInfo->headerSize.ui64Bytes++; + pNodeInfo->headerSize.ui64Count++; + + pNodeInfo->nodeAndDataType.ui64Bytes++; + pNodeInfo->nodeAndDataType.ui64Count++; + + pNodeInfo->documentId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->documentId.ui64Count++; + + pNodeInfo->parentId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->parentId.ui64Count++; + + pNodeInfo->nameId.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->nameId.ui64Count++; + + pNodeInfo->prefixId.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->prefixId.ui64Count++; + + pNodeInfo->metaValue.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->metaValue.ui64Count++; + + pNodeInfo->prevSibId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->prevSibId.ui64Count++; + + pNodeInfo->nextSibId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->nextSibId.ui64Count++; + + pNodeInfo->firstChildId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->firstChildId.ui64Count++; + + pNodeInfo->lastChildId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->lastChildId.ui64Count++; + + pNodeInfo->dataChildCount.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->dataChildCount.ui64Count++; + + pNodeInfo->childElmCount.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->childElmCount.ui64Count++; + + pNodeInfo->unencDataLen.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->unencDataLen.ui64Count++; + + pNodeInfo->encDefId.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->encDefId.ui64Count++; + + pNodeInfo->annotationId.ui64Bytes += sizeof( FLMUINT64); + pNodeInfo->annotationId.ui64Count++; + + pNodeInfo->flags.ui64Bytes += sizeof( FLMUINT32); + pNodeInfo->flags.ui64Count++; + + uiTotalOverhead += FIXED_DOM_HEADER_SIZE; + } + } + else + { + + // Determine the base ID + + if( ui64DocId < ui64BaseId) + { + flmAssert( ui64DocId); + ui64BaseId = ui64DocId; + } + + if( ui64ParentId && ui64ParentId < ui64BaseId) + { + ui64BaseId = m_nodeInfo.ui64ParentId; + } + + if( ui64PrevSibId && ui64PrevSibId < ui64BaseId) + { + ui64BaseId = ui64PrevSibId; + } + + if( ui64NextSibId && ui64NextSibId < ui64BaseId) + { + ui64BaseId = ui64NextSibId; + } + + if( ui64FirstChildId && ui64FirstChildId < ui64BaseId) + { + flmAssert( ui64LastChildId); + ui64BaseId = ui64FirstChildId; + } + + if( ui64LastChildId && ui64LastChildId < ui64BaseId) + { + flmAssert( ui64FirstChildId); + ui64BaseId = ui64LastChildId; + } + + if( ui64AnnotationId && ui64AnnotationId < ui64BaseId) + { + ui64BaseId = ui64AnnotationId; + } + + if (pucBuf) + { + flmAssert( !pNodeInfo && !pDb && puiHeaderStorageSize); + + // Reserve a byte for the header length + + pucBuf++; + + // Encode the node type + + *pucBuf++ = ((((FLMBYTE)m_nodeInfo.uiDataType) & 0x07) << 4) | + (((FLMBYTE)eNodeType) & 0x0F) | + (uiDataLength ? 0x80 : 0); + + // Document ID + + flmEncodeSEN( ui64DocId, &pucBuf); + + // Encode the base ID if it isn't equal to the document ID + + if( ui64BaseId != ui64DocId) + { + uiStorageFlags |= NSF_HAVE_BASE_ID_BIT; + flmEncodeSEN( ui64BaseId, &pucBuf); + } + + // Parent ID + + if( (ui64Tmp = ui64ParentId) == 0) + { + ui64Tmp = ui64NodeId; + } + + flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf); + + // Name ID + + flmEncodeSEN( uiNameId, &pucBuf); + + // Prefix ID + + if( uiPrefixId) + { + uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT; + flmEncodeSEN( uiPrefixId, &pucBuf); + } + + // Metavalue + + if( ui64MetaValue) + { + uiStorageFlags |= NSF_HAVE_META_VALUE_BIT; + flmEncodeSEN( ui64MetaValue, &pucBuf); + } + + // Previous and next siblings + + if( ui64PrevSibId || ui64NextSibId) + { + if( (ui64Tmp = ui64PrevSibId) == 0) + { + ui64Tmp = ui64NodeId; + } + + flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf); + + if( (ui64Tmp = ui64NextSibId) == 0) + { + ui64Tmp = ui64NodeId; + } + + flmEncodeSEN( ui64Tmp - ui64BaseId, &pucBuf); + uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT; + } + + // First, last, and data children + + if( ui64FirstChildId) + { + flmAssert( ui64LastChildId); + + flmEncodeSEN( ui64FirstChildId - ui64BaseId, &pucBuf); + flmEncodeSEN( ui64LastChildId - ui64BaseId, &pucBuf); + uiStorageFlags |= NSF_HAVE_CHILDREN_BIT; + + if( uiDataChildCount) + { + flmEncodeSEN( uiDataChildCount, &pucBuf); + uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT; + } + } + + // Child element count + + if( uiFlags & FDOM_HAVE_CELM_LIST) + { + // NOTE: It is legal for m_nodeInfo.uiChildElmCount to be zero. + // The FDOM_EXT_CHILD_ELM_LIST bit is also used to enforce + // the fact that all of the child elements must have unique + // name IDs. + + flmEncodeSEN( m_nodeInfo.uiChildElmCount, &pucBuf); + } + + // Encryption ID + + if( uiEncDefId) + { + uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; + flmEncodeSEN( uiEncDefId, &pucBuf); + } + + // Annotation ID + + if( ui64AnnotationId) + { + uiStorageFlags |= NSF_EXT_ANNOTATION_BIT; + flmEncodeSEN( ui64AnnotationId - ui64BaseId, &pucBuf); + } + + // Output the data length if needed + + if( uiDataLength && + (uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount)) + { + uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT; + flmEncodeSEN( uiDataLength, &pucBuf); + } + + // Output the storage flags (inverted SEN) + + uiFlagsLen = flmGetSENByteCount( uiStorageFlags); + if( uiFlagsLen > 1) + { + pucTmpSEN = ucTmpSEN; + flmEncodeSEN( uiStorageFlags, &pucTmpSEN); + + for( uiLoop = uiFlagsLen; uiLoop > 0; uiLoop--) + { + *pucBuf++ = ucTmpSEN[ uiLoop - 1]; + } + } + else + { + *pucBuf++ = (FLMBYTE)uiStorageFlags; + } + + flmAssert( (FLMUINT)(pucBuf - pucStart) <= MAX_DOM_HEADER_SIZE); + + // Set the header size + + *puiHeaderStorageSize = (FLMUINT)(pucBuf - pucStart); + *pucStart = (FLMBYTE)(*puiHeaderStorageSize); + } + else + { + flmAssert( pNodeInfo && pDb && !puiHeaderStorageSize); + + pNodeInfo->headerSize.ui64Bytes++; + pNodeInfo->headerSize.ui64Count++; + + pNodeInfo->nodeAndDataType.ui64Bytes++; + pNodeInfo->nodeAndDataType.ui64Count++; + + pNodeInfo->documentId.ui64Bytes += flmGetSENByteCount( ui64DocId); + pNodeInfo->documentId.ui64Count++; + + uiTotalOverhead = 3; + + // Document ID + + flmAddInfoSenLen( &pNodeInfo->documentId, ui64DocId, &uiTotalOverhead); + + // Encode the base ID if it isn't equal to the document ID + + if (ui64BaseId != ui64DocId) + { + uiStorageFlags |= NSF_HAVE_BASE_ID_BIT; + flmAddInfoSenLen( &pNodeInfo->baseId, ui64BaseId, &uiTotalOverhead); + } + + // Parent ID + + if ((ui64Tmp = ui64ParentId) == 0) + { + ui64Tmp = ui64NodeId; + } + flmAddInfoSenLen( &pNodeInfo->parentId, ui64Tmp - ui64BaseId, + &uiTotalOverhead); + + // Name ID + + flmAddInfoSenLen( &pNodeInfo->nameId, uiNameId, &uiTotalOverhead); + + // Prefix ID + + if (uiPrefixId) + { + uiStorageFlags |= NSF_EXT_HAVE_PREFIX_BIT; + flmAddInfoSenLen( &pNodeInfo->prefixId, uiPrefixId, &uiTotalOverhead); + } + + // Meta Value + + if (ui64MetaValue) + { + uiStorageFlags |= NSF_HAVE_META_VALUE_BIT; + flmAddInfoSenLen( &pNodeInfo->metaValue, ui64MetaValue, &uiTotalOverhead); + } + + // First/Last sibling + + if (ui64PrevSibId || ui64NextSibId) + { + if ((ui64Tmp = ui64PrevSibId) == 0) + { + ui64Tmp = ui64NodeId; + } + flmAddInfoSenLen( &pNodeInfo->prevSibId, ui64Tmp, &uiTotalOverhead); + + if ((ui64Tmp = ui64NextSibId) == 0) + { + ui64Tmp = ui64NodeId; + } + flmAddInfoSenLen( &pNodeInfo->nextSibId, ui64Tmp, &uiTotalOverhead); + uiStorageFlags |= NSF_HAVE_SIBLINGS_BIT; + } + + // First, last, and data children + + if (ui64FirstChildId) + { + flmAddInfoSenLen( &pNodeInfo->firstChildId, + ui64FirstChildId - ui64BaseId, &uiTotalOverhead); + flmAddInfoSenLen( &pNodeInfo->lastChildId, + ui64LastChildId - ui64BaseId, &uiTotalOverhead); + uiStorageFlags |= NSF_HAVE_CHILDREN_BIT; + if (uiDataChildCount) + { + flmAddInfoSenLen( &pNodeInfo->dataChildCount, + uiDataChildCount, &uiTotalOverhead); + uiStorageFlags |= NSF_EXT_HAVE_DCHILD_COUNT_BIT; + } + } + + // Child element count - may be zero, so we should test the flag. + + if (uiFlags & FDOM_HAVE_CELM_LIST) + { + flmAddInfoSenLen( &pNodeInfo->childElmCount, + m_nodeInfo.uiChildElmCount, &uiTotalOverhead); + } + + // Encryption ID + + if (uiEncDefId) + { + uiStorageFlags |= NSF_EXT_ENCRYPTED_BIT; + flmAddInfoSenLen( &pNodeInfo->encDefId, uiEncDefId, &uiTotalOverhead); + } + + // Annotation ID + + if( ui64AnnotationId) + { + uiStorageFlags |= NSF_EXT_ANNOTATION_BIT; + flmAddInfoSenLen( &pNodeInfo->annotationId, + ui64AnnotationId - ui64BaseId, &uiTotalOverhead); + } + + // Data length if needed + + if( uiDataLength && + (uiEncDefId || (uiFlags & FDOM_HAVE_CELM_LIST) || m_uiAttrCount)) + { + uiStorageFlags |= NSF_HAVE_DATA_LEN_BIT; + flmAddInfoSenLen( &pNodeInfo->unencDataLen, + uiDataLength, &uiTotalOverhead); + } + + flmAddInfoSenLen( &pNodeInfo->flags, uiStorageFlags, &uiTotalOverhead); + } + } + + // Account for other overhead. + + if (pNodeInfo) + { + if (eNodeType == ELEMENT_NODE) + { + NODE_ITEM * pNodeItem; + FLMUINT uiNodeCount = getChildElmCount(); + FLMUINT uiPrevNameId = 0; + FLMUINT64 ui64ElmNodeId = getNodeId(); + + // Go through the child element list and calculate the length needed + // to store the name id and node id for each one. + + pNodeItem = m_pNodeList; + for( uiLoop = 0; uiLoop < uiNodeCount; pNodeItem++, uiLoop++) + { + flmAssert( pNodeItem->uiNameId > uiPrevNameId); + flmAssert( pNodeItem->ui64NodeId > ui64ElmNodeId); + + flmAddInfoSenLen( &pNodeInfo->childElmNameId, + pNodeItem->uiNameId - uiPrevNameId, &uiTotalOverhead); + flmAddInfoSenLen( &pNodeInfo->childElmNodeId, + pNodeItem->ui64NodeId - ui64ElmNodeId, &uiTotalOverhead); + uiPrevNameId = pNodeItem->uiNameId; + } + + // Determine space taken by attributes. + + if (m_uiAttrCount) + { + if( RC_BAD( rc = exportAttributeList( pDb, NULL, pNodeInfo))) + { + goto Exit; + } + } + } + + // Determine space needed to store encryption IV and any + // encryption padding. + + if (uiEncDefId) + { + F_ENCDEF * pEncDef; + FLMUINT uiTmp; + + if (RC_BAD( rc = pDb->m_pDict->getEncDef( uiEncDefId, &pEncDef))) + { + goto Exit; + } + + uiTmp = pEncDef->pCcs->getIVLen(); + flmAssert( uiTmp == 8 || uiTmp == 16); + pNodeInfo->encIV.ui64Bytes += (FLMUINT64)uiTmp; + pNodeInfo->encIV.ui64Count++; + uiTotalOverhead += uiTmp; + + uiTmp = getEncLen( uiDataLength) - uiDataLength; + if (uiTmp) + { + pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiTmp; + pNodeInfo->encPadding.ui64Count++; + uiTotalOverhead += uiTmp; + } + } + + pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiTotalOverhead; + pNodeInfo->totalOverhead.ui64Count++; + switch (eNodeType) + { + case ELEMENT_NODE: + pNodeInfo->elementNode.ui64Bytes += + (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; + pNodeInfo->elementNode.ui64Count++; + break; + case DATA_NODE: + pNodeInfo->dataNode.ui64Bytes += + (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; + pNodeInfo->dataNode.ui64Count++; + break; + case COMMENT_NODE: + pNodeInfo->commentNode.ui64Bytes += + (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; + pNodeInfo->commentNode.ui64Count++; + break; + default: + pNodeInfo->otherNode.ui64Bytes += + (FLMUINT64)uiDataLength + (FLMUINT64)uiTotalOverhead; + pNodeInfo->otherNode.ui64Count++; + break; + } + + switch (m_nodeInfo.uiDataType) + { + case XFLM_NODATA_TYPE: + pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataNodata.ui64Count++; + break; + case XFLM_TEXT_TYPE: + pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataString.ui64Count++; + break; + case XFLM_NUMBER_TYPE: + pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataNumeric.ui64Count++; + break; + case XFLM_BINARY_TYPE: + pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataBinary.ui64Count++; + break; + default: + flmAssert( 0); + break; + } + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE flmReadNodeInfo( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_IStream * pIStream, + FLMUINT uiOverallLength, + FLMBOOL bAssertOnCorruption, + F_NODE_INFO * pNodeInfo, + FLMUINT * puiStorageFlags, + FLMBOOL * pbFixedSizeHeader) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + FLMUINT uiStorageFlags = 0; + FLMUINT uiStorageFlagsLen; + FLMUINT uiHeaderStorageSize; + FLMBYTE ucHeader[ MAX_DOM_HEADER_SIZE]; + const FLMBYTE * pucHeader; + const FLMBYTE * pucHeaderEnd; + FLMUINT64 ui64BaseId = 0; + FLMBYTE ucTmpSEN[ FLM_MAX_SEN_LEN]; + FLMBYTE * pucTmpSEN; + FLMBOOL bEOFValid = TRUE; + FLMBOOL bHaveData; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( bAssertOnCorruption); +#endif + + // Set the node ID and collection + + pNodeInfo->uiCollection = uiCollection; + pNodeInfo->ui64NodeId = ui64NodeId; + + // Read the expected length of the header + + if( RC_BAD( rc = pIStream->read( &ucHeader[ 0], 1))) + { + goto Exit; + } + + bEOFValid = FALSE; + + // A length value of XFLM_FIXED_SIZE_HEADER_TOKEN indicates that we have a + // non-compressed header on the node. This type of header is used when a + // large text or binary value is streamed into the database. + + if( ucHeader[ 0] == XFLM_FIXED_SIZE_HEADER_TOKEN) + { + // Read the rest of the header + + uiHeaderStorageSize = FIXED_DOM_HEADER_SIZE; + if( RC_BAD( rc = pIStream->read( &ucHeader[ 1], + uiHeaderStorageSize - 1, NULL))) + { + goto Exit; + } + + pucHeader = ucHeader; + pucHeaderEnd = pucHeader + uiHeaderStorageSize; + + // Skip past the header size byte + + pucHeader++; + + // Get the node type, data type, and data flag + + pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F); + pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07; + pucHeader++; + + // Document ID + + pNodeInfo->ui64DocumentId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // Parent ID + + pNodeInfo->ui64ParentId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // Name ID + + pNodeInfo->uiNameId = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + // Prefix ID + + pNodeInfo->uiPrefixId = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + // Metavalue + + pNodeInfo->ui64MetaValue = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // Previous and next siblings + + pNodeInfo->ui64PrevSibId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + pNodeInfo->ui64NextSibId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // First and last children + + pNodeInfo->ui64FirstChildId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + pNodeInfo->ui64LastChildId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // Data child count + + pNodeInfo->uiDataChildCount = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + // Child element count + + pNodeInfo->uiChildElmCount = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + if( pNodeInfo->uiChildElmCount && + pNodeInfo->eNodeType != ELEMENT_NODE) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Data length + + pNodeInfo->uiDataLength = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + // Encryption Id + + pNodeInfo->uiEncDefId = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Annotation ID + + pNodeInfo->ui64AnnotationId = FB2U64( pucHeader); + pucHeader += sizeof( FLMUINT64); + + // Storage flags + + uiStorageFlags = FB2UD( pucHeader); + pucHeader += sizeof( FLMUINT32); + + // Set the fixed size header flag + + if( pbFixedSizeHeader) + { + *pbFixedSizeHeader = TRUE; + } + } + else + { + if( (uiHeaderStorageSize = (FLMUINT)ucHeader[ 0]) > MAX_DOM_HEADER_SIZE) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Read the rest of the header + + if( RC_BAD( rc = pIStream->read( &ucHeader[ 1], + uiHeaderStorageSize - 1, NULL))) + { + goto Exit; + } + + pucHeader = ucHeader; + pucHeaderEnd = pucHeader + uiHeaderStorageSize; + + // Get the storage flags + + uiStorageFlags = pucHeader[ uiHeaderStorageSize - 1]; + uiStorageFlagsLen = flmGetSENLength( (FLMBYTE)uiStorageFlags); + + if( uiStorageFlagsLen > 1) + { + pucTmpSEN = ucTmpSEN; + for( uiLoop = 1; uiLoop <= uiStorageFlagsLen; uiLoop++) + { + *pucTmpSEN++ = pucHeader[ uiHeaderStorageSize - uiLoop]; + } + + pucTmpSEN = ucTmpSEN; + if( RC_BAD( rc = flmDecodeSEN( (const FLMBYTE **)&pucTmpSEN, + pucTmpSEN + uiStorageFlagsLen, &uiStorageFlags))) + { + goto Exit; + } + } + + // Skip past the header size byte + + pucHeader++; + + // Get the node type, data type, and data flag + + pNodeInfo->eNodeType = (eDomNodeType)((*pucHeader) & 0x0F); + + if( pNodeInfo->eNodeType == INVALID_NODE || + pNodeInfo->eNodeType > PROCESSING_INSTRUCTION_NODE) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + pNodeInfo->uiDataType = ((*pucHeader) >> 4) & 0x07; + bHaveData = *pucHeader & 0x80 ? TRUE : FALSE; + pucHeader++; + + // Document ID + + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64DocumentId))) + { + goto Exit; + } + + // Base ID + + if( uiStorageFlags & NSF_HAVE_BASE_ID_BIT) + { + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, + pucHeaderEnd, &ui64BaseId))) + { + goto Exit; + } + } + else + { + ui64BaseId = pNodeInfo->ui64DocumentId; + } + + // Parent ID + + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, + pucHeaderEnd, &pNodeInfo->ui64ParentId))) + { + goto Exit; + } + + if( (pNodeInfo->ui64ParentId += ui64BaseId) == ui64NodeId) + { + pNodeInfo->ui64ParentId = 0; + } + + // Name ID + + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiNameId))) + { + goto Exit; + } + + // Prefix ID + + if( uiStorageFlags & NSF_EXT_HAVE_PREFIX_BIT) + { + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiPrefixId))) + { + goto Exit; + } + } + else + { + pNodeInfo->uiPrefixId = 0; + } + + // Metavalue + + if( uiStorageFlags & NSF_HAVE_META_VALUE_BIT) + { + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64MetaValue))) + { + goto Exit; + } + } + else + { + pNodeInfo->ui64MetaValue = 0; + } + + // Previous and next siblings + + if( uiStorageFlags & NSF_HAVE_SIBLINGS_BIT) + { + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64PrevSibId))) + { + goto Exit; + } + + if( (pNodeInfo->ui64PrevSibId += ui64BaseId) == ui64NodeId) + { + pNodeInfo->ui64PrevSibId = 0; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64NextSibId))) + { + goto Exit; + } + + if( (pNodeInfo->ui64NextSibId += ui64BaseId) == ui64NodeId) + { + pNodeInfo->ui64NextSibId = 0; + } + } + else + { + pNodeInfo->ui64PrevSibId = 0; + pNodeInfo->ui64NextSibId = 0; + } + + // First and last children. Also will read the data child count. + + if( uiStorageFlags & NSF_HAVE_CHILDREN_BIT) + { + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64FirstChildId))) + { + goto Exit; + } + + pNodeInfo->ui64FirstChildId += ui64BaseId; + + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64LastChildId))) + { + goto Exit; + } + + pNodeInfo->ui64LastChildId += ui64BaseId; + + if( uiStorageFlags & NSF_EXT_HAVE_DCHILD_COUNT_BIT) + { + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiDataChildCount))) + { + goto Exit; + } + } + else + { + pNodeInfo->uiDataChildCount = 0; + } + } + else + { + pNodeInfo->ui64FirstChildId = 0; + pNodeInfo->ui64LastChildId = 0; + pNodeInfo->uiDataChildCount = 0; + } + + // Child element count + + if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) + { + // This bit should only be set for elements. + + if( pNodeInfo->eNodeType != ELEMENT_NODE) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // NOTE: This bit may be set even if there are no children. + // This bit also serves to indicate that this particular + // element is a unique child element, and we should keep + // a list of child elements as they are added and removed. + // We should also enforce that no children have the same + // name id. + + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiChildElmCount))) + { + goto Exit; + } + + // If the count > 0, the NSF_HAVE_CHILDREN_BIT better also be set. + + if( pNodeInfo->uiChildElmCount) + { + if( !(uiStorageFlags & NSF_HAVE_CHILDREN_BIT)) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + } + } + else + { + pNodeInfo->uiChildElmCount = 0; + } + + // Encryption ID + + if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) + { + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiEncDefId))) + { + goto Exit; + } + } + else + { + pNodeInfo->uiEncDefId = 0; + } + + // Annotation ID + + if( uiStorageFlags & NSF_EXT_ANNOTATION_BIT) + { + if( RC_BAD( rc = flmDecodeSEN64( &pucHeader, pucHeaderEnd, + &pNodeInfo->ui64AnnotationId))) + { + goto Exit; + } + + pNodeInfo->ui64AnnotationId += ui64BaseId; + } + else + { + pNodeInfo->ui64AnnotationId = 0; + } + + if( uiStorageFlags & NSF_HAVE_DATA_LEN_BIT) + { + if( RC_BAD( rc = flmDecodeSEN( &pucHeader, pucHeaderEnd, + &pNodeInfo->uiDataLength))) + { + goto Exit; + } + } + else + { + pNodeInfo->uiDataLength = 0; + } + + // Account for storage flags + + pucHeader += uiStorageFlagsLen; + + // Make sure the header was the expected size + + if( pucHeader != pucHeaderEnd) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + // Set the data length to whatever is remaining if we didn't + // have a data length in the header + + if( bHaveData && !(uiStorageFlags & NSF_HAVE_DATA_LEN_BIT)) + { + flmAssert( uiOverallLength >= uiHeaderStorageSize); + pNodeInfo->uiDataLength = uiOverallLength - uiHeaderStorageSize; + } + + // Set the fixed size header flag + + if( pbFixedSizeHeader) + { + *pbFixedSizeHeader = FALSE; + } + } + + if( pNodeInfo->uiEncDefId && !pNodeInfo->uiDataLength) + { +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + + goto Exit; + } + + if( puiStorageFlags) + { + *puiStorageFlags = uiStorageFlags; + } + +Exit: + + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN) + { + if( !bEOFValid) + { + // If one of the calls to read from the stream returned an EOF error, + // the database is corrupt. + +#ifdef FLM_DEBUG + if( bAssertOnCorruption) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + else +#endif + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::readNode( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_IStream * pIStream, + FLMUINT uiOverallLength, + FLMBYTE * pucIV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiStorageLength; + FLMUINT uiStorageFlags; + FLMUINT uiIVLen; + FLMBYTE ucIV[ 16]; + FLMBOOL bEOFValid = TRUE; + FLMBOOL bFixedSizeHeader; + + if( RC_BAD( rc = flmReadNodeInfo( uiCollection, + ui64NodeId, pIStream, uiOverallLength, FALSE, &m_nodeInfo, + &uiStorageFlags, &bFixedSizeHeader))) + { + goto Exit; + } + + bEOFValid = FALSE; + m_uiFlags = 0; + + if( bFixedSizeHeader) + { + m_uiFlags |= FDOM_FIXED_SIZE_HEADER; + } + + // Read the child element list, if any + + if( uiStorageFlags & NSF_HAVE_CELM_LIST_BIT) + { + if( m_nodeInfo.uiChildElmCount) + { + FLMUINT uiLen; + FLMUINT uiLoop; + NODE_ITEM * pElmNode; + FLMUINT uiPrevNameId = 0; + FLMUINT64 ui64ElmNodeId = getNodeId(); + FLMUINT uiChildElmCount = m_nodeInfo.uiChildElmCount; + + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + + // Need to set to zero so the resizeChildElmList will work. This + // is the actual size allocated. + + m_nodeInfo.uiChildElmCount = 0; + + if( RC_BAD( rc = resizeChildElmList( uiChildElmCount, FALSE))) + { + goto Exit; + } + + // Read in all of the element name IDs and node IDs. + + for( uiLoop = 0, pElmNode = m_pNodeList; + uiLoop < m_nodeInfo.uiChildElmCount; + uiLoop++, pElmNode++) + { + if( RC_BAD( rc = flmReadSEN( pIStream, &pElmNode->uiNameId, + &uiLen))) + { + goto Exit; + } + + pElmNode->uiNameId += uiPrevNameId; + uiPrevNameId = pElmNode->uiNameId; + + if( RC_BAD( rc = flmReadSEN64( pIStream, + &pElmNode->ui64NodeId, &uiLen))) + { + goto Exit; + } + + pElmNode->ui64NodeId += ui64ElmNodeId; + } + } + + m_uiFlags |= FDOM_HAVE_CELM_LIST; + } + + // Read the attribute list + + if( uiStorageFlags & NSF_HAVE_ATTR_LIST_BIT) + { + if( m_nodeInfo.eNodeType != ELEMENT_NODE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if( RC_BAD( rc = importAttributeList( pDb, pIStream, FALSE))) + { + goto Exit; + } + } + + // Read the initialization vector if this is an encrypted node + + if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) + { + F_Dict * pDict; + F_ENCDEF * pEncDef; + + if( RC_BAD( rc = pDb->getDictionary( &pDict))) + { + goto Exit; + } + + if( RC_BAD( rc = pDict->getEncDef( getEncDefId(), &pEncDef))) + { + goto Exit; + } + + uiIVLen = pEncDef->pCcs->getIVLen(); + + if( uiIVLen != 8 && uiIVLen != 16) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + if( RC_BAD( rc = pIStream->read( ucIV, uiIVLen))) + { + goto Exit; + } + + if( pucIV) + { + f_memcpy( pucIV, ucIV, uiIVLen); + } + + uiStorageLength = getEncLen( m_nodeInfo.uiDataLength); + } + else + { + uiStorageLength = m_nodeInfo.uiDataLength; + } + + // Read the data part of the node, if any + + if( uiStorageLength) + { + // Data size must have room for data to point back to + // the node. Always align on 8 byte boundaries - just to be safe. + // It is the highest alignment we support. + + if( calcDataBufSize( uiStorageLength) <= MAX_CELL_SIZE || + m_nodeInfo.eNodeType == ELEMENT_NODE) + { + if( RC_BAD( rc = resizeDataBuffer( uiStorageLength, FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = pIStream->read( (char *)getDataPtr(), + uiStorageLength))) + { + goto Exit; + } + + // Decrypt the data + + if( uiStorageFlags & NSF_EXT_ENCRYPTED_BIT) + { + if( RC_BAD( rc = pDb->decryptData( + m_nodeInfo.uiEncDefId, ucIV, getDataPtr(), + uiStorageLength, getDataPtr(), uiStorageLength))) + { + goto Exit; + } + } + + // If the type is a number, cache a 'quick' number. + + if( m_nodeInfo.uiDataType == XFLM_NUMBER_TYPE) + { + FLMUINT64 ui64Num; + FLMBOOL bNeg; + + if( RC_BAD( rc = flmStorageNumberToNumber( + getDataPtr(), getDataLength(), &ui64Num, &bNeg))) + { + goto Exit; + } + + if( !bNeg) + { + setUINT64( ui64Num); + } + else + { + setINT64( -(FLMINT64)ui64Num); + } + } + } + else + { + flmAssert( m_nodeInfo.eNodeType != ELEMENT_NODE); + flmAssert( m_nodeInfo.uiDataType == XFLM_TEXT_TYPE || + m_nodeInfo.uiDataType == XFLM_BINARY_TYPE); + m_uiFlags |= FDOM_VALUE_ON_DISK; + } + } + + if( uiStorageFlags & NSF_EXT_READ_ONLY_BIT) + { + m_uiFlags |= FDOM_READ_ONLY; + } + + if( uiStorageFlags & NSF_EXT_CANNOT_DELETE_BIT) + { + m_uiFlags |= FDOM_CANNOT_DELETE; + } + + if( uiStorageFlags & NSF_EXT_QUARANTINED_BIT) + { + m_uiFlags |= FDOM_QUARANTINED; + } + + if( uiStorageFlags & NSF_EXT_NAMESPACE_DECL_BIT) + { + m_uiFlags |= FDOM_NAMESPACE_DECL; + } + + // Sanity checks + + switch( m_nodeInfo.eNodeType) + { + case DOCUMENT_NODE: + { + if( !isRootNode() || getFirstChildId() != getLastChildId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + break; + } + + case ELEMENT_NODE: + { + if( isRootNode() && getParentId()) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + break; + } + + default: + { + break; + } + } + +Exit: + + if( rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_BAD_SEN) + { + if( !bEOFValid) + { + // If one of the calls to read from the stream returned an EOF error, + // the database is corrupt. + + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + } + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::importAttributeList( + F_Db * pDb, + IF_IStream * pIStream, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMUINT uiAttrCount; + FLMUINT uiNameId; + FLMUINT uiBaseNameId; + FLMUINT uiStorageFlags; + FLMUINT uiPayloadLength; + FLMUINT uiLoop; + FLMUINT uiInsertPos; + F_AttrElmInfo defInfo; + + flmAssert( !m_uiAttrCount); + flmAssert( m_nodeInfo.eNodeType == ELEMENT_NODE); + + // Determine the number of attributes + + if( RC_BAD( rc = flmReadSEN( pIStream, &uiAttrCount))) + { + goto Exit; + } + + if( !uiAttrCount) + { + goto Exit; + } + + // Import the attributes + + if( RC_BAD( rc = flmReadSEN( pIStream, &uiBaseNameId))) + { + goto Exit; + } + + for( uiLoop = 0; uiLoop < uiAttrCount; uiLoop++) + { + if( RC_BAD( rc = flmReadSEN( pIStream, &uiNameId))) + { + goto Exit; + } + + uiNameId += uiBaseNameId; + + if (getAttribute( uiNameId, &uiInsertPos) != NULL) + { + flmAssert( 0); + } + if( RC_BAD( rc = allocAttribute( pDb, + uiNameId, NULL, uiInsertPos, &pAttrItem, bMutexAlreadyLocked))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadSEN( pIStream, &uiStorageFlags))) + { + goto Exit; + } + + if( uiStorageFlags & ASF_READ_ONLY_BIT) + { + pAttrItem->m_uiFlags |= FDOM_READ_ONLY; + } + + if( uiStorageFlags & ASF_CANNOT_DELETE_BIT) + { + pAttrItem->m_uiFlags |= FDOM_CANNOT_DELETE; + } + + if( uiStorageFlags & ASF_HAVE_PREFIX_BIT) + { + if( RC_BAD( rc = flmReadSEN( pIStream, &pAttrItem->m_uiPrefixId))) + { + goto Exit; + } + } + + uiPayloadLength = (uiStorageFlags & ASF_PAYLOAD_LEN_MASK); + + if( uiPayloadLength == ASF_HAVE_PAYLOAD_LEN_SEN) + { + if( RC_BAD( rc = flmReadSEN( pIStream, &uiPayloadLength))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiNameId, &defInfo))) + { + goto Exit; + } + + pAttrItem->m_uiDataType = defInfo.getDataType(); + + if( uiStorageFlags & ASF_ENCRYPTED_BIT) + { + F_ENCDEF * pEncDef; + + if( RC_BAD( rc = flmReadSEN( pIStream, &pAttrItem->m_uiEncDefId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmReadSEN( pIStream, + &pAttrItem->m_uiDecryptedDataLen))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getEncDef( + pAttrItem->m_uiEncDefId, &pEncDef))) + { + goto Exit; + } + + pAttrItem->m_uiIVLen = pEncDef->pCcs->getIVLen(); + flmAssert( pAttrItem->m_uiIVLen == 8 || pAttrItem->m_uiIVLen == 16); + } + + if( uiPayloadLength) + { + if( RC_BAD( rc = pAttrItem->resizePayloadBuffer( + uiPayloadLength, bMutexAlreadyLocked))) + { + goto Exit; + } + + if( RC_BAD( rc = pIStream->read( pAttrItem->getAttrPayloadPtr(), + uiPayloadLength))) + { + goto Exit; + } + } + + pAttrItem->m_uiPayloadLen = uiPayloadLength; + + if( pAttrItem->m_uiDataType == XFLM_NUMBER_TYPE && + !pAttrItem->m_uiEncDefId) + { + FLMBOOL bNeg; + + if( RC_BAD( rc = flmStorageNumberToNumber( + pAttrItem->getAttrDataPtr(), pAttrItem->getAttrDataLength(), + &pAttrItem->m_ui64QuickVal, &bNeg))) + { + goto Exit; + } + + if( !bNeg) + { + pAttrItem->m_uiFlags |= FDOM_UNSIGNED_QUICK_VAL; + } + else + { + pAttrItem->m_uiFlags |= FDOM_SIGNED_QUICK_VAL; + } + } + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::importAttributeList( + F_Db * pDb, + F_CachedNode * pSourceNode, + FLMBOOL bMutexAlreadyLocked) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pNewItem; + F_AttrItem * pSourceItem; + FLMUINT uiLoop; + + flmAssert( !m_uiAttrCount); + if( RC_BAD( rc = resizeAttrList( pSourceNode->m_uiAttrCount, + bMutexAlreadyLocked))) + { + goto Exit; + } + + for (uiLoop = 0; uiLoop < pSourceNode->m_uiAttrCount; uiLoop++) + { + pSourceItem = pSourceNode->m_ppAttrList [uiLoop]; + + if( RC_BAD( rc = allocAttribute( pDb, + pSourceItem->m_uiNameId, pSourceItem, uiLoop, &pNewItem, + bMutexAlreadyLocked))) + { + goto Exit; + } + + if( pSourceItem->m_uiPayloadLen > sizeof( FLMBYTE *)) + { + if( RC_BAD( rc = pNewItem->setupAttribute( + pDb, pSourceItem->m_uiEncDefId, + pSourceItem->getAttrDataLength(), FALSE, + bMutexAlreadyLocked))) + { + goto Exit; + } + + flmAssert( pSourceItem->getAttrPayloadSize() == + pNewItem->getAttrPayloadSize()); + + f_memcpy( pNewItem->getAttrPayloadPtr(), + pSourceItem->getAttrPayloadPtr(), pSourceItem->m_uiPayloadLen); + } + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +void F_AttrItem::getAttrSizeNeeded( + FLMUINT uiBaseNameId, + XFLM_NODE_INFO * pNodeInfo, + FLMUINT * puiSaveStorageFlags, + FLMUINT * puiSizeNeeded) +{ + FLMUINT uiNameSize; + FLMUINT uiFlagsSize; + FLMUINT uiPrefixSize; + FLMUINT uiPayloadLength; + FLMUINT uiPayloadLenSize; + FLMUINT uiEncIdSize; + FLMUINT uiUnencLenSize; + FLMUINT uiOverhead = 0; + FLMUINT uiStorageFlags; + + uiNameSize = flmGetSENByteCount( m_uiNameId - uiBaseNameId); + uiOverhead += uiNameSize; + + uiStorageFlags = getAttrStorageFlags(); + if (puiSaveStorageFlags) + { + *puiSaveStorageFlags = uiStorageFlags; + } + + uiFlagsSize = flmGetSENByteCount( uiStorageFlags); + uiOverhead += uiFlagsSize; + + if( m_uiPrefixId) + { + uiPrefixSize = flmGetSENByteCount( m_uiPrefixId); + uiOverhead += uiPrefixSize; + } + else + { + uiPrefixSize = 0; + } + + uiPayloadLength = m_uiPayloadLen; + (*puiSizeNeeded) += uiPayloadLength; + + if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN) + { + uiPayloadLenSize = flmGetSENByteCount( uiPayloadLength); + uiOverhead += uiPayloadLenSize; + } + else + { + uiPayloadLenSize = 0; + } + + if( m_uiEncDefId) + { + flmAssert( uiPayloadLength); + + uiEncIdSize = flmGetSENByteCount( m_uiEncDefId); + uiOverhead += uiEncIdSize; + uiUnencLenSize = flmGetSENByteCount( m_uiDecryptedDataLen); + uiOverhead += uiUnencLenSize; + } + else + { + uiEncIdSize = 0; + uiUnencLenSize = 0; + } + + (*puiSizeNeeded) += uiOverhead; + + if (pNodeInfo) + { + FLMUINT uiDataLength; + + pNodeInfo->nameId.ui64Bytes += (FLMUINT64)uiNameSize; + pNodeInfo->nameId.ui64Count++; + + pNodeInfo->attrFlags.ui64Bytes += (FLMUINT64)uiFlagsSize; + pNodeInfo->attrFlags.ui64Count++; + + if (uiPrefixSize) + { + pNodeInfo->prefixId.ui64Bytes += (FLMUINT64)uiPrefixSize; + pNodeInfo->prefixId.ui64Count++; + } + + if (uiPayloadLenSize) + { + pNodeInfo->attrPayloadLen.ui64Bytes += (FLMUINT64)uiPayloadLenSize; + pNodeInfo->attrPayloadLen.ui64Count++; + } + + uiDataLength = getAttrDataLength(); + if (m_uiEncDefId) + { + FLMUINT uiEncPadding; + + pNodeInfo->encDefId.ui64Bytes += (FLMUINT64)uiEncIdSize; + pNodeInfo->encDefId.ui64Count++; + pNodeInfo->unencDataLen.ui64Bytes += (FLMUINT64)uiUnencLenSize; + pNodeInfo->unencDataLen.ui64Count++; + pNodeInfo->encIV.ui64Bytes += (FLMUINT64)m_uiIVLen; + pNodeInfo->encIV.ui64Count++; + uiOverhead += m_uiIVLen; + flmAssert( m_uiPayloadLen >= m_uiIVLen - uiDataLength); + uiEncPadding = m_uiPayloadLen - m_uiIVLen - uiDataLength; + if (uiEncPadding) + { + pNodeInfo->encPadding.ui64Bytes += (FLMUINT64)uiEncPadding; + pNodeInfo->encPadding.ui64Count++; + uiOverhead += uiEncPadding; + } + } + + pNodeInfo->totalOverhead.ui64Bytes += (FLMUINT64)uiOverhead; + pNodeInfo->totalOverhead.ui64Count++; + + pNodeInfo->attributeNode.ui64Bytes += (FLMUINT64)(uiOverhead + uiDataLength); + pNodeInfo->attributeNode.ui64Count++; + switch (m_uiDataType) + { + case XFLM_NODATA_TYPE: + pNodeInfo->dataNodata.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataNodata.ui64Count++; + break; + case XFLM_TEXT_TYPE: + pNodeInfo->dataString.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataString.ui64Count++; + break; + case XFLM_NUMBER_TYPE: + pNodeInfo->dataNumeric.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataNumeric.ui64Count++; + break; + case XFLM_BINARY_TYPE: + pNodeInfo->dataBinary.ui64Bytes += (FLMUINT64)uiDataLength; + pNodeInfo->dataBinary.ui64Count++; + break; + default: + flmAssert( 0); + break; + } + } +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE F_CachedNode::exportAttributeList( + F_Db * pDb, + F_DynaBuf * pDynaBuf, + XFLM_NODE_INFO * pNodeInfo) +{ + RCODE rc = NE_XFLM_OK; + F_AttrItem * pAttrItem; + FLMBYTE * pucStart; + FLMBYTE * pucBuf; + FLMBYTE * pucEnd; + FLMUINT uiPayloadLength; + FLMUINT uiLoop; + FLMUINT uiSizeNeeded; + FLMUINT uiStorageFlags; + FLMUINT uiBaseNameId = m_ppAttrList [0]->m_uiNameId; +#define MAX_STORAGE_FLAGS 32 + FLMUINT storageFlagsList[ MAX_STORAGE_FLAGS]; + + // Logging should be done by the caller + +#ifdef FLM_DEBUG + if (!pNodeInfo) + { + flmAssert( !pDb->m_pDatabase->m_pRfl->isLoggingEnabled()); + } +#endif + + // Determine the size of the node buffer + + uiSizeNeeded = 0; + for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) + { + pAttrItem = m_ppAttrList [uiLoop]; + + // If uiAttributeCount < MAX_STORAGE_FLAGS, we pass in a pointer + // to that slot in the storageFlagsList array to save the + // storage flags, so we don't have to recalculate them in the + // 2nd pass - up to the first MAX_STORAGE_FLAGS storage flags + // will be saved. + + if (uiLoop < MAX_STORAGE_FLAGS) + { + pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo, + &storageFlagsList [uiLoop], + &uiSizeNeeded); + } + else + { + pAttrItem->getAttrSizeNeeded( uiBaseNameId, pNodeInfo, NULL, + &uiSizeNeeded); + } + } + + flmAssert( m_uiAttrCount); + if (pNodeInfo) + { + flmAssert( !pDynaBuf); + pNodeInfo->attrCount.ui64Bytes += flmGetSENByteCount( m_uiAttrCount); + pNodeInfo->attrCount.ui64Count++; + pNodeInfo->attrBaseId.ui64Bytes += flmGetSENByteCount( uiBaseNameId); + pNodeInfo->attrBaseId.ui64Count++; + } + else + { + uiSizeNeeded += flmGetSENByteCount( m_uiAttrCount); + uiSizeNeeded += flmGetSENByteCount( uiBaseNameId); + flmAssert( pDynaBuf); + + if( RC_BAD( rc = pDynaBuf->allocSpace( uiSizeNeeded, (void **)&pucBuf))) + { + goto Exit; + } + + pucStart = pucBuf; + pucEnd = pucStart + uiSizeNeeded; + + if( RC_BAD( rc = flmEncodeSEN( m_uiAttrCount, &pucBuf, pucEnd))) + { + goto Exit; + } + + if( RC_BAD( rc = flmEncodeSEN( uiBaseNameId, &pucBuf, pucEnd))) + { + goto Exit; + } + + // Once we have written out attribute count, we can reset it to zero. + + for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) + { + pAttrItem = m_ppAttrList [uiLoop]; + if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiNameId - uiBaseNameId, + &pucBuf, pucEnd))) + { + goto Exit; + } + + // If we saved the storage flags in the first pass, get them + // from storageFlagsList, otherwise recalculate them. + + uiStorageFlags = (uiLoop < MAX_STORAGE_FLAGS) + ? storageFlagsList [uiLoop] + : pAttrItem->getAttrStorageFlags(); + + if( RC_BAD( rc = flmEncodeSEN( uiStorageFlags, &pucBuf, pucEnd))) + { + goto Exit; + } + + if( pAttrItem->m_uiPrefixId) + { + if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiPrefixId, + &pucBuf, pucEnd))) + { + goto Exit; + } + } + + uiPayloadLength = pAttrItem->m_uiPayloadLen; + + if( uiPayloadLength > ASF_MAX_EMBEDDED_PAYLOAD_LEN) + { + if( RC_BAD( rc = flmEncodeSEN( uiPayloadLength, &pucBuf, pucEnd))) + { + goto Exit; + } + } + + if( pAttrItem->m_uiEncDefId) + { + flmAssert( uiPayloadLength); + + if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiEncDefId, + &pucBuf, pucEnd))) + { + goto Exit; + } + + if( RC_BAD( rc = flmEncodeSEN( pAttrItem->m_uiDecryptedDataLen, + &pucBuf, pucEnd))) + { + goto Exit; + } + } + + f_memcpy( pucBuf, pAttrItem->getAttrPayloadPtr(), uiPayloadLength); + pucBuf += uiPayloadLength; + } + + flmAssert( pucBuf == pucEnd); + } + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedNode::resetNode( void) +{ + FLMBYTE * pucActualAlloc; + + // Should not count attribute size here, because it will be subtracted + // when the attributes themselves are deleted. + + FLMUINT uiSize = memSize() - m_uiTotalAttrSize; + + flmAssert( !m_pPrevInBucket); + flmAssert( !m_pNextInBucket); + flmAssert( !m_pOlderVersion); + flmAssert( !m_pNewerVersion); + flmAssert( !m_pPrevInOldList); + flmAssert( !m_pNextInOldList); + flmAssert( !m_pNotifyList); + flmAssert( !m_uiStreamUseCount); + flmAssert( !nodeInUse()); + + f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); + + // If this is an old version, decrement the old version counters. + + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize && + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerCount); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + } + + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize && + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiCount); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; + + if( m_uiFlags & FDOM_HEAP_ALLOC) + { + unlinkFromHeapList(); + } + + if( m_pucData || m_pNodeList || m_ppAttrList) + { + f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); + + if( m_pucData) + { + pucActualAlloc = getActualPointer( m_pucData); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + m_uiDataBufSize, &pucActualAlloc); + m_pucData = NULL; + m_uiDataBufSize = 0; + } + + if( m_pNodeList) + { + pucActualAlloc = getActualPointer( m_pNodeList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcNodeListBufSize( m_nodeInfo.uiChildElmCount), + &pucActualAlloc); + m_pNodeList = NULL; + } + + if( m_ppAttrList) + { + FLMUINT uiLoop; + + for (uiLoop = 0; uiLoop < m_uiAttrCount; uiLoop++) + { + delete m_ppAttrList [uiLoop]; + } + pucActualAlloc = getActualPointer( m_ppAttrList); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + calcAttrListBufSize( m_uiAttrCount), &pucActualAlloc); + m_ppAttrList = NULL; + m_uiAttrCount = 0; + } + } + + m_ui64LowTransId = 0; + m_ui64HighTransId = FLM_MAX_UINT64; + m_uiCacheFlags = 0; + m_pDatabase = NULL; + m_uiFlags = 0; + m_uiOffsetIndex = 0; + m_ui32BlkAddr = 0; + f_memset( &m_nodeInfo, 0, sizeof( F_NODE_INFO)); + + uiSize = memSize(); + if (m_ui64HighTransId != FLM_MAX_UINT64) + { + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes += uiSize; + } + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount += uiSize; +} + +#undef new +#undef delete + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_CachedNode::operator new( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvCell; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( uiSize); +#endif + flmAssert( uiSize == sizeof( F_CachedNode)); + f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); + + if( (pvCell = + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.allocCell( + &gv_XFlmSysData.pNodeCacheMgr->m_nodeRelocator)) != NULL) + { +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.unprotectCell( pvCell); +#endif + } + + return( pvCell); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_CachedNode::operator new[]( FLMSIZET) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_CachedNode::operator new( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLineNum) +#ifndef FLM_NLM + throw() +#endif +{ + // This new should never be called + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_CachedNode::operator new[]( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLine) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedNode::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.protectCell( ptr); +#endif + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.freeCell( (FLMBYTE *)ptr, FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedNode::operator delete[]( + void * // ptr) + ) +{ + flmAssert( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedNode::operator delete( + void * ptr, + const char *, // pszFileName + int // iLineNum + ) +{ + if( !ptr) + { + return; + } + + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.freeCell( (FLMBYTE *)ptr, FALSE); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedNode::operator delete[]( + void *, // ptr, + const char *, // pszFileName + int // iLineNum + ) +{ + flmAssert( 0); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +F_AttrItem::~F_AttrItem() +{ + FLMUINT uiSize = memSize(); + + if (m_pCachedNode) + { + flmAssert( m_pCachedNode->m_uiTotalAttrSize >= uiSize); + m_pCachedNode->m_uiTotalAttrSize -= uiSize; + if (m_pCachedNode->m_ui64HighTransId != FLM_MAX_UINT64) + { + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes >= uiSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + } + flmAssert( gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount >= uiSize); + gv_XFlmSysData.pNodeCacheMgr->m_Usage.uiByteCount -= uiSize; + } + if( m_uiPayloadLen > sizeof( FLMBYTE *)) + { + m_pucPayload -= sizeof( F_AttrItem *); + gv_XFlmSysData.pNodeCacheMgr->m_bufAllocator.freeBuf( + m_uiPayloadLen + sizeof( F_AttrItem *), + &m_pucPayload); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_AttrItem::operator new( + FLMSIZET uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvCell; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( uiSize); +#endif + flmAssert( uiSize == sizeof( F_AttrItem)); + f_assertMutexLocked( gv_XFlmSysData.hNodeCacheMutex); + + if( (pvCell = + gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.allocCell( + &gv_XFlmSysData.pNodeCacheMgr->m_attrItemRelocator)) != NULL) + { +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_nodeAllocator.unprotectCell( pvCell); +#endif + } + + return( pvCell); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_AttrItem::operator new[]( FLMSIZET) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_AttrItem::operator new( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLineNum) +#ifndef FLM_NLM + throw() +#endif +{ + // This new should never be called + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_AttrItem::operator new[]( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLine) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_AttrItem::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.protectCell( ptr); +#endif + gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.freeCell( (FLMBYTE *)ptr, FALSE); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_AttrItem::operator delete[]( + void * // ptr) + ) +{ + flmAssert( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_AttrItem::operator delete( + void * ptr, + const char *, // pszFileName + int // iLineNum + ) +{ + if( !ptr) + { + return; + } + + gv_XFlmSysData.pNodeCacheMgr->m_attrItemAllocator.freeCell( (FLMBYTE *)ptr, FALSE); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_DEBUG) && !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_AttrItem::operator delete[]( + void *, // ptr, + const char *, // pszFileName + int // iLineNum + ) +{ + flmAssert( 0); +} +#endif diff --git a/version5/src/nodeinfo.cpp b/version5/src/nodeinfo.cpp new file mode 100644 index 0000000..35014de --- /dev/null +++ b/version5/src/nodeinfo.cpp @@ -0,0 +1,241 @@ +//------------------------------------------------------------------------------ +// Desc: Class for gathering node information. +// +// Tabs: 3 +// +// Copyright (c) 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: nodeinfo.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/***************************************************************************** +Desc: Get node information and add it to the node information object. +******************************************************************************/ +RCODE XFLMAPI F_NodeInfo::addNodeInfo( + IF_Db * ifpDb, + IF_DOMNode * pNode, + FLMBOOL bDoNodeSubTree, + FLMBOOL bDoSelf) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pCurNode = NULL; + IF_DOMNode * pTmpNode = NULL; + FLMUINT64 ui64MyNodeId; + F_CachedNode * pCachedNode = ((F_DOMNode *)pNode)->m_pCachedNode; + FLMUINT uiCollection = pCachedNode->getCollection(); + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bDoNode; + F_Db * pDb = (F_Db *)ifpDb; + + // Start a read transaction, if no other transaction is going. + + if (ifpDb->getTransType() == XFLM_NO_TRANS) + { + if (RC_BAD( rc = ifpDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + bStartedTrans = TRUE; + } + + pCurNode = pNode; + pCurNode->AddRef(); + + // Need special case handling of attribute nodes, because + // the pCachedNode will be pointing to the element node for + // the attribute. Furthermore, it will be the only node + // we do, because it can have no children to do. + + if (pCurNode->getNodeType() == ATTRIBUTE_NODE) + { + if (bDoSelf) + { + F_AttrItem * pAttrItem = NULL; + FLMUINT uiSizeNeeded; + + if (pCachedNode->m_uiAttrCount) + { + pAttrItem = pCachedNode->getAttribute( + ((F_DOMNode *)pCurNode)->m_uiAttrNameId, NULL); + } + if (!pAttrItem) + { + rc = RC_SET( NE_XFLM_DOM_NODE_DELETED); + goto Exit; + } + pAttrItem->getAttrSizeNeeded( pCachedNode->m_ppAttrList [0]->m_uiNameId, + &m_nodeInfo, NULL, &uiSizeNeeded); + } + + // There can be no child nodes to do, so we are done. + + goto Exit; + } + + // Traverse the sub-tree and get info. on all nodes below and including the + // node we are starting on. + + ui64MyNodeId = pCachedNode->getNodeId(); + bDoNode = bDoSelf; + for (;;) + { + + // Add in statistics for the current node. + + if (bDoNode) + { + if (RC_BAD( rc = pCachedNode->headerToBuf( + pCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER + ? TRUE + : FALSE, NULL, NULL, &m_nodeInfo, pDb))) + { + goto Exit; + } + } + if (!bDoNodeSubTree) + { + break; + } + + // If the node has an annotation, do that node. + + if (pCachedNode->getAnnotationId() && bDoNode) + { + F_CachedNode * pTmpCachedNode; + + if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getAnnotationId(), + &pTmpNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + pTmpCachedNode = ((F_DOMNode *)pTmpNode)->m_pCachedNode; + if (RC_BAD( rc = pTmpCachedNode->headerToBuf( + pTmpCachedNode->getModeFlags() & FDOM_FIXED_SIZE_HEADER + ? TRUE + : FALSE, NULL, NULL, &m_nodeInfo, pDb))) + { + goto Exit; + } + } + + // If the node has a child node, go to it. + + if (pCachedNode->getFirstChildId()) + { + if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getFirstChildId(), + &pCurNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; + bDoNode = TRUE; + continue; + } + + for(;;) + { + + // If we are on the node we started on, there is nothing more to do. + + if (pCachedNode->getNodeId() == ui64MyNodeId) + { + goto Exit; // Should return NE_XFLM_OK + } + + // If node has a sibling node, go to it. + + if (pCachedNode->getNextSibId()) + { + if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getNextSibId(), + &pCurNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; + bDoNode = TRUE; + break; + } + + // Traverse back up the tree to parent node. + // Better be a parent node at this point - because we know we + // are not on the node we started on. + + flmAssert( pCachedNode->getParentId()); + if (RC_BAD( rc = ifpDb->getNode( uiCollection, pCachedNode->getParentId(), + &pCurNode))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + goto Exit; + } + pCachedNode = ((F_DOMNode *)pCurNode)->m_pCachedNode; + } + } + +Exit: + + if (pCurNode) + { + pCurNode->Release(); + } + if (pTmpNode) + { + pTmpNode->Release(); + } + if (bStartedTrans) + { + (void)ifpDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: Create an empty node info. object and return it's interface... +****************************************************************************/ +RCODE XFLMAPI F_DbSystem::createIFNodeInfo( + IF_NodeInfo ** ppNodeInfo) +{ + RCODE rc = NE_XFLM_OK; + + if ((*ppNodeInfo = f_new F_NodeInfo) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} diff --git a/version5/src/recover.cpp b/version5/src/recover.cpp new file mode 100644 index 0000000..476d6b5 --- /dev/null +++ b/version5/src/recover.cpp @@ -0,0 +1,483 @@ +//------------------------------------------------------------------------------ +// Desc: Contains routines for recovering a database after +// a failure. +// +// 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: recover.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +/**************************************************************************** +Desc: This routine reads the next before-image block from the database. +****************************************************************************/ +RCODE F_Db::readRollbackLog( + FLMUINT uiLogEOF, // Address of end of rollback log. + FLMUINT * puiCurrAddr, // This is the current address we are + // reading in the log file. It + // will be updated after reading the + // data. + F_BLK_HDR * pBlkHdr, // This is the buffer that is to hold + // the data that is read from the + // log file. + FLMBOOL * pbIsBeforeImageBlk// Is block a before-image block? + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFilePos; + FLMUINT uiBlkSize = m_pDatabase->m_uiBlockSize; + FLMUINT uiBytesRead; + F_TMSTAMP StartTime; + + uiFilePos = *puiCurrAddr; + + // Verify that we are not going to read beyond the log EOF + + if (!FSAddrIsAtOrBelow( uiFilePos + uiBlkSize, uiLogEOF)) + { + rc = RC_SET( NE_XFLM_INCOMPLETE_LOG); + goto Exit; + } + + // Position to the appropriate place and read the data + + if (m_pDbStats) + { + m_pDbStats->bHaveStats = TRUE; + m_pDbStats->LogBlockReads.ui64Count++; + m_pDbStats->LogBlockReads.ui64TotalBytes += uiBlkSize; + f_timeGetTimeStamp( &StartTime); + } + + if (RC_BAD( rc = m_pSFileHdl->ReadBlock( uiFilePos, + uiBlkSize, (FLMBYTE *)pBlkHdr, &uiBytesRead))) + { + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_INCOMPLETE_LOG); + } + + if (m_pDbStats) + { + m_pDbStats->uiReadErrors++; + } + goto Exit; + } + + if (m_pDbStats) + { + flmAddElapTime( &StartTime, &m_pDbStats->LogBlockReads.ui64ElapMilli); + } + + if (uiBytesRead != uiBlkSize) + { + if (m_pDbStats) + { + m_pDbStats->uiLogBlockChkErrs++; + } + + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + if (RC_BAD( rc = flmPrepareBlockForUse( uiBlkSize, pBlkHdr))) + { + if (m_pDbStats && rc == NE_XFLM_BLOCK_CRC) + { + m_pDbStats->uiLogBlockChkErrs++; + } + goto Exit; + } + + // See if before image bit is set. Need to unset it if it is. + + *pbIsBeforeImageBlk = (FLMBOOL)((pBlkHdr->ui8BlkFlags & + BLK_IS_BEFORE_IMAGE) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE); + pBlkHdr->ui8BlkFlags &= ~(BLK_IS_BEFORE_IMAGE); + + // Adjust the current address for the next read + + uiFilePos += uiBlkSize; + if (FSGetFileOffset( uiFilePos) >= m_pDatabase->m_uiMaxFileSize) + { + FLMUINT uiFileNumber = FSGetFileNumber( uiFilePos); + + if (!uiFileNumber) + { + uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + } + else + { + uiFileNumber++; + } + + if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER) + { + rc = RC_SET( NE_XFLM_DB_FULL); + goto Exit; + } + uiFilePos = FSBlkAddress( uiFileNumber, 0 ); + } + + *puiCurrAddr = uiFilePos; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine reads and processes a before-image block record + in the log file. The reapply flag indicates whether the + block should be written back to the database file. +****************************************************************************/ +RCODE F_Db::processBeforeImage( + FLMUINT uiLogEOF, // Address of the end of the rollback + // log. + FLMUINT * puiCurrAddrRV, // This is the current offset we are + // reading in the log file. + // It will be updated after reading the + // data. + F_BLK_HDR * pBlkHdr, // This is a pointer to a buffer that + // will be used to hold the block that + // is read. + FLMBOOL bDoingRecovery, // Are we doing a recovery as opposed to + // rolling back a transaction? + FLMUINT64 ui64MaxTransID // Maximum transaction ID to recover to when + // bDoingRecovery is TRUE. This parameter + // is ignored when bDoingRecover is FALSE. + + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkAddress; + FLMUINT uiBlkLength; +#ifdef FLM_DBG_LOG + FLMUINT64 ui64TransID; +#endif + FLMUINT uiBytesWritten; + FLMBOOL bIsBeforeImageBlk; + F_TMSTAMP StartTime; + + // Read the block from the log + + if (RC_BAD( rc = readRollbackLog( uiLogEOF, puiCurrAddrRV, pBlkHdr, + &bIsBeforeImageBlk))) + { + goto Exit; + } + + // Determine if we want to restore the block. + // If we are doing a recovery, restore the block only if + // its checkpoint is <= ui64MaxTransID. If we are + // rolling back a transaction, restore the block only if + // it is marked as a before-image block. + + // For the recovery process, multiple versions + // of the same block may be restored if there are + // multiple versions in the log. However, because + // the versions will be in ascending order in the + // file, ultimately, the one with the highest + // checkpoint that does not exceed ui64MaxTransID + // will be restored - which is precisely the one + // we want to be restored for a recovery. + + // For a transaction rollback, it is impossible for us + // to see more than one version of a block that is + // marked as the before-image version, because we + // started from a point in the log where the last + // update transaction logged its first block. All + // blocks after that point that have the BI bits + // set should be restored. Any that do not have + // the BI bit set should NOT be restored. + + if (bDoingRecovery) + { + if (pBlkHdr->ui64TransID > ui64MaxTransID) + { + goto Exit; + } + } + else if (!bIsBeforeImageBlk) + { + goto Exit; + } + + // Determine the block address before setting the checksum. + + uiBlkAddress = (FLMUINT)pBlkHdr->ui32BlkAddr; + uiBlkLength = blkGetEnd( m_pDatabase->m_uiBlockSize, blkHdrSize( pBlkHdr), + pBlkHdr); +#ifdef FLM_DBG_LOG + ui64TransID = pBlkHdr->ui64TransID; +#endif + + if (RC_BAD( rc = flmPrepareBlockToWrite( m_pDatabase->m_uiBlockSize, + pBlkHdr))) + { + goto Exit; + } + + if (m_pDbStats) + { + m_pDbStats->bHaveStats = TRUE; + m_pDbStats->LogBlockRestores.ui64Count++; + m_pDbStats->LogBlockRestores.ui64TotalBytes += uiBlkLength; + f_timeGetTimeStamp( &StartTime); + } + + m_pSFileHdl->setMaxAutoExtendSize( m_pDatabase->m_uiMaxFileSize); + m_pSFileHdl->setExtendSize( m_pDatabase->m_uiFileExtendSize); + rc = m_pSFileHdl->WriteBlock( uiBlkAddress, uiBlkLength, pBlkHdr, + m_pDatabase->m_uiBlockSize, NULL, &uiBytesWritten); +#ifdef FLM_DBG_LOG + flmDbgLogWrite( m_pDatabase, uiBlkAddress, 0, ui64TransID, + "ROLLBACK"); +#endif + + if (m_pDbStats) + { + flmAddElapTime( &StartTime, &m_pDbStats->LogBlockRestores.ui64ElapMilli); + if (RC_BAD( rc)) + { + m_pDbStats->uiWriteErrors++; + } + } + +Exit: + + return( rc); +} + +/*************************************************************************** +Desc: Writes the log header to disk. The checksum is calculated before + writing the log header to disk. +*****************************************************************************/ +RCODE F_Database::writeDbHdr( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + XFLM_DB_HDR * pDbHdr, // DB header to be written out. + XFLM_DB_HDR * pCPDbHdr, // DB header as it was at the time + // of the checkpoint. + FLMBOOL bIsCheckpoint // Are we writing a checkpoint? If we + // we are, we may write the DB header + // as is. Otherwise, we need to make + // sure we don't write out certain + // parts of the DB header - they must + // not be updated on disk until a + // checkpoint actually occurs. + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesWritten; + F_FileHdl * pCFileHdl = NULL; + XFLM_DB_HDR * pTmpDbHdr; + F_TMSTAMP StartTime; + + // Force any recent writes to disk before modifying the DB + // header. This routine is generally called after having + // written out data blocks or rollback blocks. It is + // critial that any previous writes be flushed before the + // header is updated because the header will generally have + // been modified to point to the new things that were added. + + if (RC_BAD( rc = pSFileHdl->Flush())) + { + goto Exit; + } + + // No need to ever actually write the header to disk if this is + // a temporary database + + if (m_bTempDb) + { + goto Exit; + } + + pTmpDbHdr = m_pDbHdrWriteBuf; + + uiBytesWritten = sizeof( XFLM_DB_HDR); + f_memcpy( pTmpDbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); + + // If we are not doing a checkpoint, we don't really want + // to write out certain items, so we restore them from + // the database header as it was at the time of the last + // checkpoint. + + if (!bIsCheckpoint && pCPDbHdr) + { + pTmpDbHdr->ui32RflLastCPFileNum = pCPDbHdr->ui32RflLastCPFileNum; + pTmpDbHdr->ui32RflLastCPOffset = pCPDbHdr->ui32RflLastCPOffset; + pTmpDbHdr->ui64CurrTransID = pCPDbHdr->ui64CurrTransID; + pTmpDbHdr->ui64TransCommitCnt = pCPDbHdr->ui64TransCommitCnt; + pTmpDbHdr->ui32FirstAvailBlkAddr = pCPDbHdr->ui32FirstAvailBlkAddr; + pTmpDbHdr->ui32LogicalEOF = pCPDbHdr->ui32LogicalEOF; + pTmpDbHdr->ui32BlksChangedSinceBackup = + pCPDbHdr->ui32BlksChangedSinceBackup; + pTmpDbHdr->ui64LastRflCommitID = pCPDbHdr->ui64LastRflCommitID; + } + + // Header is always written out in native format. Set the CRC + + flmAssert( !hdrIsNonNativeFormat( pTmpDbHdr)); + + pTmpDbHdr->ui32HdrCRC = calcDbHdrCRC( pTmpDbHdr); + + // Now update the log header record on disk. + + if (pDbStats) + { + pDbStats->bHaveStats = TRUE; + pDbStats->DbHdrWrites.ui64Count++; + pDbStats->DbHdrWrites.ui64TotalBytes += + uiBytesWritten; + f_timeGetTimeStamp( &StartTime); + } + + if( RC_BAD( rc = pSFileHdl->GetFileHdl( 0, TRUE, + (IF_FileHdl **)&pCFileHdl))) + { + goto Exit; + } + + if( RC_BAD( rc = pCFileHdl->SectorWrite( 0, + uiBytesWritten, pTmpDbHdr, + pCFileHdl->GetSectorSize(), + NULL, &uiBytesWritten, FALSE))) + { + if (pDbStats) + { + pDbStats->uiWriteErrors++; + } + + goto Exit; + } + + if (pDbStats) + { + flmAddElapTime( &StartTime, &pDbStats->DbHdrWrites.ui64ElapMilli); + } + + // Finally, force the header to disk. + + if (RC_BAD( rc = pCFileHdl->Flush())) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine recovers the database to a physically consistent + state. +Ret: NE_XFLM_OK - Indicates the database has been recovered. + other - other FLAIM error codes +****************************************************************************/ +RCODE F_Db::physRollback( + FLMUINT uiLogEOF, + FLMUINT uiFirstLogBlkAddr, // Address of first log block + FLMBOOL bDoingRecovery, // Doing recovery? If so, we will + // ignore blocks whose transaction + // ID is higher than ui64MaxTransID. + // Also, we will not check the BI + // bits in the logged blocks, because + // we are not rolling back a + // transaction. + FLMUINT64 ui64MaxTransID // Ignored when bDoingRecovery is + // FALSE + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCurrAddr; + FLMBYTE * pucBlk = NULL; + + // If the log is empty, no need to do anything. + // A uiFirstLogBlkAddr of zero indicates that there + // is nothing in the log to rollback. This will be true + // if we are rolling back a transaction, and the transaction + // has not logged anything or if we are doing a recovery and + // nothing was logged since the last checkpoint. + + if (uiLogEOF == m_pDatabase->m_uiBlockSize || !uiFirstLogBlkAddr) + { + goto Exit; // Will return NE_XFLM_OK + } + + // Allocate a buffer to be used for reading. + +#ifdef FLM_WIN + if ((pucBlk = (FLMBYTE *)VirtualAlloc( NULL, + (DWORD)m_pDatabase->m_uiBlockSize, + MEM_COMMIT, PAGE_READWRITE)) == NULL) + { + rc = MapWinErrorToFlaim( GetLastError(), NE_XFLM_MEM); + goto Exit; + } +#else + if (RC_BAD( rc = f_alloc( m_pDatabase->m_uiBlockSize, + &pucBlk))) + { + goto Exit; + } +#endif + + // Start from beginning of log and read to EOF restoring before-image + // blocks along the way. + + uiCurrAddr = uiFirstLogBlkAddr; + m_pSFileHdl->enableFlushMinimize(); + while (FSAddrIsBelow( uiCurrAddr, uiLogEOF)) + { + if (RC_BAD( rc = processBeforeImage( uiLogEOF, &uiCurrAddr, + (F_BLK_HDR *)pucBlk, bDoingRecovery, + ui64MaxTransID))) + { + goto Exit; + } + } + + // Force the writes to the file. + + if (RC_BAD( rc = m_pSFileHdl->Flush())) + { + goto Exit; + } + +Exit: + m_pSFileHdl->disableFlushMinimize(); + + // Free the memory handle, if one was allocated. + + if (pucBlk) + { +#ifdef FLM_WIN + (void)VirtualFree( pucBlk, 0, MEM_RELEASE); +#else + f_free( &pucBlk); +#endif + } + return( rc); +} diff --git a/version5/src/regexp.cpp b/version5/src/regexp.cpp new file mode 100644 index 0000000..b0be084 --- /dev/null +++ b/version5/src/regexp.cpp @@ -0,0 +1,4164 @@ +//------------------------------------------------------------------------------ +// Desc: This is regular expression class. +// +// 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: regexp.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +typedef enum +{ + EXP_LITERAL = 0, + EXP_CHAR_CLASS, + EXP_ALTERNATIVES +} eExpType; + +typedef struct BlockCharRangeTag +{ + FLMUNICODE uzLowChar; + FLMUNICODE uzHighChar; + const char * pszBlockName; +} BLOCK_CHAR_RANGE; + +BLOCK_CHAR_RANGE FlmBlockCharRanges[] = +{ + {0x0000, 0x007F, "BasicLatin"}, + {0x0080, 0x00FF, "Latin-1Supplement"}, + {0x0100, 0x017F, "LatinExtended-A"}, + {0x0180, 0x024F, "LatinExtended-B"}, + {0x0250, 0x02AF, "IPAExtensions"}, + {0x02B0, 0x02FF, "SpacingModifierLetters"}, + {0x0300, 0x036F, "CombiningDiacriticalMarks"}, + {0x0370, 0x03FF, "Greek"}, + {0x0400, 0x04FF, "Cyrillic"}, + {0x0530, 0x058F, "Armenian"}, + {0x0590, 0x05FF, "Hebrew"}, + {0x0600, 0x06FF, "Arabic"}, + {0x0700, 0x074F, "Syriac"}, + {0x0780, 0x07BF, "Thaana"}, + {0x0900, 0x097F, "Devanagari"}, + {0x0980, 0x09FF, "Bengali"}, + {0x0A00, 0x0A7F, "Gurmukhi"}, + {0x0A80, 0x0AFF, "Gujarati"}, + {0x0B00, 0x0B7F, "Oriya"}, + {0x0B80, 0x0BFF, "Tamil"}, + {0x0C00, 0x0C7F, "Telugu"}, + {0x0C80, 0x0CFF, "Kannada"}, + {0x0D00, 0x0D7F, "Malayalam"}, + {0x0D80, 0x0DFF, "Sinhala"}, + {0x0E00, 0x0E7F, "Thai"}, + {0x0E80, 0x0EFF, "Lao"}, + {0x0F00, 0x0FFF, "Tibetan"}, + {0x1000, 0x109F, "Myanmar"}, + {0x10A0, 0x10FF, "Georgian"}, + {0x1100, 0x11FF, "HangulJamo"}, + {0x1200, 0x137F, "Ethiopic"}, + {0x13A0, 0x13FF, "Cherokee"}, + {0x1400, 0x167F, "UnifiedCanadianAboriginalSyllabics"}, + {0x1680, 0x169F, "Ogham"}, + {0x16A0, 0x16FF, "Runic"}, + {0x1780, 0x17FF, "Khmer"}, + {0x1800, 0x18AF, "Mongolian"}, + {0x1E00, 0x1EFF, "LatinExtendedAdditional"}, + {0x1F00, 0x1FFF, "GreekExtended"}, + {0x2000, 0x206F, "GeneralPunctuation"}, + {0x2070, 0x209F, "SuperscriptsandSubscripts"}, + {0x20A0, 0x20CF, "CurrencySymbols"}, + {0x20D0, 0x20FF, "CombiningMarksforSymbols"}, + {0x2100, 0x214F, "LetterlikeSymbols"}, + {0x2150, 0x218F, "NumberForms"}, + {0x2190, 0x21FF, "Arrows"}, + {0x2200, 0x22FF, "MathematicalOperators"}, + {0x2300, 0x23FF, "MiscellaneousTechnical"}, + {0x2400, 0x243F, "ControlPictures"}, + {0x2440, 0x245F, "OpticalCharacterRecognition"}, + {0x2460, 0x24FF, "EnclosedAlphanumerics"}, + {0x2500, 0x257F, "BoxDrawing"}, + {0x2580, 0x259F, "BlockElements"}, + {0x25A0, 0x25FF, "GeometricShapes"}, + {0x2600, 0x26FF, "MiscellaneousSymbols"}, + {0x2700, 0x27BF, "Dingbats"}, + {0x2800, 0x28FF, "BraillePatterns"}, + {0x2E80, 0x2EFF, "CJKRadicalsSupplement"}, + {0x2F00, 0x2FDF, "KangxiRadicals"}, + {0x2FF0, 0x2FFF, "IdeographicDescriptionCharacters"}, + {0x3000, 0x303F, "CJKSymbolsandPunctuation"}, + {0x3040, 0x309F, "Hiragana"}, + {0x30A0, 0x30FF, "Katakana"}, + {0x3100, 0x312F, "Bopomofo"}, + {0x3130, 0x318F, "HangulCompatibilityJamo"}, + {0x3190, 0x319F, "Kanbun"}, + {0x31A0, 0x31BF, "BopomofoExtended"}, + {0x3200, 0x32FF, "EnclosedCJKLettersandMonths"}, + {0x3300, 0x33FF, "CJKCompatibility"}, + {0x3400, 0x4DB5, "CJKUnifiedIdeographsExtensionA"}, + {0x4E00, 0x9FFF, "CJKUnifiedIdeographs"}, + {0xA000, 0xA48F, "YiSyllables"}, + {0xA490, 0xA4CF, "YiRadicals"}, + {0xAC00, 0xD7A3, "HangulSyllables"}, + {0xD800, 0xDB7F, "HighSurrogates"}, + {0xDB80, 0xDBFF, "HighPrivateUseSurrogates"}, + {0xDC00, 0xDFFF, "LowSurrogates"}, + {0xE000, 0xF8FF, "PrivateUse"}, + {0xF900, 0xFAFF, "CJKCompatibilityIdeographs"}, + {0xFB00, 0xFB4F, "AlphabeticPresentationForms"}, + {0xFB50, 0xFDFF, "ArabicPresentationForms-A"}, + {0xFE20, 0xFE2F, "CombiningHalfMarks"}, + {0xFE30, 0xFE4F, "CJKCompatibilityForms"}, + {0xFE50, 0xFE6F, "SmallFormVariants"}, + {0xFE70, 0xFEFE, "ArabicPresentationForms-B"}, + {0xFEFF, 0xFEFF, "Specials"}, + {0xFF00, 0xFFEF, "HalfwidthandFullwidthForms"}, + {0xFFF0, 0xFFFD, "Specials"}, + {0, 0, NULL} +}; + +typedef struct CategoryCharRangeTag +{ + FLMUNICODE uzLowChar; + FLMUNICODE uzHighChar; +} CATEGORY_CHAR_RANGE; + +CATEGORY_CHAR_RANGE LuRanges[] = +{ + {0x0041, 0x005A}, + {0x00C0, 0x00DE}, + {0x0100, 0x0000}, + {0x0102, 0x0000}, + {0x0104, 0x0000}, + {0x0106, 0x0000}, + {0x0108, 0x0000}, + {0x010A, 0x0000}, + {0x010C, 0x0000}, + {0x010E, 0x0000}, + {0x0110, 0x0000}, + {0x0112, 0x0000}, + {0x0114, 0x0000}, + {0x0116, 0x0000}, + {0x0118, 0x0000}, + {0x011A, 0x0000}, + {0x011C, 0x0000}, + {0x011E, 0x0000}, + {0x0120, 0x0000}, + {0x0122, 0x0000}, + {0x0124, 0x0000}, + {0x0126, 0x0000}, + {0x0128, 0x0000}, + {0x012A, 0x0000}, + {0x012C, 0x0000}, + {0x012E, 0x0000}, + {0x0130, 0x0000}, + {0x0132, 0x0000}, + {0x0134, 0x0000}, + {0x0136, 0x0000}, + {0x0139, 0x0000}, + {0x013B, 0x0000}, + {0x013D, 0x0000}, + {0x013F, 0x0000}, + {0x0141, 0x0000}, + {0x0143, 0x0000}, + {0x0145, 0x0000}, + {0x0147, 0x0000}, + {0x014A, 0x0000}, + {0x014C, 0x0000}, + {0x014E, 0x0000}, + {0x0150, 0x0000}, + {0x0152, 0x0000}, + {0x0154, 0x0000}, + {0x0156, 0x0000}, + {0x0158, 0x0000}, + {0x015A, 0x0000}, + {0x015C, 0x0000}, + {0x015E, 0x0000}, + {0x0160, 0x0000}, + {0x0162, 0x0000}, + {0x0164, 0x0000}, + {0x0166, 0x0000}, + {0x0168, 0x0000}, + {0x016A, 0x0000}, + {0x016C, 0x0000}, + {0x016E, 0x0000}, + {0x0170, 0x0000}, + {0x0172, 0x0000}, + {0x0174, 0x0000}, + {0x0176, 0x0000}, + {0x0178, 0x0179}, + {0x017B, 0x0000}, + {0x017D, 0x0000}, + {0x0181, 0x0182}, + {0x0184, 0x0000}, + {0x0186, 0x0187}, + {0x0189, 0x018B}, + {0x018E, 0x0191}, + {0x0193, 0x0194}, + {0x0196, 0x0198}, + {0x019C, 0x019D}, + {0x019F, 0x01A0}, + {0x01A2, 0x0000}, + {0x01A4, 0x0000}, + {0x01A6, 0x01A7}, + {0x01A9, 0x01AC}, + {0x01AE, 0x01AF}, + {0x01B1, 0x01B3}, + {0x01B5, 0x0000}, + {0x01B7, 0x01B8}, + {0x01BC, 0x0000}, + {0x01C4, 0x0000}, + {0x01C7, 0x0000}, + {0x01CA, 0x0000}, + {0x01CD, 0x0000}, + {0x01CF, 0x0000}, + {0x01D1, 0x0000}, + {0x01D3, 0x0000}, + {0x01D5, 0x0000}, + {0x01D7, 0x0000}, + {0x01D9, 0x0000}, + {0x01DB, 0x0000}, + {0x01DE, 0x0000}, + {0x01E0, 0x0000}, + {0x01E2, 0x0000}, + {0x01E4, 0x0000}, + {0x01E6, 0x0000}, + {0x01E8, 0x0000}, + {0x01EA, 0x0000}, + {0x01EC, 0x0000}, + {0x01EE, 0x0000}, + {0x01F1, 0x0000}, + {0x01F4, 0x0000}, + {0x01F6, 0x01F8}, + {0x01FA, 0x0000}, + {0x01FC, 0x0000}, + {0x01FE, 0x0000}, + {0x0200, 0x0000}, + {0x0202, 0x0000}, + {0x0204, 0x0000}, + {0x0206, 0x0000}, + {0x0208, 0x0000}, + {0x020A, 0x0000}, + {0x020C, 0x0000}, + {0x020E, 0x0000}, + {0x0210, 0x0000}, + {0x0212, 0x0000}, + {0x0214, 0x0000}, + {0x0216, 0x0000}, + {0x0218, 0x0000}, + {0x021A, 0x0000}, + {0x021C, 0x0000}, + {0x021E, 0x0000}, + {0x0220, 0x0000}, + {0x0222, 0x0000}, + {0x0224, 0x0000}, + {0x0226, 0x0000}, + {0x0228, 0x0000}, + {0x022A, 0x0000}, + {0x022C, 0x0000}, + {0x022E, 0x0000}, + {0x0230, 0x0000}, + {0x0232, 0x0000}, + {0x0386, 0x0000}, + {0x0388, 0x038A}, + {0x038C, 0x0000}, + {0x038E, 0x038F}, + {0x0391, 0x03AB}, + {0x03D2, 0x03D4}, + {0x03D8, 0x0000}, + {0x03DA, 0x0000}, + {0x03DC, 0x0000}, + {0x03DE, 0x0000}, + {0x03E0, 0x0000}, + {0x03E2, 0x0000}, + {0x03E4, 0x0000}, + {0x03E6, 0x0000}, + {0x03E8, 0x0000}, + {0x03EA, 0x0000}, + {0x03EC, 0x0000}, + {0x03EE, 0x0000}, + {0x03F4, 0x0000}, + {0x03F7, 0x0000}, + {0x03F9, 0x0000}, + {0x03FA, 0x0000}, + {0x0400, 0x042F}, + {0x0460, 0x0000}, + {0x0462, 0x0000}, + {0x0464, 0x0000}, + {0x0466, 0x0000}, + {0x0468, 0x0000}, + {0x046A, 0x0000}, + {0x046C, 0x0000}, + {0x046E, 0x0000}, + {0x0470, 0x0000}, + {0x0472, 0x0000}, + {0x0474, 0x0000}, + {0x0476, 0x0000}, + {0x0478, 0x0000}, + {0x047A, 0x0000}, + {0x047C, 0x0000}, + {0x047E, 0x0000}, + {0x0480, 0x0000}, + {0x048A, 0x0000}, + {0x048C, 0x0000}, + {0x048E, 0x0000}, + {0x0490, 0x0000}, + {0x0492, 0x0000}, + {0x0494, 0x0000}, + {0x0496, 0x0000}, + {0x0498, 0x0000}, + {0x049A, 0x0000}, + {0x049C, 0x0000}, + {0x049E, 0x0000}, + {0x04A0, 0x0000}, + {0x04A2, 0x0000}, + {0x04A4, 0x0000}, + {0x04A6, 0x0000}, + {0x04A8, 0x0000}, + {0x04AA, 0x0000}, + {0x04AC, 0x0000}, + {0x04AE, 0x0000}, + {0x04B0, 0x0000}, + {0x04B2, 0x0000}, + {0x04B4, 0x0000}, + {0x04B6, 0x0000}, + {0x04B8, 0x0000}, + {0x04BA, 0x0000}, + {0x04BC, 0x0000}, + {0x04BE, 0x0000}, + {0x04C0, 0x04C1}, + {0x04C3, 0x0000}, + {0x04C5, 0x0000}, + {0x04C7, 0x0000}, + {0x04C9, 0x0000}, + {0x04CB, 0x0000}, + {0x04CD, 0x0000}, + {0x04D0, 0x0000}, + {0x04D2, 0x0000}, + {0x04D4, 0x0000}, + {0x04D6, 0x0000}, + {0x04D8, 0x0000}, + {0x04DA, 0x0000}, + {0x04DC, 0x0000}, + {0x04DE, 0x0000}, + {0x04E0, 0x0000}, + {0x04E2, 0x0000}, + {0x04E4, 0x0000}, + {0x04E6, 0x0000}, + {0x04E8, 0x0000}, + {0x04EA, 0x0000}, + {0x04EC, 0x0000}, + {0x04EE, 0x0000}, + {0x04F0, 0x0000}, + {0x04F2, 0x0000}, + {0x04F4, 0x0000}, + {0x04F8, 0x0000}, + {0x0500, 0x0000}, + {0x0502, 0x0000}, + {0x0504, 0x0000}, + {0x0506, 0x0000}, + {0x0508, 0x0000}, + {0x050A, 0x0000}, + {0x050C, 0x0000}, + {0x050E, 0x0000}, + {0x0531, 0x0556}, + {0x10A0, 0x10C5}, + {0x1E00, 0x0000}, + {0x1E02, 0x0000}, + {0x1E04, 0x0000}, + {0x1E06, 0x0000}, + {0x1E08, 0x0000}, + {0x1E0A, 0x0000}, + {0x1E0C, 0x0000}, + {0x1E0E, 0x0000}, + {0x1E10, 0x0000}, + {0x1E12, 0x0000}, + {0x1E14, 0x0000}, + {0x1E16, 0x0000}, + {0x1E18, 0x0000}, + {0x1E1A, 0x0000}, + {0x1E1C, 0x0000}, + {0x1E1E, 0x0000}, + {0x1E20, 0x0000}, + {0x1E22, 0x0000}, + {0x1E24, 0x0000}, + {0x1E26, 0x0000}, + {0x1E28, 0x0000}, + {0x1E2A, 0x0000}, + {0x1E2C, 0x0000}, + {0x1E2E, 0x0000}, + {0x1E30, 0x0000}, + {0x1E32, 0x0000}, + {0x1E34, 0x0000}, + {0x1E36, 0x0000}, + {0x1E38, 0x0000}, + {0x1E3A, 0x0000}, + {0x1E3C, 0x0000}, + {0x1E3E, 0x0000}, + {0x1E40, 0x0000}, + {0x1E42, 0x0000}, + {0x1E44, 0x0000}, + {0x1E46, 0x0000}, + {0x1E48, 0x0000}, + {0x1E4A, 0x0000}, + {0x1E4C, 0x0000}, + {0x1E4E, 0x0000}, + {0x1E50, 0x0000}, + {0x1E52, 0x0000}, + {0x1E54, 0x0000}, + {0x1E56, 0x0000}, + {0x1E58, 0x0000}, + {0x1E5A, 0x0000}, + {0x1E5C, 0x0000}, + {0x1E5E, 0x0000}, + {0x1E60, 0x0000}, + {0x1E62, 0x0000}, + {0x1E64, 0x0000}, + {0x1E66, 0x0000}, + {0x1E68, 0x0000}, + {0x1E6A, 0x0000}, + {0x1E6C, 0x0000}, + {0x1E6E, 0x0000}, + {0x1E70, 0x0000}, + {0x1E72, 0x0000}, + {0x1E74, 0x0000}, + {0x1E76, 0x0000}, + {0x1E78, 0x0000}, + {0x1E7A, 0x0000}, + {0x1E7C, 0x0000}, + {0x1E7E, 0x0000}, + {0x1E80, 0x0000}, + {0x1E82, 0x0000}, + {0x1E84, 0x0000}, + {0x1E86, 0x0000}, + {0x1E88, 0x0000}, + {0x1E8A, 0x0000}, + {0x1E8C, 0x0000}, + {0x1E8E, 0x0000}, + {0x1E90, 0x0000}, + {0x1E92, 0x0000}, + {0x1E94, 0x0000}, + {0x1EA0, 0x0000}, + {0x1EA2, 0x0000}, + {0x1EA4, 0x0000}, + {0x1EA6, 0x0000}, + {0x1EA8, 0x0000}, + {0x1EAA, 0x0000}, + {0x1EAC, 0x0000}, + {0x1EAE, 0x0000}, + {0x1EB0, 0x0000}, + {0x1EB2, 0x0000}, + {0x1EB4, 0x0000}, + {0x1EB6, 0x0000}, + {0x1EB8, 0x0000}, + {0x1EBA, 0x0000}, + {0x1EBC, 0x0000}, + {0x1EBE, 0x0000}, + {0x1EC0, 0x0000}, + {0x1EC2, 0x0000}, + {0x1EC4, 0x0000}, + {0x1EC6, 0x0000}, + {0x1EC8, 0x0000}, + {0x1ECA, 0x0000}, + {0x1ECC, 0x0000}, + {0x1ECE, 0x0000}, + {0x1ED0, 0x0000}, + {0x1ED2, 0x0000}, + {0x1ED4, 0x0000}, + {0x1ED6, 0x0000}, + {0x1ED8, 0x0000}, + {0x1EDA, 0x0000}, + {0x1EDC, 0x0000}, + {0x1EDE, 0x0000}, + {0x1EE0, 0x0000}, + {0x1EE2, 0x0000}, + {0x1EE4, 0x0000}, + {0x1EE6, 0x0000}, + {0x1EE8, 0x0000}, + {0x1EEA, 0x0000}, + {0x1EEC, 0x0000}, + {0x1EEE, 0x0000}, + {0x1EF0, 0x0000}, + {0x1EF2, 0x0000}, + {0x1EF4, 0x0000}, + {0x1EF6, 0x0000}, + {0x1EF8, 0x0000}, + {0x1F08, 0x1F0F}, + {0x1F18, 0x1F1D}, + {0x1F28, 0x1F2F}, + {0x1F38, 0x1F3F}, + {0x1F49, 0x1F4D}, + {0x1F59, 0x0000}, + {0x1F5B, 0x0000}, + {0x1F5D, 0x0000}, + {0x1F5F, 0x0000}, + {0x1F68, 0x1F6F}, + {0x1FB8, 0x1FBB}, + {0x1FC8, 0x1FCB}, + {0x1FD8, 0x1FDB}, + {0x1FE8, 0x1FEC}, + {0x1FF8, 0x1FFB}, + {0x2102, 0x0000}, + {0x2107, 0x0000}, + {0x210B, 0x210D}, + {0x2110, 0x2112}, + {0x2115, 0x0000}, + {0x2119, 0x211D}, + {0x2124, 0x0000}, + {0x2126, 0x0000}, + {0x2128, 0x0000}, + {0x212A, 0x212D}, + {0x2130, 0x2131}, + {0x2133, 0x0000}, + {0x213E, 0x213F}, + {0x2145, 0x0000}, + {0xFF21, 0xFF3A} +}; + +CATEGORY_CHAR_RANGE LlRanges[] = +{ + {0x0061, 0x007A}, + {0x00AA, 0x0000}, + {0x00B5, 0x0000}, + {0x00BA, 0x0000}, + {0x00DF, 0x0000}, + {0x00E0, 0x00FF}, + {0x0101, 0x0000}, + {0x0103, 0x0000}, + {0x0105, 0x0000}, + {0x0107, 0x0000}, + {0x0109, 0x0000}, + {0x010B, 0x0000}, + {0x010D, 0x0000}, + {0x010F, 0x0000}, + {0x0111, 0x0000}, + {0x0113, 0x0000}, + {0x0115, 0x0000}, + {0x0117, 0x0000}, + {0x0119, 0x0000}, + {0x011B, 0x0000}, + {0x011D, 0x0000}, + {0x011F, 0x0000}, + {0x0121, 0x0000}, + {0x0123, 0x0000}, + {0x0125, 0x0000}, + {0x0127, 0x0000}, + {0x0129, 0x0000}, + {0x012B, 0x0000}, + {0x012D, 0x0000}, + {0x012F, 0x0000}, + {0x0131, 0x0000}, + {0x0133, 0x0000}, + {0x0135, 0x0000}, + {0x0137, 0x0138}, + {0x013A, 0x0000}, + {0x013C, 0x0000}, + {0x013E, 0x0000}, + {0x0140, 0x0000}, + {0x0142, 0x0000}, + {0x0144, 0x0000}, + {0x0146, 0x0000}, + {0x0148, 0x0149}, + {0x014B, 0x0000}, + {0x014D, 0x0000}, + {0x014F, 0x0000}, + {0x0151, 0x0000}, + {0x0153, 0x0000}, + {0x0155, 0x0000}, + {0x0157, 0x0000}, + {0x0159, 0x0000}, + {0x015B, 0x0000}, + {0x015D, 0x0000}, + {0x015F, 0x0000}, + {0x0161, 0x0000}, + {0x0163, 0x0000}, + {0x0165, 0x0000}, + {0x0167, 0x0000}, + {0x0169, 0x0000}, + {0x016B, 0x0000}, + {0x016D, 0x0000}, + {0x016F, 0x0000}, + {0x0171, 0x0000}, + {0x0173, 0x0000}, + {0x0175, 0x0000}, + {0x0177, 0x0000}, + {0x017A, 0x0000}, + {0x017C, 0x0000}, + {0x017E, 0x017F}, + {0x0180, 0x0000}, + {0x0183, 0x0000}, + {0x0185, 0x0000}, + {0x0188, 0x0000}, + {0x018C, 0x018D}, + {0x0192, 0x0000}, + {0x0195, 0x0000}, + {0x0199, 0x019B}, + {0x019E, 0x0000}, + {0x01A1, 0x0000}, + {0x01A3, 0x0000}, + {0x01A5, 0x0000}, + {0x01A8, 0x0000}, + {0x01AA, 0x01AB}, + {0x01AD, 0x0000}, + {0x01B0, 0x0000}, + {0x01B4, 0x0000}, + {0x01B6, 0x0000}, + {0x01B9, 0x01BA}, + {0x01BD, 0x01BF}, + {0x01C6, 0x0000}, + {0x01C9, 0x0000}, + {0x01CC, 0x0000}, + {0x01CE, 0x0000}, + {0x01D2, 0x0000}, + {0x01D4, 0x0000}, + {0x01D6, 0x0000}, + {0x01D8, 0x0000}, + {0x01DA, 0x0000}, + {0x01DC, 0x01DD}, + {0x01DF, 0x0000}, + {0x01E1, 0x0000}, + {0x01E3, 0x0000}, + {0x01E5, 0x0000}, + {0x01E7, 0x0000}, + {0x01E9, 0x0000}, + {0x01EB, 0x0000}, + {0x01ED, 0x0000}, + {0x01EF, 0x0000}, + {0x01F0, 0x0000}, + {0x01F3, 0x0000}, + {0x01F5, 0x0000}, + {0x01F9, 0x0000}, + {0x01FB, 0x0000}, + {0x01FD, 0x0000}, + {0x01FF, 0x0000}, + {0x0201, 0x0000}, + {0x0203, 0x0000}, + {0x0205, 0x0000}, + {0x0207, 0x0000}, + {0x0209, 0x0000}, + {0x020B, 0x0000}, + {0x020D, 0x0000}, + {0x020F, 0x0000}, + {0x0211, 0x0000}, + {0x0213, 0x0000}, + {0x0215, 0x0000}, + {0x0217, 0x0000}, + {0x0219, 0x0000}, + {0x021B, 0x0000}, + {0x021D, 0x0000}, + {0x021F, 0x0000}, + {0x0221, 0x0000}, + {0x0223, 0x0000}, + {0x0225, 0x0000}, + {0x0227, 0x0000}, + {0x0229, 0x0000}, + {0x022B, 0x0000}, + {0x022D, 0x0000}, + {0x022F, 0x0000}, + {0x0231, 0x0000}, + {0x0233, 0x0236}, + {0x0234, 0x0000}, + {0x0250, 0x0000}, + {0x0209, 0x0000}, + {0x020B, 0x0000}, + {0x020D, 0x0000}, + {0x020F, 0x02AF}, + {0x0390, 0x0000}, + {0x03AC, 0x03CE}, + {0x03D0, 0x03D1}, + {0x03D5, 0x03D7}, + {0x03D9, 0x0000}, + {0x03DB, 0x0000}, + {0x03DD, 0x0000}, + {0x03DF, 0x0000}, + {0x03E1, 0x0000}, + {0x03E3, 0x0000}, + {0x03E5, 0x0000}, + {0x03E7, 0x0000}, + {0x03E9, 0x0000}, + {0x03EB, 0x0000}, + {0x03ED, 0x0000}, + {0x03EF, 0x03F3}, + {0x03F5, 0x0000}, + {0x03F8, 0x0000}, + {0x03FB, 0x0000}, + {0x0430, 0x045F}, + {0x0461, 0x0000}, + {0x0463, 0x0000}, + {0x0465, 0x0000}, + {0x0467, 0x0000}, + {0x0469, 0x0000}, + {0x046B, 0x0000}, + {0x046D, 0x0000}, + {0x046F, 0x0000}, + {0x0471, 0x0000}, + {0x0473, 0x0000}, + {0x0475, 0x0000}, + {0x0477, 0x0000}, + {0x0479, 0x0000}, + {0x047B, 0x0000}, + {0x047D, 0x0000}, + {0x047F, 0x0000}, + {0x0481, 0x0000}, + {0x048B, 0x0000}, + {0x048D, 0x0000}, + {0x048F, 0x0000}, + {0x0491, 0x0000}, + {0x0493, 0x0000}, + {0x0495, 0x0000}, + {0x0497, 0x0000}, + {0x0499, 0x0000}, + {0x049B, 0x0000}, + {0x049D, 0x0000}, + {0x049F, 0x0000}, + {0x04A1, 0x0000}, + {0x04A3, 0x0000}, + {0x04A5, 0x0000}, + {0x04A7, 0x0000}, + {0x04A9, 0x0000}, + {0x04AB, 0x0000}, + {0x04AD, 0x0000}, + {0x04AF, 0x0000}, + {0x04B1, 0x0000}, + {0x04B3, 0x0000}, + {0x04B5, 0x0000}, + {0x04B7, 0x0000}, + {0x04B9, 0x0000}, + {0x04BB, 0x0000}, + {0x04BD, 0x0000}, + {0x04BF, 0x0000}, + {0x04C2, 0x0000}, + {0x04C4, 0x0000}, + {0x04C6, 0x0000}, + {0x04C8, 0x0000}, + {0x04CA, 0x0000}, + {0x04CC, 0x0000}, + {0x04CE, 0x0000}, + {0x04D1, 0x0000}, + {0x04D3, 0x0000}, + {0x04D5, 0x0000}, + {0x04D7, 0x0000}, + {0x04D9, 0x0000}, + {0x04DB, 0x0000}, + {0x04DD, 0x0000}, + {0x04DF, 0x0000}, + {0x04E1, 0x0000}, + {0x04E3, 0x0000}, + {0x04E5, 0x0000}, + {0x04E7, 0x0000}, + {0x04E9, 0x0000}, + {0x04EB, 0x0000}, + {0x04ED, 0x0000}, + {0x04EF, 0x0000}, + {0x04F1, 0x0000}, + {0x04F3, 0x0000}, + {0x04F5, 0x0000}, + {0x04F9, 0x0000}, + {0x0501, 0x0000}, + {0x0503, 0x0000}, + {0x0505, 0x0000}, + {0x0507, 0x0000}, + {0x0509, 0x0000}, + {0x050B, 0x0000}, + {0x050D, 0x0000}, + {0x050F, 0x0000}, + {0x0561, 0x0587}, + {0x1D00, 0x1D2B}, + {0x1D62, 0x1D6B}, + {0x1E01, 0x0000}, + {0x1E03, 0x0000}, + {0x1E05, 0x0000}, + {0x1E07, 0x0000}, + {0x1E09, 0x0000}, + {0x1E0B, 0x0000}, + {0x1E0D, 0x0000}, + {0x1E0F, 0x0000}, + {0x1E11, 0x0000}, + {0x1E13, 0x0000}, + {0x1E15, 0x0000}, + {0x1E17, 0x0000}, + {0x1E19, 0x0000}, + {0x1E1B, 0x0000}, + {0x1E1D, 0x0000}, + {0x1E1F, 0x0000}, + {0x1E21, 0x0000}, + {0x1E23, 0x0000}, + {0x1E25, 0x0000}, + {0x1E27, 0x0000}, + {0x1E29, 0x0000}, + {0x1E2B, 0x0000}, + {0x1E2D, 0x0000}, + {0x1E2F, 0x0000}, + {0x1E31, 0x0000}, + {0x1E33, 0x0000}, + {0x1E35, 0x0000}, + {0x1E37, 0x0000}, + {0x1E39, 0x0000}, + {0x1E3B, 0x0000}, + {0x1E3D, 0x0000}, + {0x1E3F, 0x0000}, + {0x1E41, 0x0000}, + {0x1E43, 0x0000}, + {0x1E45, 0x0000}, + {0x1E47, 0x0000}, + {0x1E49, 0x0000}, + {0x1E4B, 0x0000}, + {0x1E4D, 0x0000}, + {0x1E4F, 0x0000}, + {0x1E51, 0x0000}, + {0x1E53, 0x0000}, + {0x1E55, 0x0000}, + {0x1E57, 0x0000}, + {0x1E59, 0x0000}, + {0x1E5B, 0x0000}, + {0x1E5D, 0x0000}, + {0x1E5F, 0x0000}, + {0x1E61, 0x0000}, + {0x1E63, 0x0000}, + {0x1E65, 0x0000}, + {0x1E67, 0x0000}, + {0x1E69, 0x0000}, + {0x1E6B, 0x0000}, + {0x1E6D, 0x0000}, + {0x1E6F, 0x0000}, + {0x1E71, 0x0000}, + {0x1E73, 0x0000}, + {0x1E75, 0x0000}, + {0x1E77, 0x0000}, + {0x1E79, 0x0000}, + {0x1E7B, 0x0000}, + {0x1E7D, 0x0000}, + {0x1E7F, 0x0000}, + {0x1E81, 0x0000}, + {0x1E83, 0x0000}, + {0x1E85, 0x0000}, + {0x1E87, 0x0000}, + {0x1E89, 0x0000}, + {0x1E8B, 0x0000}, + {0x1E8D, 0x0000}, + {0x1E8F, 0x0000}, + {0x1E91, 0x0000}, + {0x1E93, 0x0000}, + {0x1E95, 0x1E9B}, + {0x1EA1, 0x0000}, + {0x1EA3, 0x0000}, + {0x1EA5, 0x0000}, + {0x1EA7, 0x0000}, + {0x1EA9, 0x0000}, + {0x1EAB, 0x0000}, + {0x1EAD, 0x0000}, + {0x1EAF, 0x0000}, + {0x1EB1, 0x0000}, + {0x1EB3, 0x0000}, + {0x1EB5, 0x0000}, + {0x1EB7, 0x0000}, + {0x1EB9, 0x0000}, + {0x1EBB, 0x0000}, + {0x1EBD, 0x0000}, + {0x1EBF, 0x0000}, + {0x1EC1, 0x0000}, + {0x1EC3, 0x0000}, + {0x1EC5, 0x0000}, + {0x1EC7, 0x0000}, + {0x1EC9, 0x0000}, + {0x1ECB, 0x0000}, + {0x1ECD, 0x0000}, + {0x1ECF, 0x0000}, + {0x1ED1, 0x0000}, + {0x1ED3, 0x0000}, + {0x1ED5, 0x0000}, + {0x1ED7, 0x0000}, + {0x1ED9, 0x0000}, + {0x1EDB, 0x0000}, + {0x1EDD, 0x0000}, + {0x1EDF, 0x0000}, + {0x1EE1, 0x0000}, + {0x1EE3, 0x0000}, + {0x1EE5, 0x0000}, + {0x1EE7, 0x0000}, + {0x1EE9, 0x0000}, + {0x1EEB, 0x0000}, + {0x1EED, 0x0000}, + {0x1EEF, 0x0000}, + {0x1EF1, 0x0000}, + {0x1EF3, 0x0000}, + {0x1EF5, 0x0000}, + {0x1EF7, 0x0000}, + {0x1EF9, 0x0000}, + {0x1F00, 0x1F07}, + {0x1F10, 0x1F15}, + {0x1F20, 0x1F27}, + {0x1F30, 0x1F37}, + {0x1F40, 0x1F45}, + {0x1F50, 0x1F57}, + {0x1F60, 0x1F67}, + {0x1F70, 0x1F7D}, + {0x1F80, 0x1F87}, + {0x1F90, 0x1F97}, + {0x1FA0, 0x1FA7}, + {0x1FB0, 0x1FB7}, + {0x1FBE, 0x0000}, + {0x1FC2, 0x1FC4}, + {0x1FC6, 0x1FC7}, + {0x1FD0, 0x1FD7}, + {0x1FE0, 0x1FE7}, + {0x1FF2, 0x1FF4}, + {0x1FF6, 0x1FF7}, + {0x2071, 0x0000}, + {0x207F, 0x0000}, + {0x210A, 0x0000}, + {0x210E, 0x210F}, + {0x2113, 0x0000}, + {0x212F, 0x0000}, + {0x2134, 0x0000}, + {0x2139, 0x0000}, + {0x213D, 0x0000}, + {0x2146, 0x2149}, + {0xFB00, 0xFB06}, + {0xFB13, 0xFB17}, + {0xFF41, 0xFF5A}, + {0x1E0B, 0x0000}, + {0x1E0D, 0x0000}, + {0x1E0F, 0x0000} +}; + +CATEGORY_CHAR_RANGE LtRanges[] = +{ + {0x01C5, 0x0000}, + {0x01C8, 0x0000}, + {0x01CB, 0x0000}, + {0x01F2, 0x0000}, + {0x1F88, 0x1F8F}, + {0x1F98, 0x1F9F}, + {0x1FA8, 0x1FAF}, + {0x1FBC, 0x0000}, + {0x1FCC, 0x0000}, + {0x1FFC, 0x0000} +}; + +CATEGORY_CHAR_RANGE LmRanges[] = +{ + {0x02B0, 0x02C1}, + {0x02C6, 0x02D1}, + {0x02E0, 0x02E4}, + {0x02EE, 0x0000}, + {0x037A, 0x0000}, + {0x0559, 0x0000}, + {0x0640, 0x0000}, + {0x06E5, 0x06E6}, + {0x0E46, 0x0000}, + {0x0EC6, 0x0000}, + {0x17D7, 0x0000}, + {0x1843, 0x0000}, + {0x1D2C, 0x1D61}, + {0x3005, 0x0000}, + {0x3031, 0x3035}, + {0x303B, 0x0000}, + {0x309D, 0x309E}, + {0x30FC, 0x30FE}, + {0xFF70, 0x0000}, + {0xFF9E, 0xFF9F} +}; + +CATEGORY_CHAR_RANGE LoRanges[] = +{ + {0x01BB, 0x0000}, + {0x01C0, 0x01C3}, + {0x05D0, 0x05EA}, + {0x05F0, 0x05F2}, + {0x0621, 0x063A}, + {0x0641, 0x064A}, + {0x066E, 0x066F}, + {0x0671, 0x06D3}, + {0x06D5, 0x0000}, + {0x06EE, 0x06EF}, + {0x06FA, 0x06FC}, + {0x06FF, 0x0000}, + {0x0710, 0x0000}, + {0x0712, 0x072F}, + {0x074D, 0x074F}, + {0x0780, 0x07A5}, + {0x07B1, 0x0000}, + {0x0904, 0x0939}, + {0x093D, 0x0000}, + {0x0950, 0x0000}, + {0x0958, 0x0961}, + {0x0985, 0x098C}, + {0x098F, 0x0990}, + {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, + {0x09B2, 0x0000}, + {0x09B6, 0x09B9}, + {0x09BD, 0x0000}, + {0x09DC, 0x09DD}, + {0x09DF, 0x09E1}, + {0x09F0, 0x09F1}, + {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, + {0x0A13, 0x0A28}, + {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, + {0x0A35, 0x0A36}, + {0x0A38, 0x0A39}, + {0x0A59, 0x0A5C}, + {0x0A5E, 0x0000}, + {0x0A72, 0x0A74}, + {0x0A85, 0x0A8D}, + {0x0A8F, 0x0A91}, + {0x0A93, 0x0AB0}, + {0x0AB2, 0x0AB3}, + {0x0AB5, 0x0AB9}, + {0x0ABD, 0x0000}, + {0x0AD0, 0x0000}, + {0x0AE0, 0x0AE1}, + {0x0B05, 0x0B0C}, + {0x0B0F, 0x0B10}, + {0x0B13, 0x0B30}, + {0x0B32, 0x0B33}, + {0x0B35, 0x0B39}, + {0x0B3D, 0x0000}, + {0x0B5C, 0x0B5D}, + {0x0B5F, 0x0B61}, + {0x0B71, 0x0000}, + {0x0B83, 0x0000}, + {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, + {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, + {0x0B9C, 0x0000}, + {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, + {0x0BA8, 0x0BAA}, + {0x0BAE, 0x0BAF}, + {0x0BB0, 0x0BB9}, + {0x0C05, 0x0C0C}, + {0x0C0E, 0x0C10}, + {0x0C12, 0x0C28}, + {0x0C2A, 0x0C33}, + {0x0C35, 0x0C39}, + {0x0C60, 0x0C61}, + {0x0C85, 0x0C8C}, + {0x0C8E, 0x0C90}, + {0x0C92, 0x0CB3}, + {0x0CB5, 0x0CB9}, + {0x0CBD, 0x0000}, + {0x0CDE, 0x0000}, + {0x0CE0, 0x0CE1}, + {0x0D05, 0x0D0C}, + {0x0D0E, 0x0D10}, + {0x0D12, 0x0D28}, + {0x0D2A, 0x0D39}, + {0x0D60, 0x0D61}, + {0x0D85, 0x0D96}, + {0x0D9A, 0x0DB1}, + {0x0DB3, 0x0DBB}, + {0x0DBD, 0x0000}, + {0x0DC0, 0x0DC6}, + {0x0E01, 0x0E30}, + {0x0E32, 0x0E33}, + {0x0E40, 0x0E45}, + {0x0E81, 0x0E82}, + {0x0E84, 0x0000}, + {0x0E87, 0x0E88}, + {0x0E8A, 0x0000}, + {0x0E8D, 0x0000}, + {0x0E94, 0x0E97}, + {0x0E99, 0x0E9F}, + {0x0EA1, 0x0EA3}, + {0x0EA5, 0x0000}, + {0x0EA7, 0x0000}, + {0x0EAA, 0x0EAB}, + {0x0EAD, 0x0EB0}, + {0x0EB2, 0x0EB3}, + {0x0EBD, 0x0000}, + {0x0EC0, 0x0EC4}, + {0x0EDC, 0x0EDD}, + {0x0F00, 0x0000}, + {0x0F40, 0x0F47}, + {0x0F49, 0x0F6A}, + {0x0F88, 0x0F8B}, + {0x1000, 0x1021}, + {0x1023, 0x1027}, + {0x1029, 0x102A}, + {0x1050, 0x1055}, + {0x10D0, 0x10F8}, + {0x1100, 0x11A2}, + {0x11A8, 0x11F9}, + {0x1200, 0x1248}, + {0x124A, 0x124D}, + {0x1250, 0x1256}, + {0x1258, 0x0000}, + {0x125A, 0x125D}, + {0x1260, 0x1286}, + {0x1288, 0x0000}, + {0x128A, 0x128D}, + {0x1290, 0x12AE}, + {0x12B0, 0x0000}, + {0x12B2, 0x12B5}, + {0x12B8, 0x12BE}, + {0x12C0, 0x0000}, + {0x12C2, 0x12C5}, + {0x12C8, 0x12CE}, + {0x12D0, 0x12D6}, + {0x12D8, 0x12EE}, + {0x12F0, 0x130E}, + {0x1310, 0x0000}, + {0x1312, 0x1315}, + {0x1318, 0x131E}, + {0x1320, 0x1346}, + {0x1348, 0x135A}, + {0x13A0, 0x13F4}, + {0x1401, 0x166C}, + {0x166F, 0x1676}, + {0x1681, 0x169A}, + {0x16A0, 0x16EA}, + {0x1700, 0x170C}, + {0x170E, 0x1711}, + {0x1720, 0x1731}, + {0x1740, 0x1751}, + {0x1760, 0x176C}, + {0x176E, 0x1770}, + {0x1780, 0x17B3}, + {0x17DC, 0x0000}, + {0x1820, 0x1842}, + {0x1844, 0x1877}, + {0x1880, 0x18A8}, + {0x1900, 0x191C}, + {0x1950, 0x196D}, + {0x1970, 0x1974}, + {0x2135, 0x2138}, + {0x3006, 0x0000}, + {0x303C, 0x0000}, + {0x3041, 0x3096}, + {0x309F, 0x0000}, + {0x30A1, 0x30FA}, + {0x30FF, 0x0000}, + {0x3105, 0x312C}, + {0x3131, 0x318E}, + {0x31A0, 0x31B7}, + {0x31F0, 0x31FF}, + {0x3400, 0x0000}, + {0x4DB5, 0x0000}, + {0x4E00, 0x0000}, + {0x9FA5, 0x0000}, + {0xA000, 0xA48C}, + {0xAC00, 0x0000}, + {0xD7A3, 0x0000}, + {0xF900, 0xFA2D}, + {0xFA30, 0xFA6A}, + {0xFB1D, 0x0000}, + {0xFB1F, 0xFB28}, + {0xFB2A, 0xFB36}, + {0xFB38, 0xFB3C}, + {0xFB3E, 0x0000}, + {0xFB40, 0xFB41}, + {0xFB43, 0xFB44}, + {0xFB46, 0xFBB1}, + {0xFBD3, 0xFD3D}, + {0xFD50, 0xFD8F}, + {0xFD92, 0xFDC7}, + {0xFDF0, 0xFDFB}, + {0xFE70, 0xFE74}, + {0xFE76, 0xFEFC}, + {0xFF66, 0xFF6F}, + {0xFF71, 0xFF9D}, + {0xFFA0, 0xFFBE}, + {0xFFC2, 0xFFC7}, + {0xFFCA, 0xFFCF}, + {0xFFD2, 0xFFD7}, + {0xFFDA, 0xFFDC} +}; + +CATEGORY_CHAR_RANGE MnRanges[] = +{ + {0x0300, 0x0357}, + {0x035D, 0x036F}, + {0x0483, 0x0486}, + {0x0591, 0x05A1}, + {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, + {0x05BF, 0x0000}, + {0x05C1, 0x05C2}, + {0x05C4, 0x0000}, + {0x0610, 0x0615}, + {0x064B, 0x0658}, + {0x0670, 0x0000}, + {0x06D6, 0x06DC}, + {0x06DF, 0x06E4}, + {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, + {0x0711, 0x0000}, + {0x0730, 0x074A}, + {0x07A6, 0x07B0}, + {0x0901, 0x0902}, + {0x093C, 0x0000}, + {0x0941, 0x0948}, + {0x094D, 0x0000}, + {0x0951, 0x0954}, + {0x0962, 0x0963}, + {0x0981, 0x0000}, + {0x09BC, 0x0000}, + {0x09C1, 0x09C4}, + {0x09CD, 0x0000}, + {0x09E2, 0x09E3}, + {0x0A01, 0x0A02}, + {0x0A3C, 0x0000}, + {0x0A41, 0x0A42}, + {0x0A47, 0x0A48}, + {0x0A4B, 0x0A4D}, + {0x0A70, 0x0A71}, + {0x0A81, 0x0A82}, + {0x0ABC, 0x0000}, + {0x0AC1, 0x0AC5}, + {0x0AC7, 0x0AC8}, + {0x0ACD, 0x0000}, + {0x0AE2, 0x0AE3}, + {0x0B01, 0x0000}, + {0x0B3C, 0x0000}, + {0x0B3F, 0x0000}, + {0x0B41, 0x0B43}, + {0x0B4D, 0x0000}, + {0x0B56, 0x0000}, + {0x0B82, 0x0000}, + {0x0BC0, 0x0000}, + {0x0BCD, 0x0000}, + {0x0C3E, 0x0C40}, + {0x0C46, 0x0C48}, + {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, + {0x0CBC, 0x0000}, + {0x0CBF, 0x0000}, + {0x0CC6, 0x0000}, + {0x0CCC, 0x0CCD}, + {0x0D41, 0x0D43}, + {0x0D4D, 0x0000}, + {0x0DCA, 0x0000}, + {0x0DD2, 0x0DD4}, + {0x0DD6, 0x0000}, + {0x0E31, 0x0000}, + {0x0E34, 0x0E3A}, + {0x0E47, 0x0E4E}, + {0x0EB1, 0x0000}, + {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, + {0x0F35, 0x0000}, + {0x0F37, 0x0000}, + {0x0F39, 0x0000}, + {0x0F71, 0x0F7E}, + {0x0F80, 0x0F84}, + {0x0F86, 0x0F87}, + {0x0F90, 0x0F97}, + {0x0F99, 0x0FBC}, + {0x0FC6, 0x0000}, + {0x102D, 0x1030}, + {0x1032, 0x0000}, + {0x1036, 0x1037}, + {0x1039, 0x0000}, + {0x1058, 0x1059}, + {0x1712, 0x1714}, + {0x1732, 0x1734}, + {0x1752, 0x1753}, + {0x1772, 0x1773}, + {0x17B7, 0x17BD}, + {0x17C6, 0x0000}, + {0x17C9, 0x17D3}, + {0x17DD, 0x0000}, + {0x180B, 0x180D}, + {0x18A9, 0x0000}, + {0x1920, 0x1922}, + {0x1927, 0x1928}, + {0x1932, 0x0000}, + {0x1939, 0x193B}, + {0x20D0, 0x20DC}, + {0x20E1, 0x0000}, + {0x20E5, 0x20EA}, + {0x302A, 0x302F}, + {0x3099, 0x309A}, + {0xFB1E, 0x0000}, + {0xFE00, 0xFE0F}, + {0xFE20, 0xFE23} +}; + +CATEGORY_CHAR_RANGE McRanges[] = +{ + {0x0903, 0x0000}, + {0x093E, 0x0940}, + {0x0949, 0x094C}, + {0x0982, 0x0983}, + {0x09BE, 0x09C0}, + {0x09C7, 0x09C8}, + {0x09CB, 0x09CC}, + {0x09D7, 0x0000}, + {0x0A03, 0x0000}, + {0x0A3E, 0x0A40}, + {0x0A83, 0x0000}, + {0x0ABE, 0x0AC0}, + {0x0AC9, 0x0000}, + {0x0ACB, 0x0ACC}, + {0x0B02, 0x0B03}, + {0x0BE3, 0x0000}, + {0x0B40, 0x0000}, + {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4C}, + {0x0B57, 0x0000}, + {0x0BBE, 0x0BBF}, + {0x0BC1, 0x0BC2}, + {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCC}, + {0x0BD7, 0x0000}, + {0x0C01, 0x0C03}, + {0x0C41, 0x0C44}, + {0x0C82, 0x0C83}, + {0x0CBE, 0x0000}, + {0x0CC0, 0x0CC4}, + {0x0CC7, 0x0CC8}, + {0x0CCA, 0x0CCB}, + {0x0CD5, 0x0CD6}, + {0x0D02, 0x0D03}, + {0x0D3E, 0x0D40}, + {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4C}, + {0x0D57, 0x0000}, + {0x0D82, 0x0D83}, + {0x0DCF, 0x0DD1}, + {0x0DD8, 0x0DDF}, + {0x0DF2, 0x0DF3}, + {0x0F3E, 0x0F3F}, + {0x0F7F, 0x0000}, + {0x102C, 0x0000}, + {0x1031, 0x0000}, + {0x1038, 0x0000}, + {0x1056, 0x1057}, + {0x17B6, 0x0000}, + {0x17BE, 0x17C5}, + {0x17C7, 0x17C8}, + {0x1923, 0x1926}, + {0x1929, 0x192B}, + {0x1930, 0x1931}, + {0x1933, 0x1938} +}; + +CATEGORY_CHAR_RANGE MeRanges[] = +{ + {0x0488, 0x0489}, + {0x06DE, 0x0000}, + {0x20DD, 0x20DF}, + {0x20E0, 0x0000}, + {0x20E2, 0x20E4} +}; + +CATEGORY_CHAR_RANGE DigitRanges[] = // Part 1 of Nd category +{ + {0x0030, 0x0039}, + {0x0660, 0x0669}, + {0x06F0, 0x06F9}, + {0x0966, 0x096F}, + {0x09E6, 0x09EF}, + {0x0A66, 0x0A6F}, + {0x0AE6, 0x0AEF}, + {0x0B66, 0x0B6F}, + {0x0BE7, 0x0BEF}, + {0x0C66, 0x0C6F}, + {0x0CE6, 0x0CEF}, + {0x0D66, 0x0D6F}, + {0x0E50, 0x0E59}, + {0x0ED0, 0x0ED9}, + {0x0F20, 0x0F29} +}; + +CATEGORY_CHAR_RANGE Nd2Ranges[] = // Part 2 of Nd category +{ + {0x1040, 0x1049}, + {0x1369, 0x1371}, + {0x17E0, 0x17E9}, + {0x1810, 0x1819}, + {0x1946, 0x194F}, + {0xFF10, 0xFF19} +}; + +CATEGORY_CHAR_RANGE NlRanges[] = +{ + {0x16EE, 0x16F0}, + {0x2160, 0x2183}, + {0x3007, 0x0000}, + {0x3021, 0x3029}, + {0x3038, 0x303A} +}; + +CATEGORY_CHAR_RANGE NoRanges[] = +{ + {0x00B2, 0x00B3}, + {0x00B9, 0x0000}, + {0x00BC, 0x00BE}, + {0x09F4, 0x09F9}, + {0x0BF0, 0x0BF2}, + {0x0F2A, 0x0F33}, + {0x1372, 0x137C}, + {0x17F0, 0x17F9}, + {0x2070, 0x0000}, + {0x2074, 0x2079}, + {0x2080, 0x2089}, + {0x2153, 0x215F}, + {0x2460, 0x249B}, + {0x24EA, 0x24FF}, + {0x2776, 0x2793}, + {0x3192, 0x3195}, + {0x3220, 0x3229}, + {0x3251, 0x325F}, + {0x3280, 0x3289}, + {0x32B1, 0x32BF} +}; + +CATEGORY_CHAR_RANGE PcRanges[] = +{ + {0x005F, 0x0000}, + {0x203F, 0x2040}, + {0x2054, 0x0000}, + {0x30FB, 0x0000}, + {0xFE33, 0xFE34}, + {0xFE4D, 0xFE4F}, + {0xFF3F, 0x0000}, + {0xFF65, 0x0000} +}; + +CATEGORY_CHAR_RANGE PdRanges[] = +{ + {0x002D, 0x0000}, + {0x058A, 0x0000}, + {0x1806, 0x0000}, + {0x2010, 0x2015}, + {0x301C, 0x0000}, + {0x3030, 0x0000}, + {0x30A0, 0x0000}, + {0xFE31, 0xFE32}, + {0xFE58, 0x0000}, + {0xFE63, 0x0000}, + {0xFF0D, 0x0000} +}; + +CATEGORY_CHAR_RANGE PsRanges[] = +{ + {0x0028, 0x0000}, + {0x005B, 0x0000}, + {0x007B, 0x0000}, + {0x0F3A, 0x0000}, + {0x0F3C, 0x0000}, + {0x169B, 0x0000}, + {0x201A, 0x0000}, + {0x201E, 0x0000}, + {0x2045, 0x0000}, + {0x207D, 0x0000}, + {0x208D, 0x0000}, + {0x2329, 0x0000}, + {0x23B4, 0x0000}, + {0x2768, 0x0000}, + {0x276A, 0x0000}, + {0x276C, 0x0000}, + {0x276E, 0x0000}, + {0x2770, 0x0000}, + {0x2772, 0x0000}, + {0x2774, 0x0000}, + {0x27E6, 0x0000}, + {0x27E8, 0x0000}, + {0x27EA, 0x0000}, + {0x2983, 0x0000}, + {0x2985, 0x0000}, + {0x2987, 0x0000}, + {0x2989, 0x0000}, + {0x298B, 0x0000}, + {0x298D, 0x0000}, + {0x298F, 0x0000}, + {0x2991, 0x0000}, + {0x2993, 0x0000}, + {0x2995, 0x0000}, + {0x2997, 0x0000}, + {0x29D8, 0x0000}, + {0x29DA, 0x0000}, + {0x29FC, 0x0000}, + {0x3008, 0x0000}, + {0x300A, 0x0000}, + {0x300C, 0x0000}, + {0x300E, 0x0000}, + {0x3010, 0x0000}, + {0x3014, 0x0000}, + {0x3016, 0x0000}, + {0x3018, 0x0000}, + {0x301A, 0x0000}, + {0x301D, 0x0000}, + {0xFD3E, 0x0000}, + {0xFE35, 0x0000}, + {0xFE37, 0x0000}, + {0xFE39, 0x0000}, + {0xFE3B, 0x0000}, + {0xFE3D, 0x0000}, + {0xFE3F, 0x0000}, + {0xFE41, 0x0000}, + {0xFE43, 0x0000}, + {0xFE47, 0x0000}, + {0xFE59, 0x0000}, + {0xFE5B, 0x0000}, + {0xFE5D, 0x0000}, + {0xFF08, 0x0000}, + {0xFF3B, 0x0000}, + {0xFF5B, 0x0000}, + {0xFF5F, 0x0000}, + {0xFF62, 0x0000} +}; + +CATEGORY_CHAR_RANGE PeRanges[] = +{ + {0x0029, 0x0000}, + {0x005D, 0x0000}, + {0x007D, 0x0000}, + {0x0F3B, 0x0000}, + {0x0F3D, 0x0000}, + {0x169C, 0x0000}, + {0x2046, 0x0000}, + {0x207E, 0x0000}, + {0x208E, 0x0000}, + {0x232A, 0x0000}, + {0x23B5, 0x0000}, + {0x2769, 0x0000}, + {0x276B, 0x0000}, + {0x276D, 0x0000}, + {0x276F, 0x0000}, + {0x2771, 0x0000}, + {0x2773, 0x0000}, + {0x2775, 0x0000}, + {0x27E7, 0x0000}, + {0x27E9, 0x0000}, + {0x27EB, 0x0000}, + {0x2984, 0x0000}, + {0x2986, 0x0000}, + {0x2988, 0x0000}, + {0x298A, 0x0000}, + {0x298C, 0x0000}, + {0x298E, 0x0000}, + {0x2990, 0x0000}, + {0x2992, 0x0000}, + {0x2994, 0x0000}, + {0x2996, 0x0000}, + {0x2998, 0x0000}, + {0x29D9, 0x0000}, + {0x29DB, 0x0000}, + {0x29FD, 0x0000}, + {0x3009, 0x0000}, + {0x300B, 0x0000}, + {0x300D, 0x0000}, + {0x300F, 0x0000}, + {0x3011, 0x0000}, + {0x3015, 0x0000}, + {0x3017, 0x0000}, + {0x3019, 0x0000}, + {0x301B, 0x0000}, + {0x301E, 0x0000}, + {0x301F, 0x0000}, + {0xFD3F, 0x0000}, + {0xFE36, 0x0000}, + {0xFE38, 0x0000}, + {0xFE3A, 0x0000}, + {0xFE3C, 0x0000}, + {0xFE3E, 0x0000}, + {0xFE40, 0x0000}, + {0xFE42, 0x0000}, + {0xFE44, 0x0000}, + {0xFE48, 0x0000}, + {0xFE5A, 0x0000}, + {0xFE5C, 0x0000}, + {0xFE5E, 0x0000}, + {0xFF09, 0x0000}, + {0xFF3D, 0x0000}, + {0xFF5D, 0x0000}, + {0xFF60, 0x0000}, + {0xFF63, 0x0000} +}; + +CATEGORY_CHAR_RANGE PiRanges[] = +{ + {0x00AB, 0x0000}, + {0x2018, 0x0000}, + {0x201B, 0x201C}, + {0x201F, 0x0000}, + {0x2039, 0x0000} +}; + +CATEGORY_CHAR_RANGE PfRanges[] = +{ + {0x00BB, 0x0000}, + {0x2019, 0x0000}, + {0x201D, 0x0000}, + {0x203A, 0x0000} +}; + +CATEGORY_CHAR_RANGE PoRanges[] = +{ + {0x0021, 0x0023}, + {0x0025, 0x0027}, + {0x002A, 0x0000}, + {0x002C, 0x0000}, + {0x002E, 0x002F}, + {0x003A, 0x003B}, + {0x003F, 0x0040}, + {0x005C, 0x0000}, + {0x00A1, 0x0000}, + {0x00B7, 0x0000}, + {0x00BF, 0x0000}, + {0x037E, 0x0000}, + {0x0387, 0x0000}, + {0x055A, 0x055F}, + {0x0589, 0x0000}, + {0x05BE, 0x0000}, + {0x05C0, 0x0000}, + {0x05C3, 0x0000}, + {0x05F3, 0x05F4}, + {0x060C, 0x060D}, + {0x061B, 0x0000}, + {0x061F, 0x0000}, + {0x066A, 0x066D}, + {0x06D4, 0x0000}, + {0x0700, 0x070D}, + {0x0964, 0x0965}, + {0x0970, 0x0000}, + {0x0DF4, 0x0000}, + {0x0E4F, 0x0000}, + {0x0E5A, 0x0E5B}, + {0x0F04, 0x0F12}, + {0x0F85, 0x0000}, + {0x104A, 0x104F}, + {0x10FB, 0x0000}, + {0x1361, 0x1368}, + {0x166D, 0x166E}, + {0x16EB, 0x16ED}, + {0x1735, 0x1736}, + {0x17D4, 0x17D6}, + {0x17D8, 0x17DA}, + {0x1800, 0x1805}, + {0x1807, 0x180A}, + {0x1944, 0x1945}, + {0x2016, 0x2017}, + {0x2020, 0x2027}, + {0x2030, 0x2038}, + {0x203B, 0x203E}, + {0x2041, 0x2043}, + {0x2047, 0x2051}, + {0x2053, 0x0000}, + {0x2057, 0x0000}, + {0x23B6, 0x0000}, + {0x3001, 0x3003}, + {0x303D, 0x0000}, + {0xFE30, 0x0000}, + {0xFE45, 0xFE46}, + {0xFE49, 0xFE4C}, + {0xFE50, 0xFE52}, + {0xFE54, 0xFE57}, + {0xFE5F, 0xFE61}, + {0xFE68, 0x0000}, + {0xFE6A, 0xFE6B}, + {0xFF01, 0xFF03}, + {0xFF05, 0xFF07}, + {0xFF0A, 0x0000}, + {0xFF0C, 0x0000}, + {0xFF0E, 0xFF0F}, + {0xFF1A, 0xFF1B}, + {0xFF1F, 0xFF20}, + {0xFF3C, 0x0000}, + {0xFF61, 0x0000}, + {0xFF64, 0x0000} +}; + +CATEGORY_CHAR_RANGE ZsRanges[] = +{ + {0x0020, 0x0000}, + {0x00A0, 0x0000}, + {0x1680, 0x0000}, + {0x180E, 0x0000}, + {0x2000, 0x200B}, + {0x202F, 0x0000}, + {0x205F, 0x0000}, + {0x3000, 0x0000} +}; + +CATEGORY_CHAR_RANGE ZlRanges[] = +{ + {0x2028, 0x0000} +}; + +CATEGORY_CHAR_RANGE ZpRanges[] = +{ + {0x2029, 0x0000} +}; + +CATEGORY_CHAR_RANGE SmRanges[] = +{ + {0x002B, 0x0000}, + {0x003C, 0x003E}, + {0x007C, 0x0000}, + {0x007E, 0x0000}, + {0x00AC, 0x0000}, + {0x00B1, 0x0000}, + {0x00D7, 0x0000}, + {0x00F7, 0x0000}, + {0x03F6, 0x0000}, + {0x2044, 0x0000}, + {0x2052, 0x0000}, + {0x207A, 0x207C}, + {0x208A, 0x208C}, + {0x2140, 0x2144}, + {0x214B, 0x0000}, + {0x2190, 0x2194}, + {0x219A, 0x219B}, + {0x21A0, 0x0000}, + {0x21A3, 0x0000}, + {0x21A6, 0x0000}, + {0x21AE, 0x0000}, + {0x21CE, 0x21CF}, + {0x21D2, 0x0000}, + {0x21D4, 0x0000}, + {0x21F4, 0x21FF}, + {0x2200, 0x22FF}, + {0x2308, 0x230B}, + {0x2320, 0x2321}, + {0x237C, 0x0000}, + {0x239B, 0x23B3}, + {0x25B7, 0x0000}, + {0x25C1, 0x0000}, + {0x25F8, 0x25FF}, + {0x266F, 0x0000}, + {0x27D0, 0x27E5}, + {0x27F0, 0x27FF}, + {0x2900, 0x2982}, + {0x2999, 0x29D7}, + {0x29DC, 0x29FB}, + {0x29FE, 0x2AFF}, + {0xFB29, 0x0000}, + {0xFE62, 0x0000}, + {0xFE64, 0xFE66}, + {0xFF0B, 0x0000}, + {0xFF1C, 0xFF1E}, + {0xFF5C, 0x0000}, + {0xFF5E, 0x0000}, + {0xFFE2, 0x0000}, + {0xFFE9, 0xFFEC} +}; + +CATEGORY_CHAR_RANGE ScRanges[] = +{ + {0x0024, 0x0000}, + {0x00A2, 0x00A5}, + {0x09F2, 0x09F3}, + {0x0AF1, 0x0000}, + {0x0BF9, 0x0000}, + {0x0E3F, 0x0000}, + {0x17DB, 0x0000}, + {0x20A0, 0x20B1}, + {0xFDFC, 0x0000}, + {0xFE69, 0x0000}, + {0xFF04, 0x0000}, + {0xFFE0, 0xFFE1}, + {0xFFE5, 0xFFE6} +}; + +CATEGORY_CHAR_RANGE SkRanges[] = +{ + {0x005E, 0x0000}, + {0x0060, 0x0000}, + {0x00A8, 0x0000}, + {0x00AF, 0x0000}, + {0x00B4, 0x0000}, + {0x00B8, 0x0000}, + {0x02C2, 0x02C5}, + {0x02D2, 0x02DF}, + {0x02E5, 0x02FF}, + {0x0374, 0x0375}, + {0x0384, 0x0385}, + {0x1FBD, 0x0000}, + {0x1FBF, 0x1FC1}, + {0x1FCD, 0x1FCF}, + {0x1FDD, 0x1FDF}, + {0x1FED, 0x1FEF}, + {0x1FFD, 0x0000}, + {0x1FFE, 0x0000}, + {0x309B, 0x309C}, + {0xFF3E, 0x0000}, + {0xFF40, 0x0000}, + {0xFFE3, 0x0000} +}; + +CATEGORY_CHAR_RANGE SoRanges[] = +{ + {0x00A6, 0x00A7}, + {0x00A9, 0x0000}, + {0x00AE, 0x0000}, + {0x00B0, 0x0000}, + {0x00B6, 0x0000}, + {0x0482, 0x0000}, + {0x060E, 0x060F}, + {0x06E9, 0x0000}, + {0x06FD, 0x06FE}, + {0x09FA, 0x0000}, + {0x0B70, 0x0000}, + {0x0BF3, 0x0BFA}, + {0x0F01, 0x0F03}, + {0x0F13, 0x0F17}, + {0x0F1A, 0x0F1F}, + {0x0F34, 0x0000}, + {0x0F36, 0x0000}, + {0x0F38, 0x0000}, + {0x0FBE, 0x0FBF}, + {0x0FC0, 0x0FC5}, + {0x0FC7, 0x0FCC}, + {0x0FCF, 0x0000}, + {0x1940, 0x0000}, + {0x19E0, 0x19FF}, + {0x2100, 0x2101}, + {0x2103, 0x2106}, + {0x2108, 0x2109}, + {0x2114, 0x0000}, + {0x2116, 0x2118}, + {0x211E, 0x211F}, + {0x2120, 0x2123}, + {0x2125, 0x0000}, + {0x2127, 0x0000}, + {0x2129, 0x0000}, + {0x212E, 0x0000}, + {0x2132, 0x0000}, + {0x213A, 0x213B}, + {0x214A, 0x0000}, + {0x2195, 0x2199}, + {0x219C, 0x219F}, + {0x21A1, 0x21A2}, + {0x21A4, 0x21A5}, + {0x21A7, 0x21AD}, + {0x21AF, 0x21CD}, + {0x21D0, 0x21D1}, + {0x21D3, 0x0000}, + {0x21D5, 0x21F3}, + {0x2300, 0x2307}, + {0x230C, 0x231F}, + {0x2322, 0x2328}, + {0x232B, 0x237B}, + {0x237D, 0x239A}, + {0x23B7, 0x23D0}, + {0x2400, 0x2426}, + {0x2440, 0x244A}, + {0x249C, 0x24E9}, + {0x2500, 0x25C0}, + {0x25C2, 0x25F7}, + {0x2600, 0x2617}, + {0x2619, 0x266E}, + {0x2670, 0x267D}, + {0x2680, 0x2691}, + {0x26A0, 0x26A1}, + {0x2701, 0x2704}, + {0x2706, 0x2709}, + {0x270C, 0x2727}, + {0x2729, 0x274B}, + {0x274D, 0x0000}, + {0x274F, 0x2752}, + {0x2756, 0x0000}, + {0x2758, 0x275E}, + {0x2761, 0x2767}, + {0x2794, 0x0000}, + {0x2798, 0x27AF}, + {0x27B1, 0x27BE}, + {0x2800, 0x28FF}, + {0x2B00, 0x2B0D}, + {0x2E80, 0x2E99}, + {0x2E9B, 0x2EF3}, + {0x2F00, 0x2FD5}, + {0x2FF0, 0x2FFB}, + {0x3004, 0x0000}, + {0x3012, 0x3013}, + {0x3020, 0x0000}, + {0x3036, 0x3037}, + {0x303E, 0x303F}, + {0x3190, 0x3191}, + {0x3196, 0x319F}, + {0x3200, 0x321E}, + {0x322A, 0x3243}, + {0x3250, 0x0000}, + {0x3260, 0x327D}, + {0x327F, 0x0000}, + {0x328A, 0x32FE}, + {0x3300, 0x33FF}, + {0x4DC0, 0x4DFF}, + {0xA490, 0xA4C6}, + {0xFDFD, 0x0000}, + {0xFFE4, 0x0000}, + {0xFFE8, 0x0000}, + {0xFFED, 0xFFEE}, + {0xFFFC, 0xFFFD} +}; + +CATEGORY_CHAR_RANGE CcRanges[] = +{ + {0x0001, 0x001F}, + {0x007F, 0x009F} +}; + +CATEGORY_CHAR_RANGE CfRanges[] = +{ + {0x00AD, 0x0000}, + {0x0600, 0x0603}, + {0x06DD, 0x0000}, + {0x070F, 0x0000}, + {0x17B4, 0x17B5}, + {0x200C, 0x200F}, + {0x202A, 0x202E}, + {0x2060, 0x2063}, + {0x206A, 0x206F}, + {0xFEFF, 0x0000}, + {0xFFF9, 0xFFFB} +}; + +CATEGORY_CHAR_RANGE CoRanges[] = +{ + {0xE000, 0x0000}, + {0xF8FF, 0x0000} +}; + +CATEGORY_CHAR_RANGE * CnRanges = NULL; + +typedef struct CATEGORY_CHARS +{ + FLMUINT uiNumArrays; + FLMUINT * puiRangeArraySizes; + CATEGORY_CHAR_RANGE ** ppRangeArrays; +} CATEGORY_CHARS; + +#define LuRangeSize (sizeof( LuRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define LlRangeSize (sizeof( LlRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define LtRangeSize (sizeof( LtRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define LmRangeSize (sizeof( LmRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define LoRangeSize (sizeof( LoRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT LCategoryRangeSizes [] = + {LuRangeSize, LlRangeSize, LtRangeSize, LmRangeSize, LoRangeSize}; + +CATEGORY_CHAR_RANGE * LCategoryRanges [] = + {LuRanges, LlRanges, LtRanges, LmRanges, LoRanges}; + +CATEGORY_CHARS LCategory = { 5, &LCategoryRangeSizes [0], &LCategoryRanges [0]}; +CATEGORY_CHARS LuCategory = { 1, &LCategoryRangeSizes [0], &LCategoryRanges [0]}; +CATEGORY_CHARS LlCategory = { 1, &LCategoryRangeSizes [1], &LCategoryRanges [1]}; +CATEGORY_CHARS LtCategory = { 1, &LCategoryRangeSizes [2], &LCategoryRanges [2]}; +CATEGORY_CHARS LmCategory = { 1, &LCategoryRangeSizes [3], &LCategoryRanges [3]}; +CATEGORY_CHARS LoCategory = { 1, &LCategoryRangeSizes [4], &LCategoryRanges [4]}; + +#define MnRangeSize (sizeof( MnRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define McRangeSize (sizeof( McRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define MeRangeSize (sizeof( MeRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT MCategoryRangeSizes [] = {MnRangeSize, McRangeSize, MeRangeSize}; + +CATEGORY_CHAR_RANGE * MCategoryRanges[] = {MnRanges, McRanges, MeRanges}; + +CATEGORY_CHARS MCategory = { 3, &MCategoryRangeSizes [0], &MCategoryRanges [0]}; +CATEGORY_CHARS MnCategory = { 1, &MCategoryRangeSizes [0], &MCategoryRanges [0]}; +CATEGORY_CHARS McCategory = { 1, &MCategoryRangeSizes [1], &MCategoryRanges [1]}; +CATEGORY_CHARS MeCategory = { 1, &MCategoryRangeSizes [2], &MCategoryRanges [2]}; + +#define DigitRangeSize (sizeof( DigitRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define Nd2RangeSize (sizeof( Nd2Ranges) / sizeof( CATEGORY_CHAR_RANGE)) +#define NlRangeSize (sizeof( NlRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define NoRangeSize (sizeof( NoRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT NCategoryRangeSizes [] = + {DigitRangeSize, Nd2RangeSize, NlRangeSize, NoRangeSize}; + +CATEGORY_CHAR_RANGE * NCategoryRanges[] = + {DigitRanges, Nd2Ranges, NlRanges, NoRanges}; + +CATEGORY_CHARS NCategory = { 4, &NCategoryRangeSizes [0], &NCategoryRanges [0]}; +CATEGORY_CHARS DigitCategory = { 1, &NCategoryRangeSizes [0], &NCategoryRanges [0]}; +CATEGORY_CHARS NdCategory = { 2, &NCategoryRangeSizes [0], &NCategoryRanges [0]}; +CATEGORY_CHARS Nd2Category = { 1, &NCategoryRangeSizes [1], &NCategoryRanges [1]}; +CATEGORY_CHARS NlCategory = { 1, &NCategoryRangeSizes [2], &NCategoryRanges [2]}; +CATEGORY_CHARS NoCategory = { 1, &NCategoryRangeSizes [3], &NCategoryRanges [3]}; + +#define PcRangeSize (sizeof( PcRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PdRangeSize (sizeof( PdRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PsRangeSize (sizeof( PsRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PeRangeSize (sizeof( PeRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PiRangeSize (sizeof( PiRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PfRangeSize (sizeof( PfRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define PoRangeSize (sizeof( PoRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT PCategoryRangeSizes [] = + {PcRangeSize, PdRangeSize, PsRangeSize, PeRangeSize, + PiRangeSize, PfRangeSize, PoRangeSize}; + +CATEGORY_CHAR_RANGE * PCategoryRanges[] = + {PcRanges, PdRanges, PsRanges, PeRanges, PiRanges, PfRanges, PoRanges}; + +CATEGORY_CHARS PCategory = { 7, &PCategoryRangeSizes [0], &PCategoryRanges [0]}; +CATEGORY_CHARS PcCategory = { 1, &PCategoryRangeSizes [0], &PCategoryRanges [0]}; +CATEGORY_CHARS PdCategory = { 1, &PCategoryRangeSizes [1], &PCategoryRanges [1]}; +CATEGORY_CHARS PsCategory = { 1, &PCategoryRangeSizes [2], &PCategoryRanges [2]}; +CATEGORY_CHARS PeCategory = { 1, &PCategoryRangeSizes [3], &PCategoryRanges [3]}; +CATEGORY_CHARS PiCategory = { 1, &PCategoryRangeSizes [4], &PCategoryRanges [4]}; +CATEGORY_CHARS PfCategory = { 1, &PCategoryRangeSizes [5], &PCategoryRanges [5]}; +CATEGORY_CHARS PoCategory = { 1, &PCategoryRangeSizes [6], &PCategoryRanges [6]}; + +#define ZsRangeSize (sizeof( ZsRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define ZlRangeSize (sizeof( ZlRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define ZpRangeSize (sizeof( ZpRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT ZCategoryRangeSizes [] = {ZsRangeSize, ZlRangeSize, ZpRangeSize}; + +CATEGORY_CHAR_RANGE * ZCategoryRanges[] = {ZsRanges, ZlRanges, ZpRanges}; + +CATEGORY_CHARS ZCategory = { 3, &ZCategoryRangeSizes [0], &ZCategoryRanges [0]}; +CATEGORY_CHARS ZsCategory = { 1, &ZCategoryRangeSizes [0], &ZCategoryRanges [0]}; +CATEGORY_CHARS ZlCategory = { 1, &ZCategoryRangeSizes [1], &ZCategoryRanges [1]}; +CATEGORY_CHARS ZpCategory = { 1, &ZCategoryRangeSizes [2], &ZCategoryRanges [2]}; + +#define SmRangeSize (sizeof( SmRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define ScRangeSize (sizeof( ScRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define SkRangeSize (sizeof( SkRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define SoRangeSize (sizeof( SoRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT SCategoryRangeSizes [] = {SmRangeSize, ScRangeSize, SkRangeSize, SoRangeSize}; + +CATEGORY_CHAR_RANGE * SCategoryRanges[] = {SmRanges, ScRanges, SkRanges, SoRanges}; + +CATEGORY_CHARS SCategory = { 4, &SCategoryRangeSizes [0], &SCategoryRanges [0]}; +CATEGORY_CHARS SmCategory = { 1, &SCategoryRangeSizes [0], &SCategoryRanges [0]}; +CATEGORY_CHARS ScCategory = { 1, &SCategoryRangeSizes [1], &SCategoryRanges [1]}; +CATEGORY_CHARS SkCategory = { 1, &SCategoryRangeSizes [2], &SCategoryRanges [2]}; +CATEGORY_CHARS SoCategory = { 1, &SCategoryRangeSizes [3], &SCategoryRanges [3]}; + +#define CcRangeSize (sizeof( CcRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define CfRangeSize (sizeof( CfRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define CoRangeSize (sizeof( CoRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define CnRangeSize 0 + +FLMUINT CCategoryRangeSizes [] = {CcRangeSize, CfRangeSize, CoRangeSize, CnRangeSize}; + +CATEGORY_CHAR_RANGE * CCategoryRanges[] = {CcRanges, CfRanges, CoRanges, CnRanges}; + +CATEGORY_CHARS CCategory = { 4, &CCategoryRangeSizes [0], &CCategoryRanges [0]}; +CATEGORY_CHARS CcCategory = { 1, &CCategoryRangeSizes [0], &CCategoryRanges [0]}; +CATEGORY_CHARS CfCategory = { 1, &CCategoryRangeSizes [1], &CCategoryRanges [1]}; +CATEGORY_CHARS CoCategory = { 1, &CCategoryRangeSizes [2], &CCategoryRanges [2]}; +CATEGORY_CHARS CnCategory = { 1, &CCategoryRangeSizes [3], &CCategoryRanges [3]}; + +CATEGORY_CHAR_RANGE LetterRanges [] = +{ + {0x0041, 0x005A}, + {0x0061, 0x007A}, + {0x00C0, 0x00D6}, + {0x00D8, 0x00F6}, + {0x00F8, 0x00FF}, + {0x0100, 0x0131}, + {0x0134, 0x013E}, + {0x0141, 0x0148}, + {0x014A, 0x017E}, + {0x0180, 0x01C3}, + {0x01CD, 0x01F0}, + {0x01F4, 0x01F5}, + {0x01FA, 0x0217}, + {0x0250, 0x02A8}, + {0x02BB, 0x02C1}, + {0x0386, 0x0000}, + {0x0388, 0x038A}, + {0x038C, 0x0000}, + {0x038E, 0x03A1}, + {0x03A3, 0x03CE}, + {0x03D0, 0x03D6}, + {0x03DA, 0x0000}, + {0x03DC, 0x0000}, + {0x03DE, 0x0000}, + {0x03E0, 0x0000}, + {0x03E2, 0x03F3}, + {0x0401, 0x040C}, + {0x040E, 0x044F}, + {0x0451, 0x045C}, + {0x045E, 0x0481}, + {0x0490, 0x04C4}, + {0x04C7, 0x04C8}, + {0x04CB, 0x04CC}, + {0x04D0, 0x04EB}, + {0x04EE, 0x04F5}, + {0x04F8, 0x04F9}, + {0x0531, 0x0556}, + {0x0559, 0x0000}, + {0x0561, 0x0586}, + {0x05D0, 0x05EA}, + {0x05F0, 0x05F2}, + {0x0621, 0x063A}, + {0x0641, 0x064A}, + {0x0671, 0x06B7}, + {0x06BA, 0x06BE}, + {0x06C0, 0x06CE}, + {0x06D0, 0x06D3}, + {0x06D5, 0x0000}, + {0x06E5, 0x06E6}, + {0x0905, 0x0939}, + {0x093D, 0x0000}, + {0x0958, 0x0961}, + {0x0985, 0x098C}, + {0x098F, 0x0990}, + {0x0993, 0x09A8}, + {0x09AA, 0x09B0}, + {0x09B2, 0x0000}, + {0x09B6, 0x09B9}, + {0x09DC, 0x09DD}, + {0x09DF, 0x09E1}, + {0x09F0, 0x09F1}, + {0x0A05, 0x0A0A}, + {0x0A0F, 0x0A10}, + {0x0A13, 0x0A28}, + {0x0A2A, 0x0A30}, + {0x0A32, 0x0A33}, + {0x0A35, 0x0A36}, + {0x0A38, 0x0A39}, + {0x0A59, 0x0A5C}, + {0x0A5E, 0x0000}, + {0x0A72, 0x0A74}, + {0x0A85, 0x0A8B}, + {0x0A8D, 0x0000}, + {0x0A8F, 0x0A91}, + {0x0A93, 0x0AA8}, + {0x0AAA, 0x0AB0}, + {0x0AB2, 0x0AB3}, + {0x0AB5, 0x0AB9}, + {0x0ABD, 0x0000}, + {0x0AE0, 0x0000}, + {0x0B05, 0x0B0C}, + {0x0B0F, 0x0B10}, + {0x0B13, 0x0B28}, + {0x0B2A, 0x0B30}, + {0x0B32, 0x0B33}, + {0x0B36, 0x0B39}, + {0x0B3D, 0x0000}, + {0x0B5C, 0x0B5D}, + {0x0B5F, 0x0B61}, + {0x0B85, 0x0B8A}, + {0x0B8E, 0x0B90}, + {0x0B92, 0x0B95}, + {0x0B99, 0x0B9A}, + {0x0B9C, 0x0000}, + {0x0B9E, 0x0B9F}, + {0x0BA3, 0x0BA4}, + {0x0BA8, 0x0BAA}, + {0x0BAE, 0x0BB5}, + {0x0BB7, 0x0BB9}, + {0x0C05, 0x0C0C}, + {0x0C0E, 0x0C10}, + {0x0C12, 0x0C28}, + {0x0C2A, 0x0C33}, + {0x0C35, 0x0C39}, + {0x0C60, 0x0C61}, + {0x0C85, 0x0C8C}, + {0x0C8E, 0x0C90}, + {0x0C92, 0x0CA8}, + {0x0CAA, 0x0CB3}, + {0x0CB5, 0x0CB9}, + {0x0CDE, 0x0000}, + {0x0CE0, 0x0CE1}, + {0x0D05, 0x0D0C}, + {0x0D0E, 0x0D10}, + {0x0D12, 0x0D28}, + {0x0D2A, 0x0D39}, + {0x0D60, 0x0D61}, + {0x0E01, 0x0E2E}, + {0x0E30, 0x0000}, + {0x0E32, 0x0E33}, + {0x0E40, 0x0E45}, + {0x0E81, 0x0E82}, + {0x0E84, 0x0000}, + {0x0E87, 0x0E88}, + {0x0E8A, 0x0000}, + {0x0E8D, 0x0000}, + {0x0E94, 0x0E97}, + {0x0E99, 0x0E9F}, + {0x0EA1, 0x0EA3}, + {0x0EA5, 0x0000}, + {0x0EA7, 0x0000}, + {0x0EAA, 0x0EAB}, + {0x0EAD, 0x0EAE}, + {0x0EB0, 0x0000}, + {0x0EB2, 0x0EB3}, + {0x0EBD, 0x0000}, + {0x0EC0, 0x0EC4}, + {0x0F40, 0x0F47}, + {0x0F49, 0x0F69}, + {0x10A0, 0x10C5}, + {0x10D0, 0x10F6}, + {0x1100, 0x0000}, + {0x1102, 0x1103}, + {0x1105, 0x1107}, + {0x1109, 0x0000}, + {0x110B, 0x110C}, + {0x110E, 0x1112}, + {0x113C, 0x0000}, + {0x113E, 0x0000}, + {0x1140, 0x0000}, + {0x114C, 0x0000}, + {0x114E, 0x0000}, + {0x1150, 0x0000}, + {0x1154, 0x1155}, + {0x1159, 0x0000}, + {0x115F, 0x1161}, + {0x1163, 0x0000}, + {0x1165, 0x0000}, + {0x1167, 0x0000}, + {0x1169, 0x0000}, + {0x116D, 0x116E}, + {0x1172, 0x1173}, + {0x1175, 0x0000}, + {0x119E, 0x0000}, + {0x11A8, 0x0000}, + {0x11AB, 0x0000}, + {0x11AE, 0x11AF}, + {0x11B7, 0x11B8}, + {0x11BA, 0x0000}, + {0x11BC, 0x11C2}, + {0x11EB, 0x0000}, + {0x11F0, 0x0000}, + {0x11F9, 0x0000}, + {0x1E00, 0x1E9B}, + {0x1EA0, 0x1EF9}, + {0x1F00, 0x1F15}, + {0x1F18, 0x1F1D}, + {0x1F20, 0x1F45}, + {0x1F48, 0x1F4D}, + {0x1F50, 0x1F57}, + {0x1F59, 0x0000}, + {0x1F5B, 0x0000}, + {0x1F5D, 0x0000}, + {0x1F5F, 0x1F7D}, + {0x1F80, 0x1FB4}, + {0x1FB6, 0x1FBC}, + {0x1FBE, 0x0000}, + {0x1FC2, 0x1FC4}, + {0x1FC6, 0x1FCC}, + {0x1FD0, 0x1FD3}, + {0x1FD6, 0x1FDB}, + {0x1FE0, 0x1FEC}, + {0x1FF2, 0x1FF4}, + {0x1FF6, 0x1FFC}, + {0x2126, 0x0000}, + {0x212A, 0x212B}, + {0x212E, 0x0000}, + {0x2180, 0x2182}, + {0x3007, 0x0000}, + {0x3021, 0x3029}, + {0x3041, 0x3094}, + {0x30A1, 0x30FA}, + {0x3105, 0x312C}, + {0x4E00, 0x9FA5}, + {0xAC00, 0xD7A3} +}; + +CATEGORY_CHAR_RANGE CombiningRanges [] = +{ + {0x0300, 0x0345}, + {0x0360, 0x0361}, + {0x0483, 0x0486}, + {0x0591, 0x05A1}, + {0x05A3, 0x05B9}, + {0x05BB, 0x05BD}, + {0x05BF, 0x0000}, + {0x05C1, 0x05C2}, + {0x05C4, 0x0000}, + {0x064B, 0x0652}, + {0x0670, 0x0000}, + {0x06D6, 0x06DC}, + {0x06DD, 0x06DF}, + {0x06E0, 0x06E4}, + {0x06E7, 0x06E8}, + {0x06EA, 0x06ED}, + {0x0901, 0x0903}, + {0x093C, 0x0000}, + {0x093E, 0x094C}, + {0x094D, 0x0000}, + {0x0951, 0x0954}, + {0x0962, 0x0963}, + {0x0981, 0x0983}, + {0x09BC, 0x0000}, + {0x09BE, 0x0000}, + {0x09BF, 0x0000}, + {0x09C0, 0x09C4}, + {0x09C7, 0x09C8}, + {0x09CB, 0x09CD}, + {0x09D7, 0x0000}, + {0x09E2, 0x09E3}, + {0x0A02, 0x0000}, + {0x0A3C, 0x0000}, + {0x0A3E, 0x0000}, + {0x0A3F, 0x0000}, + {0x0A40, 0x0A42}, + {0x0A47, 0x0A48}, + {0x0A4B, 0x0A4D}, + {0x0A70, 0x0A71}, + {0x0A81, 0x0A83}, + {0x0ABC, 0x0000}, + {0x0ABE, 0x0AC5}, + {0x0AC7, 0x0AC9}, + {0x0ACB, 0x0ACD}, + {0x0B01, 0x0B03}, + {0x0B3C, 0x0000}, + {0x0B3E, 0x0B43}, + {0x0B47, 0x0B48}, + {0x0B4B, 0x0B4D}, + {0x0B56, 0x0B57}, + {0x0B82, 0x0B83}, + {0x0BBE, 0x0BC2}, + {0x0BC6, 0x0BC8}, + {0x0BCA, 0x0BCD}, + {0x0BD7, 0x0000}, + {0x0C01, 0x0C03}, + {0x0C3E, 0x0C44}, + {0x0C46, 0x0C48}, + {0x0C4A, 0x0C4D}, + {0x0C55, 0x0C56}, + {0x0C82, 0x0C83}, + {0x0CBE, 0x0CC4}, + {0x0CC6, 0x0CC8}, + {0x0CCA, 0x0CCD}, + {0x0CD5, 0x0CD6}, + {0x0D02, 0x0D03}, + {0x0D3E, 0x0D43}, + {0x0D46, 0x0D48}, + {0x0D4A, 0x0D4D}, + {0x0D57, 0x0000}, + {0x0E31, 0x0000}, + {0x0E34, 0x0E3A}, + {0x0E47, 0x0E4E}, + {0x0EB1, 0x0000}, + {0x0EB4, 0x0EB9}, + {0x0EBB, 0x0EBC}, + {0x0EC8, 0x0ECD}, + {0x0F18, 0x0F19}, + {0x0F35, 0x0000}, + {0x0F37, 0x0000}, + {0x0F39, 0x0000}, + {0x0F3E, 0x0000}, + {0x0F3F, 0x0000}, + {0x0F71, 0x0F84}, + {0x0F86, 0x0F8B}, + {0x0F90, 0x0F95}, + {0x0F97, 0x0000}, + {0x0F99, 0x0FAD}, + {0x0FB1, 0x0FB7}, + {0x0FB9, 0x0000}, + {0x20D0, 0x20DC}, + {0x20E1, 0x0000}, + {0x302A, 0x302F}, + {0x3099, 0x0000}, + {0x309A, 0x0000} +}; + +CATEGORY_CHAR_RANGE ExtenderRanges [] = +{ + {0x00B7, 0x0000}, + {0x02D0, 0x0000}, + {0x02D1, 0x0000}, + {0x0387, 0x0000}, + {0x0640, 0x0000}, + {0x0E46, 0x0000}, + {0x0EC6, 0x0000}, + {0x3005, 0x0000}, + {0x3031, 0x3035}, + {0x309D, 0x309E}, + {0x30FC, 0x30FE} +}; + +#define LetterRangeSize (sizeof( LetterRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define CombiningRangeSize (sizeof( CombiningRanges) / sizeof( CATEGORY_CHAR_RANGE)) +#define ExtenderRangeSize (sizeof( ExtenderRanges) / sizeof( CATEGORY_CHAR_RANGE)) + +FLMUINT NameCharCategoryRangeSizes [] = + {LetterRangeSize, CombiningRangeSize, ExtenderRangeSize, DigitRangeSize}; + +CATEGORY_CHAR_RANGE * NameCharCategoryRanges[] = + {LetterRanges, CombiningRanges, ExtenderRanges, DigitRanges}; + +CATEGORY_CHARS NameCharCategory = { 4, &NameCharCategoryRangeSizes [0], &NameCharCategoryRanges [0]}; +CATEGORY_CHARS LetterCategory = { 1, &NameCharCategoryRangeSizes [0], &NameCharCategoryRanges [0]}; +CATEGORY_CHARS CombiningCategory = { 1, &NameCharCategoryRangeSizes [1], &NameCharCategoryRanges [1]}; +CATEGORY_CHARS ExtenderCategory = { 1, &NameCharCategoryRangeSizes [2], &NameCharCategoryRanges [2]}; + +typedef struct REGEXP_LITERAL +{ + FLMUNICODE * puzLiteral; + FLMUINT uiNumChars; +} REGEXP_LITERAL; + +typedef struct CHAR_RANGE +{ + FLMBOOL bNegatedRange; + FLMUNICODE uzLowChar; + FLMUNICODE uzHighChar; + CHAR_RANGE * pNext; + CHAR_RANGE * pPrev; +} CHAR_RANGE; + +typedef struct CHAR_LIST +{ + FLMBOOL bNegatedChars; + FLMUNICODE * puzChars; + FLMUINT uiNumChars; + CHAR_LIST * pNext; + CHAR_LIST * pPrev; +} CHAR_LIST; + +typedef struct CHAR_CATEGORY +{ + FLMBOOL bNegatedCategory; + CATEGORY_CHARS * pCategoryChars; + CHAR_CATEGORY * pNext; + CHAR_CATEGORY * pPrev; +} CHAR_CATEGORY; + +typedef struct REG_EXP * REG_EXP_p; + +typedef struct CHAR_CLASS +{ + FLMBOOL bNegatedClass; + CHAR_LIST * pFirstCharList; + CHAR_LIST * pLastCharList; + CHAR_RANGE * pFirstCharRange; + CHAR_RANGE * pLastCharRange; + CHAR_CATEGORY * pFirstCharCategory; + CHAR_CATEGORY * pLastCharCategory; + CHAR_CLASS * pFirstCharClass; + CHAR_CLASS * pLastCharClass; + CHAR_CLASS * pSubtractionClass; + CHAR_CLASS * pNext; + CHAR_CLASS * pPrev; +} CHAR_CLASS; + +typedef struct REG_EXP_BRANCH +{ + REG_EXP_p pParentExpr; + REG_EXP_p pFirstExpr; + REG_EXP_p pLastExpr; + REG_EXP_BRANCH * pNextBranch; + REG_EXP_BRANCH * pPrevBranch; +} REG_EXP_BRANCH; + +typedef struct REG_EXP_ALTERNATIVE +{ + REG_EXP_BRANCH * pFirstBranch; + REG_EXP_BRANCH * pLastBranch; +} REG_EXP_ALTERNATIVE; + +typedef struct REG_EXP +{ + eExpType eType; + FLMUINT uiMinOccurs; + FLMUINT uiMaxOccurs; + FLMBOOL bUnlimited; + FLMBOOL bQuantified; + union + { + REGEXP_LITERAL literal; + CHAR_CLASS charClass; + REG_EXP_ALTERNATIVE alternative; + } exp; + REG_EXP_BRANCH * pBranch; + REG_EXP * pNext; + REG_EXP * pPrev; +} REG_EXP; + +/***************************************************************************** +Desc: The regular expression class. +*****************************************************************************/ +class F_RegExp : public XF_Base +{ +public: + + F_RegExp(); + + ~F_RegExp(); + + RCODE setExpression( + FLMUNICODE * puzRegExp); + + FLMBOOL testString( + IF_PosIStream * pIStream); + +private: + + RCODE createRegExp( + eExpType eType, + REG_EXP ** ppExpr); + + RCODE addLiteralChar( + FLMUNICODE uzChar); + + RCODE addLiteralExpr( + FLMUNICODE * puzLiteral, + FLMUINT uiNumChars, + REG_EXP ** ppExpr); + + RCODE saveLiteral( void); + + RCODE createCharCategory( + CATEGORY_CHARS * pCategoryChars, + FLMBOOL bNegatedCategory, + CHAR_CLASS * pCharClass); + + RCODE createCharRange( + FLMUNICODE uzLowChar, + FLMUNICODE uzHighChar, + FLMBOOL bNegatedRange, + CHAR_CLASS * pCharClass); + + RCODE createCharList( + const char * pszChars, + FLMUNICODE * puzChars, + FLMUINT uiNumChars, + FLMBOOL bNegatedChars, + CHAR_CLASS * pCharClass); + + RCODE createCharClass( + FLMBOOL bNegatedClass, + CHAR_CLASS * pCharClass, + CHAR_CLASS ** ppNewCharClass); + + RCODE parseEscape( + FLMUNICODE ** ppuzRegExp, + CHAR_CLASS * pCharClass, + FLMUNICODE * puzChar); + + RCODE parseCharClass( + FLMUNICODE ** ppuzRegExp, + CHAR_CLASS * pCharClass); + + RCODE parseQuantifier( + FLMUNICODE ** ppuzRegExp); + + RCODE startAlternative( void); + + RCODE endAlternative( void); + + RCODE startNewBranch( void); + + F_Pool m_Pool; + REG_EXP_BRANCH m_topBranch; + REG_EXP_BRANCH * m_pCurrBranch; // Used only when parsing + FLMUNICODE m_uzLiteral [256]; + FLMUNICODE * m_puzLiteral; + FLMUINT m_uiMaxLiteralChars; + FLMUINT m_uiNumLiteralChars; +}; + +// Local function prototypes + +FSTATIC FLMBOOL isCategory( + CATEGORY_CHARS ** ppCategoryChars, + FLMUNICODE ** ppuzRegExp); + +FSTATIC FLMBOOL isBlock( + FLMUNICODE * puzLowChar, + FLMUNICODE * puzHighChar, + FLMUNICODE ** ppuzRegExp); + +/***************************************************************************** +Desc: Constructor +*****************************************************************************/ +F_RegExp::F_RegExp() +{ + m_Pool.poolInit( 256); + m_topBranch.pParentExpr = NULL; + m_topBranch.pNextBranch = NULL; + m_topBranch.pPrevBranch = NULL; + m_topBranch.pFirstExpr = NULL; + m_topBranch.pLastExpr = NULL; + m_pCurrBranch = &m_topBranch; + m_puzLiteral = &m_uzLiteral [0]; + m_uiMaxLiteralChars = sizeof( m_uzLiteral) / sizeof( FLMUNICODE); + m_uiNumLiteralChars = 0; +} + +/***************************************************************************** +Desc: Destructor +*****************************************************************************/ +F_RegExp::~F_RegExp() +{ + m_Pool.poolFree(); + if (m_puzLiteral != &m_uzLiteral [0]) + { + f_free( &m_puzLiteral); + } +} + +/***************************************************************************** +Desc: Skip whitespace in a string. +*****************************************************************************/ +FINLINE FLMBOOL isWhitespace( + FLMUNICODE uzChar + ) +{ + return( (uzChar == ' ' || uzChar == '\t' || + uzChar == '\n' || uzChar == '\r') ? TRUE : FALSE); +} + +/***************************************************************************** +Desc: Skip whitespace in a string. +*****************************************************************************/ +FINLINE FLMUNICODE * skipWhitespace( + FLMUNICODE * puzRegExp + ) +{ + while (isWhitespace( *puzRegExp)) + { + puzRegExp++; + } + return( puzRegExp); +} + +/***************************************************************************** +Desc: Create a new regular expression. +*****************************************************************************/ +RCODE F_RegExp::createRegExp( + eExpType eType, + REG_EXP ** ppExpr + ) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pExpr; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( REG_EXP), + (void **)&pExpr))) + { + goto Exit; + } + *ppExpr = pExpr; + pExpr->eType = eType; + if ((pExpr->pPrev = m_pCurrBranch->pLastExpr) != NULL) + { + m_pCurrBranch->pLastExpr->pNext = pExpr; + } + else + { + m_pCurrBranch->pFirstExpr = pExpr; + } + m_pCurrBranch->pLastExpr = pExpr; + pExpr->pBranch = m_pCurrBranch; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Add a literal character to our current literal buffer. +*****************************************************************************/ +RCODE F_RegExp::addLiteralChar( + FLMUNICODE uzChar + ) +{ + RCODE rc = NE_XFLM_OK; + + // See if we need to allocate a new buffer. + + if (m_uiNumLiteralChars == m_uiMaxLiteralChars) + { + FLMUNICODE * puzTmp; + FLMUINT uiNewMax = m_uiMaxLiteralChars + 128; + + if (RC_BAD( rc = f_alloc( sizeof( FLMUNICODE) * uiNewMax, + &puzTmp))) + { + goto Exit; + } + if (m_uiNumLiteralChars) + { + f_memcpy( puzTmp, m_puzLiteral, + sizeof( FLMUNICODE) * m_uiNumLiteralChars); + } + if (m_puzLiteral != &m_uzLiteral [0]) + { + f_free( &m_puzLiteral); + } + m_puzLiteral = puzTmp; + m_uiMaxLiteralChars = uiNewMax; + } + m_puzLiteral [m_uiNumLiteralChars] = uzChar; + m_uiNumLiteralChars++; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Add a literal expression. +*****************************************************************************/ +RCODE F_RegExp::addLiteralExpr( + FLMUNICODE * puzLiteral, + FLMUINT uiNumChars, + REG_EXP ** ppExpr + ) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pTmpExpr; + + if (RC_BAD( rc = createRegExp( EXP_LITERAL, &pTmpExpr))) + { + goto Exit; + } + + if (ppExpr) + { + *ppExpr = pTmpExpr; + } + if (RC_BAD( rc = m_Pool.poolAlloc( uiNumChars * sizeof( FLMUNICODE), + (void **)&pTmpExpr->exp.literal.puzLiteral))) + { + goto Exit; + } + f_memcpy( pTmpExpr->exp.literal.puzLiteral, puzLiteral, + uiNumChars * sizeof( FLMUNICODE)); + pTmpExpr->exp.literal.uiNumChars = uiNumChars; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Save out our current literal into our expression tree. +*****************************************************************************/ +RCODE F_RegExp::saveLiteral( void) +{ + RCODE rc = NE_XFLM_OK; + + if (m_uiNumLiteralChars) + { + if (RC_BAD( rc = addLiteralExpr( m_puzLiteral, + m_uiNumLiteralChars, NULL))) + { + goto Exit; + } + + // Zero out the literal and start over. + + m_uiNumLiteralChars = 0; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: See if an escape sequence is a category +*****************************************************************************/ +FSTATIC FLMBOOL isCategory( + CATEGORY_CHARS ** ppCategoryChars, + FLMUNICODE ** ppuzRegExp + ) +{ + FLMBOOL bIsCategory = FALSE; + FLMUNICODE * puzRegExp = *ppuzRegExp; + + // Skip past the 'p' or 'P' + + puzRegExp++; + + // Next character better be a '{' + + if (*puzRegExp != '{') + { + goto Exit; + } + puzRegExp++; + + switch (*puzRegExp) + { + case 'L': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &LCategory; break; + case 'u': *ppCategoryChars = &LuCategory; puzRegExp++; break; + case 'l': *ppCategoryChars = &LlCategory; puzRegExp++; break; + case 't': *ppCategoryChars = &LtCategory; puzRegExp++; break; + case 'm': *ppCategoryChars = &LmCategory; puzRegExp++; break; + case 'o': *ppCategoryChars = &LoCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'M': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &MCategory; break; + case 'n': *ppCategoryChars = &MnCategory; puzRegExp++; break; + case 'c': *ppCategoryChars = &McCategory; puzRegExp++; break; + case 'e': *ppCategoryChars = &MeCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'N': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &NCategory; break; + case 'd': *ppCategoryChars = &NdCategory; puzRegExp++; break; + case 'l': *ppCategoryChars = &NlCategory; puzRegExp++; break; + case 'o': *ppCategoryChars = &NoCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'P': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &PCategory; break; + case 'c': *ppCategoryChars = &PcCategory; puzRegExp++; break; + case 'd': *ppCategoryChars = &PdCategory; puzRegExp++; break; + case 's': *ppCategoryChars = &PsCategory; puzRegExp++; break; + case 'e': *ppCategoryChars = &PeCategory; puzRegExp++; break; + case 'i': *ppCategoryChars = &PiCategory; puzRegExp++; break; + case 'f': *ppCategoryChars = &PfCategory; puzRegExp++; break; + case 'o': *ppCategoryChars = &PoCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'Z': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &ZCategory; break; + case 's': *ppCategoryChars = &ZsCategory; puzRegExp++; break; + case 'l': *ppCategoryChars = &ZlCategory; puzRegExp++; break; + case 'p': *ppCategoryChars = &ZpCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'S': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &SCategory; break; + case 'm': *ppCategoryChars = &SmCategory; puzRegExp++; break; + case 'c': *ppCategoryChars = &ScCategory; puzRegExp++; break; + case 'k': *ppCategoryChars = &SkCategory; puzRegExp++; break; + case 'o': *ppCategoryChars = &SoCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + case 'C': + puzRegExp++; + switch (*puzRegExp) + { + case '}': *ppCategoryChars = &CCategory; break; + case 'c': *ppCategoryChars = &CcCategory; puzRegExp++; break; + case 'f': *ppCategoryChars = &CfCategory; puzRegExp++; break; + case 'o': *ppCategoryChars = &CoCategory; puzRegExp++; break; + case 'n': *ppCategoryChars = &CnCategory; puzRegExp++; break; + default: goto Exit; + } + break; + + default: + goto Exit; + } + + // The last letter better be a '}' + + if (*puzRegExp != '}') + { + goto Exit; + } + puzRegExp++; + bIsCategory = TRUE; + +Exit: + + // Only move the pointer forward if it is, in fact, a category + + if (bIsCategory) + { + *ppuzRegExp = puzRegExp; + } + return( bIsCategory); +} + +/***************************************************************************** +Desc: See if an escape sequence is a block range of characters +*****************************************************************************/ +FSTATIC FLMBOOL isBlock( + FLMUNICODE * puzLowChar, + FLMUNICODE * puzHighChar, + FLMUNICODE ** ppuzRegExp + ) +{ + FLMBOOL bIsBlock = FALSE; + FLMUINT uiLoop; + FLMUNICODE * puzRegExp = *ppuzRegExp; + FLMUNICODE * puzBlockName; + FLMUNICODE * puzSaveBlockName; + const char * pszBlockName; + + // Skip past the 'p' or 'P' + + puzRegExp++; + + // Next three characters better be '{Is' + + if (*puzRegExp != '{') + { + goto Exit; + } + puzRegExp++; + + if (*puzRegExp != 'I') + { + goto Exit; + } + puzRegExp++; + + if (*puzRegExp != 's') + { + goto Exit; + } + puzRegExp++; + + puzSaveBlockName = puzRegExp; + uiLoop = 0; + for (uiLoop = 0;;uiLoop++) + { + if ((pszBlockName = FlmBlockCharRanges [uiLoop].pszBlockName) == NULL) + { + goto Exit; + } + + // Compare the name + + puzBlockName = puzSaveBlockName; + while (*pszBlockName && *puzBlockName != '}') + { + if ((FLMUNICODE)(*pszBlockName) != *puzBlockName) + { + break; + } + pszBlockName++; + puzBlockName++; + } + + if (*pszBlockName == 0 && *puzBlockName == '}') + { + puzRegExp = puzBlockName + 1; + bIsBlock = TRUE; + *puzLowChar = FlmBlockCharRanges [uiLoop].uzLowChar; + *puzHighChar = FlmBlockCharRanges [uiLoop].uzLowChar; + bIsBlock = TRUE; + break; + } + } + +Exit: + + // Only move the pointer forward if it is, in fact, a category + + if (bIsBlock) + { + *ppuzRegExp = puzRegExp; + } + return( bIsBlock); +} + +/***************************************************************************** +Desc: Create a character category. +*****************************************************************************/ +RCODE F_RegExp::createCharCategory( + CATEGORY_CHARS * pCategoryChars, + FLMBOOL bNegatedCategory, + CHAR_CLASS * pCharClass + ) +{ + RCODE rc = NE_XFLM_OK; + CHAR_CATEGORY * pCharCategory; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CHAR_CATEGORY), + (void **)&pCharCategory))) + { + goto Exit; + } + pCharCategory->bNegatedCategory = bNegatedCategory; + pCharCategory->pCategoryChars = pCategoryChars; + + // Link at end of list of character categories + + if ((pCharCategory->pPrev = pCharClass->pLastCharCategory) != NULL) + { + pCharCategory->pPrev->pNext = pCharCategory; + } + else + { + pCharClass->pFirstCharCategory = pCharCategory; + } + pCharClass->pLastCharCategory = pCharCategory; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Create a character range. +*****************************************************************************/ +RCODE F_RegExp::createCharRange( + FLMUNICODE uzLowChar, + FLMUNICODE uzHighChar, + FLMBOOL bNegatedRange, + CHAR_CLASS * pCharClass + ) +{ + RCODE rc = NE_XFLM_OK; + CHAR_RANGE * pCharRange; + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CHAR_RANGE), + (void **)&pCharRange))) + { + goto Exit; + } + pCharRange->bNegatedRange = bNegatedRange; + pCharRange->uzLowChar = uzLowChar; + pCharRange->uzHighChar = uzHighChar; + + // Link at end of list of character ranges + + if ((pCharRange->pPrev = pCharClass->pLastCharRange) != NULL) + { + pCharRange->pPrev->pNext = pCharRange; + } + else + { + pCharClass->pFirstCharRange = pCharRange; + } + pCharClass->pLastCharRange = pCharRange; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Create a character list. +*****************************************************************************/ +RCODE F_RegExp::createCharList( + const char * pszChars, + FLMUNICODE * puzChars, + FLMUINT uiNumChars, + FLMBOOL bNegatedChars, + CHAR_CLASS * pCharClass) +{ + RCODE rc = NE_XFLM_OK; + CHAR_LIST * pCharList; + + // Allocate the CHAR_LIST structure. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CHAR_LIST), + (void **)&pCharList))) + { + goto Exit; + } + pCharList->bNegatedChars = bNegatedChars; + + // Allocate an array for the characters. + + if (RC_BAD( rc = m_Pool.poolAlloc( sizeof( FLMUNICODE) * uiNumChars, + (void **)&pCharList->puzChars))) + { + goto Exit; + } + pCharList->uiNumChars = uiNumChars; + + // Copy the characters from pszChars or puzChars into pCharList->puzChars + + if (pszChars) + { + flmAssert( !puzChars); + puzChars = pCharList->puzChars; + while (*pszChars) + { + *puzChars = (FLMUNICODE)(*pszChars); + puzChars++; + pszChars++; + } + } + else + { + f_memcpy( pCharList->puzChars, puzChars, + sizeof( FLMUNICODE) * uiNumChars); + } + + // Link at end of list of character lists + + if ((pCharList->pPrev = pCharClass->pLastCharList) != NULL) + { + pCharList->pPrev->pNext = pCharList; + } + else + { + pCharClass->pFirstCharList = pCharList; + } + pCharClass->pLastCharList = pCharList; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Create a sub-character class +*****************************************************************************/ +RCODE F_RegExp::createCharClass( + FLMBOOL bNegatedClass, + CHAR_CLASS * pCharClass, + CHAR_CLASS ** ppNewCharClass + ) +{ + RCODE rc = NE_XFLM_OK; + CHAR_CLASS * pNewCharClass; + + // Allocate the CHAR_CLASS structure. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CHAR_CLASS), + (void **)&pNewCharClass))) + { + goto Exit; + } + *ppNewCharClass = pNewCharClass; + pNewCharClass->bNegatedClass = bNegatedClass; + + // Link at end of list of character classes + + if ((pNewCharClass->pPrev = pCharClass->pLastCharClass) != NULL) + { + pNewCharClass->pPrev->pNext = pCharClass; + } + else + { + pNewCharClass->pFirstCharClass = pCharClass; + } + pCharClass->pLastCharClass = pNewCharClass; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Parse an escape sequence. +*****************************************************************************/ +RCODE F_RegExp::parseEscape( + FLMUNICODE ** ppuzRegExp, + CHAR_CLASS * pCharClass, + FLMUNICODE * puzChar + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzRegExp = *ppuzRegExp; + CHAR_CLASS * pNewCharClass; + CATEGORY_CHARS * pCategoryChars; + FLMUNICODE uzLowChar; + FLMUNICODE uzHighChar; + FLMBOOL bNegated; + REG_EXP * pTmpExpr; + + *puzChar = 0; + + // Skip past the '\' + + puzRegExp++; + + switch (*puzRegExp) + { + case 'p': + case 'P': + bNegated = (*puzRegExp == 'P') ? TRUE : FALSE; + if (isCategory( &pCategoryChars, &puzRegExp)) + { + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharCategory( pCategoryChars, bNegated, + pCharClass))) + { + goto Exit; + } + } + else if (isBlock( &uzLowChar, &uzHighChar, &puzRegExp)) + { + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharRange( uzLowChar, uzHighChar, + bNegated, pCharClass))) + { + goto Exit; + } + } + else + { + + // Treat as a regular character. + + *puzChar = *puzRegExp; + puzRegExp++; + } + break; + + case 's': + case 'S': + bNegated = (*puzRegExp == 'S') ? TRUE : FALSE; + puzRegExp++; + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharList( " \t\n\r", NULL, 4, + bNegated, pCharClass))) + { + goto Exit; + } + break; + + case 'd': + case 'D': + + // The same as {Nd} category + + bNegated = (*puzRegExp == 'D') ? TRUE : FALSE; + puzRegExp++; + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharCategory( &NdCategory, + bNegated, pCharClass))) + { + goto Exit; + } + break; + + case 'w': + case 'W': + + // Create a sub-character class that excludes the + // categories {P} - punctuation, {Z} - separators, and {C} - others + // NOTE: bNegated should be set to TRUE for lowercase 'w', unlike + // others above where negated flag is TRUE for uppercase + // character. This is because we are trying to create a class + // that is everything EXCEPT the three character categories + // mentioned. In the case of 'W', it should include those + // three categories - just the opposite of 'w'. + + bNegated = (*puzRegExp == 'w') ? TRUE : FALSE; + puzRegExp++; + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharClass( bNegated, pCharClass, + &pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharCategory( &PCategory, + FALSE, pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharCategory( &ZCategory, + FALSE, pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharCategory( &CCategory, + FALSE, pNewCharClass))) + { + goto Exit; + } + break; + + case 'c': + case 'C': + + // NameChar and '.', '_', '-', ':' + + bNegated = (*puzRegExp == 'C') ? TRUE : FALSE; + puzRegExp++; + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharClass( bNegated, pCharClass, &pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharCategory( &NameCharCategory, FALSE, + pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharList( "._-:", NULL, 4, FALSE, pNewCharClass))) + { + goto Exit; + } + break; + + case 'i': + case 'I': + + // Letter and '_', ':' + + bNegated = (*puzRegExp == 'I') ? TRUE : FALSE; + puzRegExp++; + if (!pCharClass) + { + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + pCharClass = &pTmpExpr->exp.charClass; + } + if (RC_BAD( rc = createCharClass( bNegated, pCharClass, &pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharCategory( &LetterCategory, FALSE, + pNewCharClass))) + { + goto Exit; + } + if (RC_BAD( rc = createCharList( "_:", NULL, 2, FALSE, pNewCharClass))) + { + goto Exit; + } + break; + + case 'n': + *puzChar = 0xA; + puzRegExp++; + break; + + case 'r': + *puzChar = 0xD; + puzRegExp++; + break; + + case 't': + *puzChar = 0x9; + puzRegExp++; + break; + + default: + + *puzChar = *puzRegExp; + puzRegExp++; + break; + } + +Exit: + + *ppuzRegExp = puzRegExp; + return( rc); +} + +/***************************************************************************** +Desc: Parse a character class - [xxxxx]. +*****************************************************************************/ +RCODE F_RegExp::parseCharClass( + FLMUNICODE ** ppuzRegExp, + CHAR_CLASS * pCharClass + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzRegExp = *ppuzRegExp; + FLMBOOL bAtBeginning; + FLMBOOL bHaveDash; + FLMUNICODE uzChar; + + flmAssert( *puzRegExp == '['); + + // skip past the '[' + + puzRegExp++; + + // Save whatever literal expression may have built up. + + if (RC_BAD( rc = saveLiteral())) + { + goto Exit; + } + + bAtBeginning = TRUE; + bHaveDash = FALSE; + for (;;) + { + switch (*puzRegExp) + { + case 0: + rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_EXPR); + goto Exit; + + case '^': + + // This is only the negation character if it comes immediately + // after the opening '['. Otherwise, it is a regular character. + + if (bAtBeginning && !pCharClass->bNegatedClass) + { + pCharClass->bNegatedClass = TRUE; + puzRegExp++; + } + else + { + goto Save_Char; + } + break; + + case '-': + + // This is NOT a range operator if it comes immediately after + // the opening '[' or right before the closing ']'. In those + // two cases, it is a regular character. + + if (bAtBeginning || *(puzRegExp + 1) == ']') + { + goto Save_Char; + } + else if (!m_uiNumLiteralChars) + { + // The dash doesn't have a preceding character we can use + // as the beginning character of the range. + + rc = RC_SET( NE_XFLM_UNESCAPED_METACHAR); + goto Exit; + } + else + { + bHaveDash = TRUE; + puzRegExp++; + } + break; + + case '\\': + + if (RC_BAD( rc = parseEscape( &puzRegExp, pCharClass, &uzChar))) + { + goto Exit; + } + if (uzChar) + { + goto Save_Char; + } + bAtBeginning = FALSE; + + break; + + case ']': + + // If it comes right after the opening '[', or after the '^' when it + // is used as a negation (see above), it is a regular character. + // In both of those cases, bAtBeginning will still be TRUE. + // Otherwise, it is the end of the character class. + + if (bAtBeginning) + { + goto Save_Char; + } + goto End_Of_Expr; + + case '[': + + // If it comes right after a '-', it represents the beginning of + // a subtraction group. + + if (!bHaveDash) + { + goto Save_Char; + } + + // Won't be at the beginning in this case + + flmAssert( !bAtBeginning); + + // Had the dash character, toggle flag back to FALSE. + + bHaveDash = FALSE; + + // Before calling self recursively, need to clear out + // any characters we have gathered up so far. + + if (m_uiNumLiteralChars) + { + if (RC_BAD( rc = createCharList( NULL, m_puzLiteral, + m_uiNumLiteralChars, FALSE, + pCharClass))) + { + goto Exit; + } + m_uiNumLiteralChars = 0; + } + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( CHAR_CLASS), + (void **)&pCharClass->pSubtractionClass))) + { + goto Exit; + } + if (RC_BAD( rc = parseCharClass( &puzRegExp, + pCharClass->pSubtractionClass))) + { + goto Exit; + } + + // Next character must be a ']' to end this character class + + if (*puzRegExp == ']') + { + goto End_Of_Expr; + } + rc = (RCODE)((*puzRegExp) + ? RC_SET( NE_XFLM_ILLEGAL_CLASS_SUBTRACTION) + : RC_SET( NE_XFLM_UNEXPECTED_END_OF_EXPR)); + goto Exit; + + default: + +Save_Char: + + if (!bHaveDash) + { + if (RC_BAD( rc = addLiteralChar( *puzRegExp))) + { + goto Exit; + } + } + else + { + FLMUNICODE uzLowChar; + + // Reset the bHaveDash flag. + + bHaveDash = FALSE; + + uzLowChar = m_puzLiteral [m_uiNumLiteralChars - 1]; + if (*puzRegExp < uzLowChar) + { + rc = RC_SET( NE_XFLM_ILLEGAL_CHAR_RANGE_IN_EXPR); + goto Exit; + } + + // No need to do anything if the character is equal to + // the last one. i.e., they are doing a range like A-A + + else if (*puzRegExp > uzLowChar) + { + + // Save off the characters we have gathered so far, + // except for the last one, which will be the beginning + // of our range. + + if (m_uiNumLiteralChars > 1) + { + if (RC_BAD( rc = createCharList( NULL, m_puzLiteral, + m_uiNumLiteralChars - 1, FALSE, + pCharClass))) + { + goto Exit; + } + } + + // Need to zero out number of characters so that if there + // is another dash, it will be reported as an unescaped + // metacharacter. + + m_uiNumLiteralChars = 0; + + // Create a range. + + if (RC_BAD( rc = createCharRange( uzLowChar, *puzRegExp, + FALSE, pCharClass))) + { + goto Exit; + } + } + } + bAtBeginning = FALSE; + puzRegExp++; + break; + } + } + +End_Of_Expr: + + // Keep the final set of characters we may have + // gathered. + + if (m_uiNumLiteralChars) + { + if (RC_BAD( rc = createCharList( NULL, m_puzLiteral, + m_uiNumLiteralChars, FALSE, pCharClass))) + { + goto Exit; + } + m_uiNumLiteralChars = 0; + } + + // Skip past the ']' + + puzRegExp++; + +Exit: + + *ppuzRegExp = puzRegExp; + return( rc); +} + +/***************************************************************************** +Desc: Parse a quantifier expression. + All of the following forms are allowed: + {3} - exactly 3 + {,4} - same as {0,4} + {3,} - 3 to unlimited + {3,5} - min of 3, max of 5 + + - same as {1,} + * - same as {0,} + ? - same as {0,1} +*****************************************************************************/ +RCODE F_RegExp::parseQuantifier( + FLMUNICODE ** ppuzRegExp) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzRegExp = *ppuzRegExp; + FLMUINT uiMin; + FLMUINT uiMax; + FLMBOOL bUnlimited; + REG_EXP * pTmpExpr; + + if (!m_uiNumLiteralChars && + (!m_pCurrBranch->pLastExpr || + m_pCurrBranch->pLastExpr->bQuantified)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_QUANTIFIER); + goto Exit; + } + + // Skip the first character + + if (*puzRegExp == '?') + { + uiMin = 0; + uiMax = 1; + bUnlimited = FALSE; + + // Skip past the '?' + + puzRegExp++; + } + else if (*puzRegExp == '*') + { + uiMin = 0; + uiMax = 0; + bUnlimited = TRUE; + + // Skip past the '*' + + puzRegExp++; + } + else if (*puzRegExp == '+') + { + uiMin = 1; + uiMax = 0; + bUnlimited = TRUE; + + // Skip past the '+' + + puzRegExp++; + } + else + { + + // Only thing left better be a left brace + + flmAssert( *puzRegExp == '{'); + + // Skip past the left brace + + puzRegExp++; + + // Skip any white space + + puzRegExp = skipWhitespace( puzRegExp); + + // Get the first number, if any + + uiMin = 0; + while (*puzRegExp >= '0' && *puzRegExp <= '9') + { + uiMin *= 10; + uiMin += (FLMUINT)(*puzRegExp - '0'); + puzRegExp++; + } + + // Skip any whitespace that comes after the number. + + puzRegExp = skipWhitespace( puzRegExp); + + // Better have landed on a comma or right brace + + if (*puzRegExp == ',') + { + puzRegExp++; + + // Skip any whitespace that comes after the comma + + puzRegExp = skipWhitespace( puzRegExp); + } + else if (*puzRegExp == '}') + { + if (!uiMin) + { + rc = RC_SET( NE_XFLM_ILLEGAL_QUANTIFIER); + goto Exit; + } + uiMax = uiMin; + bUnlimited = FALSE; + } + else + { + rc = RC_SET( NE_XFLM_ILLEGAL_MIN_COUNT); + goto Exit; + } + + uiMax = 0; + bUnlimited = TRUE; + + // Get the next number, if any + + while (*puzRegExp >= '0' && *puzRegExp <= '9') + { + uiMax *= 10; + uiMax += (FLMUINT)(*puzRegExp - '0'); + bUnlimited = FALSE; + puzRegExp++; + } + + // Skip any whitespace that comes after the number. + + puzRegExp = skipWhitespace( puzRegExp); + + // Better have landed on a right brace + + if (*puzRegExp != '}') + { + rc = (RCODE)((*puzRegExp) + ? RC_SET( NE_XFLM_ILLEGAL_MAX_COUNT) + : RC_SET( NE_XFLM_UNEXPECTED_END_OF_EXPR)); + goto Exit; + } + + // Got the '}', see if min and max are legal. + + puzRegExp++; + if (!bUnlimited && (!uiMax || uiMax < uiMin)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_MAX_COUNT); + goto Exit; + } + } + + // If we have a literal, create two expressions. First + // expression will be all but the last character of the + // literal. Second expression will be the one character + // literal with a count. + + if (m_uiNumLiteralChars) + { + if (m_uiNumLiteralChars > 1) + { + if (RC_BAD( rc = addLiteralExpr( m_puzLiteral, + m_uiNumLiteralChars - 1, NULL))) + { + goto Exit; + } + } + if (RC_BAD( rc = addLiteralExpr( + &m_puzLiteral [m_uiNumLiteralChars - 1], 1, + &pTmpExpr))) + { + goto Exit; + } + + // Zero out the literal and start over. + + m_uiNumLiteralChars = 0; + } + else + { + pTmpExpr = m_pCurrBranch->pLastExpr; + } + flmAssert( pTmpExpr); + pTmpExpr->uiMinOccurs = uiMin; + pTmpExpr->uiMaxOccurs = uiMax; + pTmpExpr->bUnlimited = bUnlimited; + pTmpExpr->bQuantified = TRUE; + +Exit: + + *ppuzRegExp = puzRegExp; + return( rc); +} + +/***************************************************************************** +Desc: Start an alternative - Called when we hit a left paren. +*****************************************************************************/ +RCODE F_RegExp::startAlternative( void) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pTmpExpr; + + // If we were gathering up a literal, save it out. + + if (RC_BAD( rc = saveLiteral())) + { + goto Exit; + } + + // Start a new alternative expression node + + if (RC_BAD( rc = createRegExp( EXP_ALTERNATIVES, &pTmpExpr))) + { + goto Exit; + } + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( REG_EXP_BRANCH), + (void **)&pTmpExpr->exp.alternative.pFirstBranch))) + { + goto Exit; + } + pTmpExpr->exp.alternative.pLastBranch = + pTmpExpr->exp.alternative.pFirstBranch; + m_pCurrBranch = pTmpExpr->exp.alternative.pFirstBranch; + m_pCurrBranch->pParentExpr = pTmpExpr; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: End an alternative - Called when we hit a right paren. +*****************************************************************************/ +RCODE F_RegExp::endAlternative( void) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pParentExpr; + REG_EXP * pTmpExpr; + + // If the current branch doesn't have a parent + // expression, this is an illegal unescaped right paren. + + if ((pParentExpr = m_pCurrBranch->pParentExpr) == NULL) + { + rc = RC_SET( NE_XFLM_UNESCAPED_METACHAR); + goto Exit; + } + flmAssert( pParentExpr->eType == EXP_ALTERNATIVES); + + // If we were gathering up a literal, save it out. + + if (RC_BAD( rc = saveLiteral())) + { + goto Exit; + } + + // Make sure the current branch isn't empty + + if (!m_pCurrBranch->pFirstExpr) + { + rc = RC_SET( NE_XFLM_EMPTY_BRANCH_IN_EXPR); + goto Exit; + } + + // If there is only one alternative, link these + // nodes right in where the parent expression would + // have gone. This is not strictly necessary, because + // the processor can handle only one alternative, but + // it may save processing time in the end. + + if (pParentExpr->exp.alternative.pFirstBranch == + pParentExpr->exp.alternative.pLastBranch) + { + if ((m_pCurrBranch->pFirstExpr->pPrev = pParentExpr->pPrev) != NULL) + { + pParentExpr->pPrev->pNext = m_pCurrBranch->pFirstExpr; + } + else + { + pParentExpr->pBranch->pFirstExpr = m_pCurrBranch->pFirstExpr; + } + + // Need to alter the branch pointed to by each of the + // expressions in this list. + + pTmpExpr = m_pCurrBranch->pFirstExpr; + while (pTmpExpr) + { + pTmpExpr->pBranch = pParentExpr->pBranch; + pTmpExpr = pTmpExpr->pNext; + } + pParentExpr->pBranch->pLastExpr = m_pCurrBranch->pLastExpr; + } + + // Go back to the parent branch + + m_pCurrBranch = pParentExpr->pBranch; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Start a new branch of an alternatives list. This is called when + we hit the '|' character. +*****************************************************************************/ +RCODE F_RegExp::startNewBranch( void) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pTmpExpr; + REG_EXP_BRANCH * pTmpBranch; + + // If we were gathering up a literal, save it out. + + if (RC_BAD( rc = saveLiteral())) + { + goto Exit; + } + + // Make sure the current branch isn't empty + + if (!m_pCurrBranch->pFirstExpr) + { + rc = RC_SET( NE_XFLM_EMPTY_BRANCH_IN_EXPR); + goto Exit; + } + + // Create a new branch to link to current branch. + + if (RC_BAD( rc = m_Pool.poolCalloc( sizeof( REG_EXP_BRANCH), + (void **)&pTmpBranch))) + { + goto Exit; + } + + // Link this branch after the current branch + + pTmpBranch->pPrevBranch = m_pCurrBranch; + m_pCurrBranch->pNextBranch = pTmpBranch; + + // If current branch has a parent, the parent should be + // an alternative, and it's last branch should now point + // to this new branch. + + if ((pTmpBranch->pParentExpr = m_pCurrBranch->pParentExpr) != NULL) + { + pTmpExpr = pTmpBranch->pParentExpr; + flmAssert( pTmpExpr->eType == EXP_ALTERNATIVES); + pTmpExpr->exp.alternative.pLastBranch = pTmpBranch; + } + + // Current branch should now become this newly created branch + + m_pCurrBranch = pTmpBranch; + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Set a regular expression. Parse the expression, turning it into + constructs that can be used to test strings more easily. +*****************************************************************************/ +RCODE F_RegExp::setExpression( + FLMUNICODE * puzRegExp + ) +{ + RCODE rc = NE_XFLM_OK; + REG_EXP * pTmpExpr; + FLMUNICODE uzChar; + + while (*puzRegExp) + { + switch (*puzRegExp) + { + case '[': + if (RC_BAD( rc = createRegExp( EXP_CHAR_CLASS, &pTmpExpr))) + { + goto Exit; + } + if (RC_BAD( rc = parseCharClass( &puzRegExp, &pTmpExpr->exp.charClass))) + { + goto Exit; + } + break; + + case '\\': + if (RC_BAD( rc = parseEscape( &puzRegExp, NULL, &uzChar))) + { + goto Exit; + } + if (uzChar) + { + if (RC_BAD( rc = addLiteralChar( uzChar))) + { + goto Exit; + } + } + break; + + case '|': + if (RC_BAD( rc = startNewBranch())) + { + goto Exit; + } + + // Skip past the '|' + + puzRegExp++; + break; + + case '(': + if (RC_BAD( rc = startAlternative())) + { + goto Exit; + } + + // Skip past the '(' + + puzRegExp++; + break; + + case ')': + if (RC_BAD( rc = endAlternative())) + { + goto Exit; + } + + // Skip past the ')' + + puzRegExp++; + break; + + case '{': + case '+': + case '*': + case '?': + if (RC_BAD( rc = parseQuantifier( &puzRegExp))) + { + goto Exit; + } + break; + + case '.': + case '^': + case ']': + case '}': + rc = RC_SET( NE_XFLM_UNESCAPED_METACHAR); + goto Exit; + + default: + + // Add character to the literal expression we + // are saving up. + + if (RC_BAD( rc = addLiteralChar( *puzRegExp))) + { + goto Exit; + } + puzRegExp++; + break; + } + } + + // Output the last literal, if any + + if (RC_BAD( rc = saveLiteral())) + { + goto Exit; + } + + // Make sure we are not nested in parens. + + if (m_pCurrBranch->pParentExpr) + { + rc = RC_SET( NE_XFLM_UNEXPECTED_END_OF_EXPR); + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: Test a string to see if it matches the regular expression. +*****************************************************************************/ +FLMBOOL F_RegExp::testString( + IF_PosIStream * // pIStream + ) +{ + return( FALSE); +} diff --git a/version5/src/rfl.cpp b/version5/src/rfl.cpp new file mode 100644 index 0000000..c8bba6f --- /dev/null +++ b/version5/src/rfl.cpp @@ -0,0 +1,9150 @@ +//------------------------------------------------------------------------------ +// Desc: This module contains routine for roll forward logging. +// +// 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: rfl.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define MOD_512( uiNum) (FLMUINT)((uiNum) & 511) +#define ON_512_BYTE_BOUNDARY( uiNum) (!MOD_512(uiNum)) +#define ROUND_DOWN_TO_NEAREST_512( uiNum) \ + (FLMUINT)((uiNum) & (~((FLMUINT)511))) + +/******************************************************************** +Desc: +*********************************************************************/ +FINLINE FLMBOOL F_Rfl::useDataOnlyBlocks( + F_Db * pDb, + FLMUINT uiDataLen) +{ + if( uiDataLen > (pDb->m_pDatabase->m_uiBlockSize * 8) / 5) + { + return( TRUE); + } + else + { + return( FALSE); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +class F_RflOStream : public IF_OStream +{ +public: + + F_RflOStream( + F_Rfl * pRfl, + F_Db * pDb) + { + m_pRfl = pRfl; + m_pRfl->AddRef(); + m_pDb = pDb; + } + + virtual ~F_RflOStream() + { + if( m_pRfl) + { + m_pRfl->Release(); + } + } + + RCODE XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten = NULL); + + RCODE write( + IF_PosIStream * pIStream); + + FINLINE RCODE XFLMAPI close( void) + { + if( m_pRfl) + { + m_pRfl->Release(); + m_pRfl = NULL; + } + + return( NE_XFLM_OK); + } + +private: + + F_Rfl * m_pRfl; + F_Db * m_pDb; +}; + +/******************************************************************** +Desc: +*********************************************************************/ +F_Rfl::F_Rfl() +{ + m_pDatabase = NULL; + m_hBufMutex = F_MUTEX_NULL; + m_pCommitBuf = NULL; + m_pCurrentBuf = NULL; + m_uiRflWriteBufs = DEFAULT_RFL_WRITE_BUFFERS; + m_uiBufferSize = DEFAULT_RFL_BUFFER_SIZE; + f_memset( &m_Buf1, 0, sizeof( m_Buf1)); + f_memset( &m_Buf2, 0, sizeof( m_Buf2)); + m_bKeepRflFiles = FALSE; + m_uiRflMinFileSize = XFLM_DEFAULT_MIN_RFL_FILE_SIZE; + m_uiRflMaxFileSize = XFLM_DEFAULT_MAX_RFL_FILE_SIZE; + m_pFileHdl = NULL; + m_uiLastRecoverFileNum = 0; + f_memset( m_ucCurrSerialNum, 0, sizeof( m_ucCurrSerialNum)); + m_uiTransStartFile = 0; + m_uiTransStartAddr = 0; + m_ui64CurrTransID = 0; + m_ui64LastTransID = 0; + m_ui64LastLoggedCommitTransID = 0; + m_uiOperCount = 0; + m_uiRflReadOffset = 0; + m_uiFileEOF = 0; + m_pRestore = NULL; + m_pRestoreStatus = NULL; + f_memset( m_szRflDir, 0, sizeof( m_szRflDir)); + m_bRflDirSameAsDb = FALSE; + m_bCreateRflDir = FALSE; + f_memset( m_ucNextSerialNum, 0, sizeof( m_ucNextSerialNum)); + m_bRflVolumeOk = TRUE; + m_bRflVolumeFull = FALSE; + m_uiLastLfNum = 0; + m_eLastLfType = XFLM_LF_INVALID; + m_pIxCompareObject = NULL; + m_pCompareObject = NULL; + m_uiDisableCount = 0; +} + +/******************************************************************** +Desc: Destructor +*********************************************************************/ +F_Rfl::~F_Rfl() +{ + if (m_Buf1.pIOBuffer) + { + m_Buf1.pIOBuffer->Release(); + m_Buf1.pIOBuffer = NULL; + } + if (m_Buf2.pIOBuffer) + { + m_Buf2.pIOBuffer->Release(); + m_Buf2.pIOBuffer = NULL; + } + + if( m_Buf1.pBufferMgr) + { + flmAssert( !m_Buf1.pBufferMgr->havePendingIO() && + !m_Buf1.pBufferMgr->haveUsed()); + m_Buf1.pBufferMgr->Release(); + m_Buf1.pBufferMgr = NULL; + } + + if( m_Buf2.pBufferMgr) + { + flmAssert( !m_Buf2.pBufferMgr->havePendingIO() && + !m_Buf2.pBufferMgr->haveUsed()); + m_Buf2.pBufferMgr->Release(); + m_Buf2.pBufferMgr = NULL; + } + + if (m_hBufMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &m_hBufMutex); + } + + if (m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + m_pDatabase = NULL; + } + + if (m_pIxCompareObject) + { + m_pIxCompareObject->Release(); + } +} + +/******************************************************************** +Desc: Returns a boolean indicating whether or not we are at + the end of the RFL log - will only be TRUE when we are + doing recovery. +*********************************************************************/ +FLMBOOL F_Rfl::atEndOfLog( void) +{ + return( (!m_pRestore && + m_uiFileEOF && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes >= m_uiFileEOF && + m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + ? TRUE + : FALSE); +} + +/******************************************************************** +Desc: Gets the base RFL file name - does not have directory part. + This needs to be separate from the F_Rfl object so it can + be called without having to instantiate and set up an F_Rfl + object. +*********************************************************************/ +void rflGetBaseFileName( + FLMUINT uiFileNum, + char * pszBaseNameOut, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated) +{ + FLMUINT uiCnt; + FLMUINT uiDigit; + char szTmpBuf [14]; + char * pszTmp = &szTmpBuf [0]; + + // Output as eight digit hex number + + uiCnt = 0; + pszTmp += 7; + while (uiCnt < 8) + { + uiDigit = (FLMUINT)(uiFileNum & 0xF); + uiFileNum >>= 4; + if (uiDigit <= 9) + { + uiDigit += NATIVE_ZERO; + } + else + { + uiDigit += (NATIVE_LOWER_A - 10); + } + *pszTmp = (FLMBYTE)uiDigit; + pszTmp--; + uiCnt++; + } + + // Skip to end of digits and append ".log" to name + + f_strcpy( pszTmp + 9, ".log"); + if (*puiFileNameBufSize >= 13) + { + *puiFileNameBufSize = 12; + f_strcpy( pszBaseNameOut, szTmpBuf); + if (pbNameTruncated) + { + *pbNameTruncated = FALSE; + } + } + else + { + flmAssert( *puiFileNameBufSize); + (*puiFileNameBufSize)--; + if (*puiFileNameBufSize) + { + f_memcpy( pszBaseNameOut, szTmpBuf, *puiFileNameBufSize); + } + pszBaseNameOut [*puiFileNameBufSize] = 0; + if (pbNameTruncated) + { + *pbNameTruncated = TRUE; + } + } +} + +/******************************************************************** +Desc: Generates the full roll forward log file name. +*********************************************************************/ +void F_Rfl::getFullRflFileName( + FLMUINT uiFileNum, + char * pszRflFileName, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated) +{ + FLMUINT uiBaseNameSize; + FLMUINT uiLen = f_strlen( m_szRflDir); + FLMBOOL bNameTruncated = FALSE; + + // Must at least be room for a null byte to terminate the string. + + flmAssert( *puiFileNameBufSize); + if (uiLen > *puiFileNameBufSize - 1) + { + uiLen = *puiFileNameBufSize - 1; + if (uiLen) + { + f_memcpy( pszRflFileName, m_szRflDir, uiLen); + } + bNameTruncated = TRUE; + goto Exit; + } + + // Get the directory name. + + if (uiLen) + { + f_memcpy( pszRflFileName, m_szRflDir, uiLen); + + // See if we need to append a slash. + +#ifdef FLM_UNIX + if (m_szRflDir [uiLen - 1] != '/') +#else + if (m_szRflDir [uiLen - 1] != '/' && + m_szRflDir [uiLen - 1] != '\\') +#endif + { + + // See if we have room for one more character, plus null + + if (uiLen == *puiFileNameBufSize - 1) + { + bNameTruncated = TRUE; + goto Exit; + } +#ifdef FLM_UNIX + pszRflFileName [uiLen] = '/'; +#else + pszRflFileName [uiLen] = '\\'; +#endif + uiLen++; + } + } + + // See if there is room for at least one more byte plus a + // null byte. + + if (uiLen == *puiFileNameBufSize - 1) + { + bNameTruncated = TRUE; + goto Exit; + } + + // Get the base RFL file name. + + uiBaseNameSize = *puiFileNameBufSize - uiLen; + rflGetBaseFileName( uiFileNum, pszRflFileName + uiLen, + &uiBaseNameSize, &bNameTruncated); + uiLen += uiBaseNameSize; + +Exit: + + pszRflFileName [uiLen] = 0; + *puiFileNameBufSize = uiLen; + if (pbNameTruncated) + { + *pbNameTruncated = bNameTruncated; + } +} + +/******************************************************************** +Desc: Positions to the offset specified in the RFL file. +*********************************************************************/ +RCODE F_Rfl::positionTo( + FLMUINT uiFileOffset) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesToRead; + FLMUINT uiBytesRead; + + // Should never be attempting to position to something less + // than 512 - the header is stored in the first 512 bytes. + + flmAssert( uiFileOffset >= 512); + + // If the position is within our current buffer, see if we + // can adjust things without having to go back and re-read + // the buffer from disk. + + if (m_pCurrentBuf->uiRflBufBytes && + uiFileOffset >= m_pCurrentBuf->uiRflFileOffset && + uiFileOffset <= m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes) + { + + // Whatever is in the buffer beyond uiFileOffset is irrelevant + // and can be discarded. + + m_pCurrentBuf->uiRflBufBytes = uiFileOffset - + m_pCurrentBuf->uiRflFileOffset; + } + else + { + + // Populate the buffer from the 512 byte boundary that is just + // before the offset we are trying to position to. + + uiBytesToRead = MOD_512( uiFileOffset); + m_pCurrentBuf->uiRflFileOffset = ROUND_DOWN_TO_NEAREST_512( uiFileOffset); + m_pCurrentBuf->uiRflBufBytes = MOD_512( uiFileOffset); + if (m_pCurrentBuf->uiRflBufBytes) + { + if (RC_BAD( rc = m_pFileHdl->SectorRead( + m_pCurrentBuf->uiRflFileOffset, m_pCurrentBuf->uiRflBufBytes, + m_pCurrentBuf->pIOBuffer->getBuffer(), &uiBytesRead))) + { + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + goto Exit; + } + else if (uiBytesRead < m_pCurrentBuf->uiRflBufBytes) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Get the ACTUAL RFL directory, using as input parameters the + database version, the name of the database, and the + user specified RFL directory. Also return the database + prefix. +*********************************************************************/ +RCODE rflGetDirAndPrefix( + const char * pszDbFileName, + const char * pszRflDirIn, + char * pszRflDirOut) +{ + RCODE rc = NE_XFLM_OK; + char szDbPath [F_PATH_MAX_SIZE]; + char szBaseName [F_FILENAME_SIZE]; + char szPrefix [F_FILENAME_SIZE]; + F_DbSystem dbSystem; + + // Parse the database name into directory and base name + + if (RC_BAD( rc = gv_pFileSystem->pathReduce( pszDbFileName, + szDbPath, szBaseName))) + { + goto Exit; + } + + // Get the base path + + F_DbSystem::getDbBasePath( szPrefix, szBaseName, NULL); + + // Determine the RFL directory. If one was + // specified, it is whatever was specified. + // Otherwise, it is relative to the database + // directory. + + if (pszRflDirIn && *pszRflDirIn) + { + f_strcpy( pszRflDirOut, pszRflDirIn); + } + else + { + f_strcpy( pszRflDirOut, szDbPath); + } + + f_strcpy( szBaseName, szPrefix); + f_strcat( szBaseName, ".rfl"); + gv_pFileSystem->pathAppend( pszRflDirOut, szBaseName); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Set the RFL directory. If pszRflDir is NULL or empty string, + the RFL directory is set to the same directory as the + database. +*********************************************************************/ +RCODE F_Rfl::setRflDir( + const char * pszRflDir) +{ + // Better have set up the F_Database pointer. + + flmAssert( m_pDatabase != NULL); + + m_bRflDirSameAsDb = (!pszRflDir || !(*pszRflDir)) + ? TRUE + : FALSE; + + flmAssert( m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion); + + m_bCreateRflDir = TRUE; + return( rflGetDirAndPrefix( m_pDatabase->m_pszDbPath, + pszRflDir, m_szRflDir)); +} + +/******************************************************************** +Desc: Gets an RFL file name - based on DB name and RFL directory. +*********************************************************************/ +RCODE rflGetFileName( + const char * pszDbName, + const char * pszRflDir, + FLMUINT uiFileNum, + char * pszRflFileName) +{ + RCODE rc = NE_XFLM_OK; + char szBaseName [F_FILENAME_SIZE]; + FLMUINT uiBaseNameSize; + + // Get the full RFL file name. + + if (RC_BAD( rc = rflGetDirAndPrefix( pszDbName, pszRflDir, + pszRflFileName))) + { + goto Exit; + } + + uiBaseNameSize = sizeof( szBaseName); + rflGetBaseFileName( uiFileNum, szBaseName, &uiBaseNameSize, NULL); + + if (RC_BAD( rc = gv_pFileSystem->pathAppend( pszRflFileName, szBaseName))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Gets an RFL file number from the RFL file name. +*********************************************************************/ +FLMBOOL rflGetFileNum( + const char * pszRflFileName, + FLMUINT * puiFileNum) +{ + FLMBOOL bGotNum = FALSE; + char szDir[F_PATH_MAX_SIZE]; + char szBaseName[F_FILENAME_SIZE]; + char * pszTmp; + FLMUINT uiCharCnt; + + if( RC_BAD( gv_pFileSystem->pathReduce( pszRflFileName, szDir, szBaseName))) + { + goto Exit; + } + + // See if it has a .log extension. + + pszTmp = &szBaseName [0]; + while (*pszTmp && *pszTmp != '.') + { + pszTmp++; + } + + // If we do not have a .log extension, it is not a legitimate + // RFL file. + + if (f_stricmp( pszTmp, ".log") != 0) + { + goto Exit; + } + + // Parse out the name according to the rules for this DB version. + + *pszTmp = 0; // Set period to zero + pszTmp = &szBaseName [0]; + *puiFileNum = 0; + uiCharCnt = 0; + + // Name up to the period should be a hex number + + while (*pszTmp) + { + (*puiFileNum) <<= 4; + if (*pszTmp >= NATIVE_ZERO && *pszTmp <= NATIVE_NINE) + { + *puiFileNum += (FLMUINT)(*pszTmp - NATIVE_ZERO); + } + else if (*pszTmp >= NATIVE_LOWER_A && *pszTmp <= NATIVE_LOWER_F) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_LOWER_A) + 10); + } + else if (*pszTmp >= NATIVE_UPPER_A && *pszTmp <= NATIVE_UPPER_F) + { + *puiFileNum += ((FLMUINT)(*pszTmp - NATIVE_UPPER_A) + 10); + } + else + { + goto Exit; // Not a hex number + } + uiCharCnt++; + pszTmp++; + } + + // Better have been exactly 8 hex digits. + + bGotNum = (FLMBOOL)((uiCharCnt == 8) + ? TRUE + : FALSE); +Exit: + + return( bGotNum); +} + +/******************************************************************** +Desc: Sets up the RFL object - associating with a file, etc. +*********************************************************************/ +RCODE F_Rfl::setup( + F_Database * pDatabase, + const char * pszRflDir) +{ + RCODE rc = NE_XFLM_OK; + + // Better not already be associated with an F_Database object + + flmAssert( m_pDatabase == NULL); + m_pDatabase = pDatabase; + + // Allocate memory for the RFL buffers + +#ifndef FLM_UNIX + if (!gv_XFlmSysData.bOkToDoAsyncWrites) +#endif + { + m_uiRflWriteBufs = 1; + m_uiBufferSize = + DEFAULT_RFL_WRITE_BUFFERS * DEFAULT_RFL_BUFFER_SIZE; + } + + if (RC_BAD( rc = f_mutexCreate( &m_hBufMutex))) + { + goto Exit; + } + + if( (m_Buf1.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if( (m_Buf2.pBufferMgr = f_new F_IOBufferMgr) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + m_Buf1.pBufferMgr->enableKeepBuffer(); + m_Buf1.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf1.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if( RC_BAD( rc = m_Buf1.pBufferMgr->getBuffer( &m_Buf1.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_Buf2.pBufferMgr->enableKeepBuffer(); + m_Buf2.pBufferMgr->setMaxBuffers( m_uiRflWriteBufs); + m_Buf2.pBufferMgr->setMaxBytes( m_uiRflWriteBufs * m_uiBufferSize); + + if( RC_BAD( rc = m_Buf2.pBufferMgr->getBuffer( &m_Buf2.pIOBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + m_pCurrentBuf = &m_Buf1; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Set the RFL directory and prefix if necessary. + + if (RC_BAD( rc = setRflDir( pszRflDir))) + { + goto Exit; + } + + // Set up the compare object for comparing index keys. + + if ((m_pIxCompareObject = f_new IXKeyCompare) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Wait for the writes of a buffer to finish. This routine assumes + that the m_hBufMutex is locked when coming in. It will ALWAYS + unlock the mutex before exiting. +*********************************************************************/ +RCODE F_Rfl::waitForWrites( + F_SEM hWaitSem, + RFL_BUFFER * pBuffer, + FLMBOOL bIsWriter) +{ + RCODE rc = NE_XFLM_OK; + RCODE TempRc; + RFL_WAITER Waiter; + FLMBOOL bMutexLocked = TRUE; + + // Put self on the wait queue for the buffer. + + Waiter.uiThreadId = f_threadId(); + Waiter.bIsWriter = bIsWriter; + Waiter.hESem = hWaitSem; + + // Note: rc better be changed to success or write error + // by the process that signals us. + + rc = RC_SET( NE_XFLM_FAILURE); + + Waiter.pRc = &rc; + Waiter.pNext = NULL; + + if (pBuffer->pLastWaiter) + { + pBuffer->pLastWaiter->pNext = &Waiter; + } + else + { + pBuffer->pFirstWaiter = &Waiter; + + } + pBuffer->pLastWaiter = &Waiter; + + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + + // Now just wait to be signaled. + + if (RC_BAD( TempRc = f_semWait( Waiter.hESem, F_SEM_WAITFOREVER))) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + RC_UNEXPECTED_ASSERT( TempRc); +#endif + rc = TempRc; + } + else + { + // Process that signaled us better set the rc to something + // besides NE_XFLM_FAILURE. + + if (rc == NE_XFLM_FAILURE) + { +#ifdef FLM_NLM + EnterDebugger(); +#else + RC_UNEXPECTED_ASSERT( rc); +#endif + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + + return( rc); +} + +/******************************************************************** +Desc: If a commit is in progress, wait for it to finish. +*********************************************************************/ +RCODE F_Rfl::waitForCommit( + F_SEM hWaitSem) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + + // NOTE: If m_pCommitBuf is NULL it cannot be set to something + // non-NULL except by this thread when this thread ends the + // transaction. So, there is no need to lock the mutex and + // re-check if it is NULL. + + if (m_pCommitBuf) + { + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Check m_pCommitBuf again after locking mutex - may have + // finished. + + if (m_pCommitBuf) + { + bMutexLocked = FALSE; + rc = waitForWrites( hWaitSem, m_pCommitBuf, FALSE); + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + + return( rc); +} + +/******************************************************************** +Desc: Write out the header information for an RFL file. +*********************************************************************/ +RCODE F_Rfl::writeHeader( + FLMUINT uiFileNum, + FLMUINT uiEof, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucBuf[ 512]; + FLMUINT uiBytesWritten; + + flmAssert( m_pDatabase); + flmAssert( m_pFileHdl); + + f_memset( ucBuf, 0, sizeof( ucBuf)); + f_memcpy( &ucBuf [RFL_NAME_POS], RFL_NAME, RFL_NAME_LEN); + f_memcpy( &ucBuf [RFL_VERSION_POS], RFL_VERSION, RFL_VERSION_LEN); + UD2FBA( (FLMUINT32)uiFileNum, &ucBuf [RFL_FILE_NUMBER_POS]); + UD2FBA( (FLMUINT32)uiEof, &ucBuf [RFL_EOF_POS]); + + f_memcpy( &ucBuf [RFL_DB_SERIAL_NUM_POS], + m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf [RFL_SERIAL_NUM_POS], pucSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_memcpy( &ucBuf [RFL_NEXT_FILE_SERIAL_NUM_POS], pucNextSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_strcpy( &ucBuf [RFL_KEEP_SIGNATURE_POS], + (char *)((bKeepSignature) + ? RFL_KEEP_SIGNATURE + : RFL_NOKEEP_SIGNATURE)); + + // Write out the header + + if (RC_BAD( rc = m_pFileHdl->SectorWrite( 0L, 512, + ucBuf, sizeof( ucBuf), + NULL, &uiBytesWritten))) + { + // Remap disk full error + + if (rc == NE_XFLM_IO_DISK_FULL) + { + rc = RC_SET( NE_XFLM_RFL_DISK_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + + // Flush the file handle to ensure it is forced to disk. + + if (RC_BAD( rc = m_pFileHdl->Flush())) + { + // Remap disk full error + + if (rc == NE_XFLM_IO_DISK_FULL) + { + rc = RC_SET( NE_XFLM_RFL_DISK_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Verifies the header of an RFL file. +*********************************************************************/ +RCODE F_Rfl::verifyHeader( + FLMBYTE * pucHeader, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_pDatabase); + + // Check the RFL name and version number + + if( f_memcmp( &pucHeader [RFL_NAME_POS], RFL_NAME, + RFL_NAME_LEN) != 0) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + + if( f_memcmp( &pucHeader [RFL_VERSION_POS], RFL_VERSION, + RFL_VERSION_LEN) != 0) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + + // Verify the database serial number + + if( f_memcmp( &pucHeader [RFL_DB_SERIAL_NUM_POS], + m_pDatabase->m_lastCommittedDbHdr.ucDbSerialNum, + XFLM_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( NE_XFLM_BAD_RFL_DB_SERIAL_NUM); + goto Exit; + } + + // Verify the serial number that is expected to be on the + // RFL file. If pucSerialNum is NULL, we will not verify + // it. This is generally only done during recovery or restore + // when we are reading through multiple RFL files and we need + // to verify their serial numbers. + + if( pucSerialNum && + f_memcmp( &pucHeader [RFL_SERIAL_NUM_POS], + pucSerialNum, XFLM_SERIAL_NUM_SIZE) != 0) + { + rc = RC_SET( NE_XFLM_BAD_RFL_SERIAL_NUM); + goto Exit; + } + + // Verify the file number. + + if( uiFileNum != (FLMUINT)FB2UD( &pucHeader [RFL_FILE_NUMBER_POS])) + { + rc = RC_SET( NE_XFLM_BAD_RFL_FILE_NUMBER); + goto Exit; + } + + // Save serial numbers from the header. + + f_memcpy( m_ucCurrSerialNum, &pucHeader [RFL_SERIAL_NUM_POS], + XFLM_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, &pucHeader [RFL_NEXT_FILE_SERIAL_NUM_POS], + XFLM_SERIAL_NUM_SIZE); + + // Save some things from the header. + + m_uiFileEOF = (FLMUINT)FB2UD( &pucHeader [RFL_EOF_POS]); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Opens an RFL file. Verifies the serial number for 4.3 dbs. +*********************************************************************/ +RCODE F_Rfl::openFile( + F_SEM hWaitSem, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum) +{ + RCODE rc = NE_XFLM_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMUINT uiFileNameSize; + FLMBYTE ucBuf [512]; + FLMUINT uiBytesRead; + + flmAssert( m_pDatabase); + + // If we have a file open and it is not the file number + // passed in, close it. + + if (m_pFileHdl) + { + if (m_pCurrentBuf->uiCurrFileNum != uiFileNum) + { + if (RC_BAD( rc = waitForCommit( hWaitSem))) + { + goto Exit; + } + closeFile(); + } + else + { + goto Exit; // Will return NE_XFLM_OK + } + } + else + { + // Should not be able to be in the middle of a commit + // if we don't have a file open! + + flmAssert( !m_pCommitBuf); + } + + // Generate the log file name. + + uiFileNameSize = sizeof( szRflFileName); + getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL); + + // Open the file. + + if (RC_BAD( rc = gv_pFileSystem->OpenBlockFile( szRflFileName, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, + 512, &m_pFileHdl))) + { + goto Exit; + } + + // Read the header. + + if (RC_BAD( rc = m_pFileHdl->SectorRead( 0, 512, ucBuf, + &uiBytesRead))) + { + if (rc == NE_XFLM_IO_END_OF_FILE) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + } + else + { + m_bRflVolumeOk = FALSE; + } + goto Exit; + } + + // If there is not enough data in the buffer, it is not an + // RFL file. + + if (uiBytesRead < 512) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + + // Verify the header information + + if (RC_BAD( rc = verifyHeader( ucBuf, uiFileNum, pucSerialNum))) + { + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 0; + m_pCurrentBuf->uiCurrFileNum = uiFileNum; + +Exit: + + if( RC_BAD( rc)) + { + waitForCommit( hWaitSem); + closeFile(); + } + + return( rc); +} + +/******************************************************************** +Desc: Creates a new roll forward log file. +*********************************************************************/ +RCODE F_Rfl::createFile( + F_Db * pDb, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature) +{ + RCODE rc = NE_XFLM_OK; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMUINT uiFileNameSize; + + flmAssert( m_pDatabase); + + // Better not be trying to create the current file + + flmAssert( uiFileNum != m_pCurrentBuf->uiCurrFileNum); + + // If we have a file open, close it. + + if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) + { + goto Exit; + } + closeFile(); + + // Generate the log file name. + + uiFileNameSize = sizeof( szRflFileName); + getFullRflFileName( uiFileNum, szRflFileName, &uiFileNameSize, NULL); + + // Delete the file if it already exists - don't care + // about return code here + + (void)gv_pFileSystem->Delete( szRflFileName); + + // If DB is 4.3 or greater and we are in the same directory as + // our database files, see if we need to create the + // subdirectory. Otherwise, the RFL directory should already + // have been created. If the directory already exists, it is + // OK - we only try this the first time after setRflDir is + // called - to either verify that the directory exists, or if + // it doesn't, to create it. + + if (m_bCreateRflDir) + { + + // If it already exists, don't attempt to create it. + + if (RC_BAD( rc = gv_pFileSystem->Exists( m_szRflDir))) + { + if (rc != NE_XFLM_IO_PATH_NOT_FOUND && + rc != NE_XFLM_IO_INVALID_FILENAME) + { + goto Exit; + } + else + { + if (RC_BAD( rc = + gv_pFileSystem->CreateDir( m_szRflDir))) + { + goto Exit; + } + } + } + m_bCreateRflDir = FALSE; + } + + // Create the file + + if (RC_BAD( rc = gv_pFileSystem->CreateBlockFile( szRflFileName, + XFLM_IO_RDWR | XFLM_IO_EXCL | XFLM_IO_SH_DENYNONE | XFLM_IO_DIRECT, + 512, &m_pFileHdl))) + { + goto Exit; + } + + // Initialize the header. + + if (RC_BAD( rc = writeHeader( uiFileNum, 0, + pucSerialNum, pucNextSerialNum, bKeepSignature))) + { + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 512; + m_pCurrentBuf->uiCurrFileNum = uiFileNum; +Exit: + + // Close the RFL log file AND delete it if we were not successful. + + if (RC_BAD( rc)) + { + closeFile(); + (void)gv_pFileSystem->Delete( szRflFileName); + } + return( rc); +} + +/******************************************************************** +Desc: Copy last partial sector of last buffer written (or to be + written) into a new buffer. +*********************************************************************/ +void F_Rfl::copyLastSector( + RFL_BUFFER * pBuffer, + FLMBYTE * pucOldBuffer, + FLMBYTE * pucNewBuffer, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + FLMUINT uiOldBufBytes = pBuffer->uiRflBufBytes; + + // If we will be starting a new file, no need to keep any of + // what is in the buffer. Only the current packet needs to + // be copied - at the beginning of the buffer. + + // OTHERWISE: + + // If there are fewer than 512 bytes in the buffer, we simply + // keep them and keep appending to the buffer the next time + // we output stuff. The beginning of the buffer must ALWAYS be + // a 512 byte boundary in the file, because we want to always + // do our writing on 512 byte boundaries - because of direct IO. + + // If the number of bytes in the buffer is over 512 and it is + // evenly divisible by 512, we can clear the buffer. Otherwise, + // we want to move the extra bytes over the last 512 byte boundary + // down to the beginning of the buffer and adjust the buffer bytes + // to reflect just these left-over bytes. + + if (bStartingNewFile) + { + pBuffer->uiRflBufBytes = 0; + pBuffer->uiRflFileOffset = 512; + } + else if (pBuffer->uiRflBufBytes >= 512) + { + + // See if the number of bytes in the buffer is an exact + // multiple of 512. + + if (pBuffer->uiRflBufBytes & 511) // Not exact multiple + { + + // Round m_uiRflBufBytes down to next 512 byte boundary + + FLMUINT ui512Offset = ROUND_DOWN_TO_NEAREST_512( + pBuffer->uiRflBufBytes); + + // Move all bytes above the nearest 512 byte boundary + // down to the beginning of the buffer and adjust + // pBuffer->uiRflBufBytes and + // pBuffer->uiRflFileOffset accordingly. + + f_memcpy( pucNewBuffer, &pucOldBuffer[ui512Offset], + pBuffer->uiRflBufBytes - ui512Offset); + pBuffer->uiRflBufBytes -= ui512Offset; + pBuffer->uiRflFileOffset += ui512Offset; + } + else + { + pBuffer->uiRflFileOffset += pBuffer->uiRflBufBytes; + pBuffer->uiRflBufBytes = 0; + } + } + else if (pucNewBuffer != pucOldBuffer) + { + f_memcpy( pucNewBuffer, pucOldBuffer, pBuffer->uiRflBufBytes); + } + + if (uiCurrPacketLen) + { + flmAssert( uiOldBufBytes + uiCurrPacketLen <= m_uiBufferSize); + f_memmove( &pucNewBuffer [pBuffer->uiRflBufBytes], + &pucOldBuffer [uiOldBufBytes], + uiCurrPacketLen); + } +} + +/******************************************************************** +Desc: Flush the RFL data from the buffer to disk. +*********************************************************************/ +RCODE F_Rfl::flush( + F_Db * pDb, + RFL_BUFFER * pBuffer, + FLMBOOL bFinalWrite, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesWritten; + F_IOBuffer * pNewBuffer; + F_IOBuffer * pAsyncBuf = NULL; + FLMBYTE * pucOldBuffer; + FLMUINT uiFileOffset; + FLMUINT uiBufBytes; + + if (m_pFileHdl && pBuffer->uiRflBufBytes) + { + // Must wait for stuff in committing buffer, if any, before + // going ahead here. + + if (pBuffer != m_pCommitBuf) + { + if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) + { + goto Exit; + } + } + + if (m_uiRflWriteBufs > 1 && m_pFileHdl->CanDoAsync()) + { + pAsyncBuf = pBuffer->pIOBuffer; + } + + if ((FLMUINT)(-1) - pBuffer->uiRflFileOffset <= + pBuffer->uiRflBufBytes) + { + rc = RC_SET( NE_XFLM_DB_FULL); + goto Exit; + } + + pucOldBuffer = pBuffer->pIOBuffer->getBuffer(); + uiFileOffset = pBuffer->uiRflFileOffset; + uiBufBytes = pBuffer->uiRflBufBytes; + if (m_uiRflWriteBufs > 1) + { + if( RC_BAD( rc = pBuffer->pBufferMgr->getBuffer( + &pNewBuffer, + m_uiBufferSize, m_uiBufferSize))) + { + goto Exit; + } + + // No need to copy data if it is the final write, + // because it won't be reused anyway - the data for + // the next transaction has already been copied to + // another buffer. + + if (!bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pNewBuffer->getBuffer(), + uiCurrPacketLen, bStartingNewFile); + } + } + + rc = m_pFileHdl->SectorWrite( uiFileOffset, uiBufBytes, + pucOldBuffer, + m_uiBufferSize, pAsyncBuf, + &uiBytesWritten, FALSE); + if( m_uiRflWriteBufs == 1) + { + + // We are counting on the fact that the write completed. + // When we only have one buffer, we cannot do async + // writes. + + flmAssert( !pAsyncBuf); + if (RC_OK( rc) && !bFinalWrite) + { + copyLastSector( pBuffer, pucOldBuffer, pucOldBuffer, + uiCurrPacketLen, bStartingNewFile); + } + + // DO NOT call notifyComplete - that would put + // pBuffer->pIOBuffer into the avail list, and we + // don't want that. We simply want to keep + // reusing it. + + } + else + { + + // No need to call copyLastSector, because it was called + // above before calling SectorWrite. The part of the + // old buffer that needs to be transferred to the new + // buffer has already been transferred. + + if (!pAsyncBuf) + { + pBuffer->pIOBuffer->notifyComplete( rc); + } + pBuffer->pIOBuffer = pNewBuffer; + } + + if (RC_BAD( rc)) + { + // Remap disk full error + + if (rc == NE_XFLM_IO_DISK_FULL) + { + rc = RC_SET( NE_XFLM_RFL_DISK_FULL); + m_bRflVolumeFull = TRUE; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Switch buffers. This routine assumes the m_hBufMutex is locked. +*********************************************************************/ +void F_Rfl::switchBuffers( void) +{ + RFL_BUFFER * pOldBuffer = m_pCurrentBuf; + + if (m_pCurrentBuf == &m_Buf1) + { + m_pCurrentBuf = &m_Buf2; + } + else + { + m_pCurrentBuf = &m_Buf1; + } + m_pCurrentBuf->bTransInProgress = pOldBuffer->bTransInProgress; + m_pCurrentBuf->uiCurrFileNum = pOldBuffer->uiCurrFileNum; + m_pCurrentBuf->uiRflBufBytes = pOldBuffer->uiRflBufBytes; + m_pCurrentBuf->uiRflFileOffset = pOldBuffer->uiRflFileOffset; + + if (pOldBuffer->uiRflBufBytes) + { + copyLastSector( m_pCurrentBuf, pOldBuffer->pIOBuffer->getBuffer(), + m_pCurrentBuf->pIOBuffer->getBuffer(), 0, FALSE); + } +} + +/******************************************************************** +Desc: Wait for all RFL transaction writes to be finished. The caller + has the write lock on the database, which will prevent further + writes to the RFL. +*********************************************************************/ +FLMBOOL F_Rfl::seeIfRflWritesDone( + F_SEM hWaitSem, + FLMBOOL bForceWait) +{ + FLMBOOL bWritesDone; + + f_mutexLock( m_hBufMutex); + + if (!bForceWait) + { + bWritesDone = (FLMBOOL)((m_pCurrentBuf->pFirstWaiter || m_pCommitBuf) + ? FALSE + : TRUE); + + if( bWritesDone) + { + m_pCurrentBuf->uiRflBufBytes = 0; + } + + f_mutexUnlock( m_hBufMutex); + } + else + { + // If the current buffer has a waiter, add self to that list + // to wait, because it will be notified after the commit buffer + // has been notified. Otherwise, if there is a commit in + // progress, add self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + + // If bTransInProgress is TRUE and m_pCommitBuf is NULL + // then this thread is the current transaction, and + // nobody is going to wake up the first waiter until we + // are done! Hence, we must wake him up. + + if (!m_pCommitBuf) + { + + // If m_pCommitBuf is NULL, this could only be possible if + // there is a transaction in progress. Otherwise, there + // would not have been a pFirstWaiter, because when + // the commit buffer finishes writing, if there is a + // waiter, it will set commitbuf=currentbuf if there + // is no transaction active. + + flmAssert( m_pCurrentBuf->bTransInProgress); + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( NE_XFLM_OK, TRUE); + (void)waitForWrites( hWaitSem, m_pCommitBuf, FALSE); + } + else + { + FLMBOOL bSaveTransInProgress = m_pCurrentBuf->bTransInProgress; + + // Must set bTransInProgress to FALSE so that when the writer + // of m_pCommitBuf finishes, it will signal the first waiter + // on m_pCurrentBuf. If we don't do this, m_pCommitBuf will + // simply be set to NULL, and the first waiter will never + // be woke up. + + m_pCurrentBuf->bTransInProgress = FALSE; + (void)waitForWrites( hWaitSem, m_pCurrentBuf, FALSE); + + // It is OK to restore the trans in progress flag to what it + // was before, because whoever called this routine has a lock + // on the database, and it is his trans-in-progress state + // that should be preserved. No other thread will have been + // able to change that state because the database is locked. + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = bSaveTransInProgress; + f_mutexUnlock( m_hBufMutex); + } + + m_pCurrentBuf->uiRflBufBytes = 0; + } + else if (m_pCommitBuf) + { + (void)waitForWrites( hWaitSem, m_pCommitBuf, FALSE); + } + else + { + f_mutexUnlock( m_hBufMutex); + } + bWritesDone = TRUE; + } + return( bWritesDone); +} + +/******************************************************************** +Desc: Wake up the first thread that is waiting on the commit buffer. +*********************************************************************/ +void F_Rfl::wakeUpWaiter( + RCODE rc, + FLMBOOL bIsWriter) +{ + F_SEM hESem; + +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( bIsWriter); +#else + if (bIsWriter) + { + flmAssert( m_pCommitBuf->pFirstWaiter->bIsWriter); + } + else + { + flmAssert( !m_pCommitBuf->pFirstWaiter->bIsWriter); + } +#endif + + *(m_pCommitBuf->pFirstWaiter->pRc) = rc; + hESem = m_pCommitBuf->pFirstWaiter->hESem; + if ((m_pCommitBuf->pFirstWaiter = + m_pCommitBuf->pFirstWaiter->pNext) == NULL) + { + m_pCommitBuf->pLastWaiter = NULL; + } + f_semSignal( hESem); +} + +/******************************************************************** +Desc: Wait for the transaction writes to be finished. +*********************************************************************/ +RCODE F_Rfl::completeTransWrites( + F_Db * pDb, + FLMBOOL bCommitting, + FLMBOOL bOkToUnlock) +{ + RCODE rc = NE_XFLM_OK; + RCODE tmpRc; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bNotifyWaiters = FALSE; + FLMBOOL bDbUnlocked = FALSE; + XFLM_DB_STATS * pDbStats = NULL; + F_TMSTAMP StartTime; + + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + m_pCurrentBuf->bTransInProgress = FALSE; + + flmAssert( pDb->m_uiFlags & FDB_HAS_WRITE_LOCK); + + // When recovering or restoring all we need to do is write out + // the database header. + + if (pDb->m_uiFlags & FDB_REPLAYING_RFL) + { + if (pDb->m_bHadUpdOper && + m_pCurrentBuf->bOkToWriteHdrs) + { + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, + pDb->m_pSFileHdl, + &m_pCurrentBuf->dbHdr, + &m_pCurrentBuf->cpHdr, FALSE))) + { + m_pDatabase->setMustCloseFlags( rc, FALSE); + } + } + goto Exit; + } + + // Handle empty transactions differently. + // These transactions should not do any writing and do not need to + // wait for all writes to complete, unless the bOkToUnlock flag + // is set to FALSE. In that case they must wait for all writes + // to complete before unlocking. + + if (!pDb->m_bHadUpdOper) + { + // If the current buffer has a waiter, add self to that list + // to wait, because it will be notified after the commit buffer + // has been notified. Otherwise, if there is a commit in + // progress, add self to that list to wait. + + if (m_pCurrentBuf->pFirstWaiter) + { + // If m_pCommitBuf is NULL then nobody is going to wake up + // the first waiter - we must do it. + + if (!m_pCommitBuf) + { + if (bOkToUnlock) + { + pDb->unlinkFromTransList( bCommitting); + bDbUnlocked = TRUE; + } + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( NE_XFLM_OK, TRUE); + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void)waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); + } + } + else if (!bOkToUnlock) + { + bMutexLocked = FALSE; + (void)waitForWrites( pDb->m_hWaitSem, m_pCurrentBuf, FALSE); + } + } + else if (m_pCommitBuf) + { + if (!bOkToUnlock) + { + bMutexLocked = FALSE; + rc = waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); + } + } + goto Exit; + } + + // If there is a transaction committing, put self into + // the wait list on the current buffer. When the committer + // finishes, he will wake up the first thread in the list + // and that thread will commit the buffer. + + if (m_pCommitBuf) + { + FLMBOOL bIsWriter; + + // Another thread has to be doing the writes to m_pCommitBuf, + // which means that m_pCurrentBuf better not be equal to + // m_pCommitBuf. + + flmAssert( m_pCommitBuf != m_pCurrentBuf); + + // If there are no waiters, we are the first one, so when + // we get signaled, we should proceed and do the write. + + bIsWriter = m_pCurrentBuf->pFirstWaiter ? FALSE : TRUE; + if (bOkToUnlock) + { + pDb->unlinkFromTransList( bCommitting); + bDbUnlocked = TRUE; + } + bMutexLocked = FALSE; + rc = waitForWrites( pDb->m_hWaitSem, m_pCurrentBuf, bIsWriter); + + // If we were the first one in the queue, we must now + // do the write. + + if (!bIsWriter) + { + goto Exit; + } + + // First one in the queue, fall through to do the write. + + // The thread that woke me up better have set m_pCommitBuf + // See below. + + flmAssert( m_pCommitBuf); + } + else if (m_pCurrentBuf->pFirstWaiter) + { + + // Another thread is ready to commit the next set of + // buffers, but just needs to be woke up. + + if (bOkToUnlock) + { + pDb->unlinkFromTransList( bCommitting); + bDbUnlocked = TRUE; + } + + // Need to set things up for that first waiter and get him + // going. + + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + + // Wait for the write to be completed. + + bMutexLocked = FALSE; + rc = waitForWrites( pDb->m_hWaitSem, m_pCommitBuf, FALSE); + goto Exit; + } + else + { + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + if (bOkToUnlock) + { + pDb->unlinkFromTransList( bCommitting); + bDbUnlocked = TRUE; + } + f_mutexUnlock( m_hBufMutex); + bMutexLocked = FALSE; + } + + // NOTE: From this point on we use tmpRc because we don't want to + // lose the rc that may have been set above in the call to + // waitForWrites + + // At this point the mutex better not be locked. + + flmAssert( !bMutexLocked); + bNotifyWaiters = TRUE; + + if( (pDbStats = pDb->m_pDbStats) != NULL) + { + f_timeGetTimeStamp( &StartTime); + } + + // Must write out whatever we have in the commit buffer before + // unlocking the database. + + if (RC_BAD( tmpRc = flush( pDb, m_pCommitBuf, TRUE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + goto Exit; + } + + // Wait for any pending IO off of the log buffer + + if (RC_BAD( tmpRc = m_pCommitBuf->pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + goto Exit; + } + + // Force the RFL writes to disk if necessary. + // NOTE: It is possible for m_pFileHdl to be NULL at this point if + // there were no operations actually logged. This happens in + // FlmDbUpgrade (see flconvrt.cpp). Even though nothing was logged + // the transaction is not an empty transaction, because it still needs + // to write out the log header. + + if (m_pFileHdl) + { + if (RC_BAD( tmpRc = m_pFileHdl->Flush())) + { + + // Remap disk full error + + if (tmpRc == NE_XFLM_IO_DISK_FULL) + { + rc = RC_SET( NE_XFLM_RFL_DISK_FULL); + m_bRflVolumeFull = TRUE; + } + else if (RC_OK( rc)) + { + rc = tmpRc; + } + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + + // Write the log header + + if (m_pCommitBuf->bOkToWriteHdrs) + { + if (RC_BAD( tmpRc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, + pDb->m_pSFileHdl, + &m_pCommitBuf->dbHdr, + &m_pCommitBuf->cpHdr, FALSE))) + { + if (RC_OK( rc)) + { + rc = tmpRc; + } + m_pDatabase->setMustCloseFlags( tmpRc, FALSE); + goto Exit; + } + } + +Exit: + + if (!bDbUnlocked && bOkToUnlock) + { + pDb->unlinkFromTransList( bCommitting); + } + + if (bNotifyWaiters) + { + FLMUINT uiNumFinished = 1; // For self + + flmAssert( !bMutexLocked); + f_mutexLock( m_hBufMutex); + bMutexLocked = TRUE; + + // Wake up any waiters + + while (m_pCommitBuf->pFirstWaiter) + { + uiNumFinished++; + wakeUpWaiter( rc, FALSE); + } + + // If there are waiters on the current buffer, the first one + // should be woke up so it can start the next set of writes. + + if (m_pCurrentBuf->pFirstWaiter && !m_pCurrentBuf->bTransInProgress) + { + flmAssert( m_pCurrentBuf != m_pCommitBuf); + m_pCommitBuf = m_pCurrentBuf; + switchBuffers(); + wakeUpWaiter( rc, TRUE); + } + else + { + m_pCommitBuf = NULL; + } + if (pDbStats) + { + flmAddElapTime( &StartTime, + &pDbStats->UpdateTransStats.GroupCompletes.ui64ElapMilli); + pDbStats->UpdateTransStats.GroupCompletes.ui64Count++; + pDbStats->bHaveStats = TRUE; + pDbStats->UpdateTransStats.ui64GroupFinished += uiNumFinished; + } + } + + if (bMutexLocked) + { + f_mutexUnlock( m_hBufMutex); + } + return( rc); +} + +/******************************************************************** +Desc: Flush all completed packets out of the RFL buffer, and shift + the new partial packet down. This guarantees that there is + now room in the buffer for the maximum packet size. +*********************************************************************/ +RCODE F_Rfl::shiftPacketsDown( + F_Db * pDb, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile) +{ + RCODE rc = NE_XFLM_OK; + + // The call to flush will move whatever needs to be moved from + // the current buffer into a new buffer if multiple buffers are + // being used. If only one buffer is being used, it will move + // the part of the packet that needs to be moved down to the + // beginning of the buffer - AFTER writing out the buffer. + + if (RC_BAD( rc = flush( pDb, m_pCurrentBuf, FALSE, + uiCurrPacketLen, bStartingNewFile))) + { + goto Exit; + } + + // NOTE: If multiple buffers are being used, whatever was moved + // to the new buffer has not yet been written out + + if (bStartingNewFile) + { + if( RC_BAD( rc = waitPendingWrites())) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Determine if we should start a new file. If we are over the + low limit, and the bDoNewIfOverLowLimit flag is set, we + will start a new log file. Or, if this packet size would + put us over the upper limit, we will start a new log file. +*********************************************************************/ +RCODE F_Rfl::seeIfNeedNewFile( + F_Db * pDb, + FLMUINT uiPacketLen, + FLMBOOL bDoNewIfOverLowLimit) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE ucNextSerialNum [XFLM_SERIAL_NUM_SIZE]; + + flmAssert( m_pDatabase); + + // If the keep files flag is FALSE, we won't start + // a new file. NOTE: This should ALWAYS be false + // for pre 4.3 databases. + + if( !m_bKeepRflFiles) + { + goto Exit; + } + + // VERY IMPORTANT NOTE: It is preferrable that we keep transactions + // entirely contained in the same RFL file if at all possible. Note + // that it is NOT a hard and fast requirement. The system will work + // just fine if we don't. However, it would be nice if RFL files + // always ended with a commit or abort packet. This preferences is + // due to what happens after a restore operation. After a restore + // operation, we always need to start a new RFL file, but if possible, + // we would like that new RFL file to be the next one in the sequence + // after the last RFL file that was restored. We can only do this if + // we were able to restore EVERY transaction that was in the last + // restored RFL file - which we can only do if the last restored RFL + // file ended with a commit or abort packet. + // To accomplish this end, we try to roll to new files on the first + // transaction begin packet that occurs after we have exceeded our + // low threshold - which is why bDoNewIfOverLowLimit is only set to + // TRUE on transaction begin packets. It is set to FALSE on other + // packets so that we will continue logging the transaction in the + // same file that we started the transaction in - if possible. The + // only thing that will cause a non-transaction-begin packet to roll + // to a new file is if we would exceed the high limit. + + if ((bDoNewIfOverLowLimit && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes >= + m_uiRflMinFileSize) || + (m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes + + uiPacketLen >= m_uiRflMaxFileSize)) + { + FLMUINT uiCurrFileEOF = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + // Shift the current packet to the beginning of the buffer. + // Any packets in the buffer before that one will be written + // out to the current file. + + if (RC_BAD( rc = shiftPacketsDown( pDb, uiPacketLen, TRUE))) + { + goto Exit; + } + + // Update the header of the current file and close it. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, + uiCurrFileEOF, + m_ucCurrSerialNum, m_ucNextSerialNum, TRUE))) + { + goto Exit; + } + + // Truncate the file. + + if (!ON_512_BYTE_BOUNDARY( uiCurrFileEOF)) + { + uiCurrFileEOF = ROUND_DOWN_TO_NEAREST_512( uiCurrFileEOF) + 512; + } + + if (RC_BAD( rc = m_pFileHdl->Truncate( uiCurrFileEOF))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Get the next serial number that will be used for the RFL + // file after this one. + + if (RC_BAD( rc = f_createSerialNumber( ucNextSerialNum))) + { + goto Exit; + } + + // Create next file in the sequence. + + // Use the next serial number stored in the FDB's log header + // for the serial number on this RFL file. Use the serial + // number we just generated as the next RFL serial number. + + if (RC_BAD( rc = createFile( pDb, m_pCurrentBuf->uiCurrFileNum + 1, + m_ucNextSerialNum, ucNextSerialNum, TRUE))) + { + goto Exit; + } + + // Move the next serial number to the current serial number + // and the serial number we generated above into the next + // serial number. + + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + } + +Exit: + return( rc); +} + +/******************************************************************** +Desc: Finish the current RFL file - set up so that next + transaction will begin a new RFL file. +*********************************************************************/ +RCODE F_Rfl::finishCurrFile( + F_Db * pDb, + FLMBOOL bNewKeepState) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDbLocked = FALSE; + FLMUINT uiTransFileNum; + FLMUINT uiTransOffset; + FLMUINT uiTruncateSize; + XFLM_DB_HDR * pUncommittedDbHdr; + XFLM_DB_HDR checkpointDbHdr; + + // Make sure we don't have a transaction going + + if (pDb->m_eTransType != XFLM_NO_TRANS) + { + rc = RC_SET( NE_XFLM_TRANS_ACTIVE); + goto Exit; + } + + // Make sure there is no active backup running + + m_pDatabase->lockMutex(); + if (m_pDatabase->m_bBackupActive) + { + m_pDatabase->unlockMutex(); + rc = RC_SET( NE_XFLM_BACKUP_ACTIVE); + goto Exit; + } + m_pDatabase->unlockMutex(); + + // Lock the database - need to prevent update + // transactions and checkpoint thread from running. + + if (RC_BAD( rc = pDb->lockExclusive( XFLM_NO_TIMEOUT))) + { + goto Exit; + } + bDbLocked = TRUE; + + // Must wait for all RFL writes before switching files. + + (void)seeIfRflWritesDone( pDb->m_hWaitSem, TRUE); + + // Better not be in the middle of a transaction. + + flmAssert( !m_ui64CurrTransID); + + pUncommittedDbHdr = &m_pDatabase->m_uncommittedDbHdr; + + // Don't want to copy last committed log header into + // uncommitted log header if bNewKeepState is TRUE because + // the caller has already done it, and has made modifications + // to the uncommitted log header that we don't want to lose. + + if (!bNewKeepState) + { + f_memcpy( pUncommittedDbHdr, &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + + // If we are in a no-keep state, but we were not told that + // we have a new keep state, we cannot roll to the next + // RFL file, because a checkpoint has not been done. This is + // not an error - it is just the case where FlmDbConfig was + // asked to roll to the next RFL file when the keep flag was + // still FALSE. + + if (!pUncommittedDbHdr->ui8RflKeepFiles) + { + goto Exit; + } + } + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + pUncommittedDbHdr->ucLastTransRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, + pUncommittedDbHdr->ucNextRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + uiTransFileNum = (FLMUINT)pUncommittedDbHdr->ui32RflCurrFileNum; + uiTransOffset = (FLMUINT)pUncommittedDbHdr->ui32RflLastTransOffset; + + // If ui32RflLastTransOffset is zero, there is no need to go set + // up to go to the next file, because we are already poised to do so at + // the beginning of the next transaction. Just return if this is the + // case. Same for if the file does not exist. + + if (!uiTransOffset) + { + if (!bNewKeepState) + { + goto Exit; + } + } + else if (RC_BAD( rc = openFile( pDb->m_hWaitSem, uiTransFileNum, + m_ucCurrSerialNum))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + if (!bNewKeepState) + { + goto Exit; + } + } + else + { + goto Exit; + } + } + else + { + + // At this point, we know the file exists, so we will update + // its header and then update the log header. Note that we + // use the keep RFL state from the last committed log header, + // not the uncommitted log header - because it will contain + // the correct keep-state for the current RFL file. + + if (RC_BAD( rc = writeHeader( m_pCurrentBuf->uiCurrFileNum, uiTransOffset, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles + ? TRUE + : FALSE))) + { + goto Exit; + } + + // Truncate the file down to its EOF size - the nearest 512 byte boundary. + + uiTruncateSize = uiTransOffset; + if (!ON_512_BYTE_BOUNDARY( uiTruncateSize)) + { + uiTruncateSize = ROUND_DOWN_TO_NEAREST_512( uiTruncateSize) + 512; + } + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + goto Exit; + } + + // Close the file handle. + + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + + // Set things up in the log header to go to the next file when + // we begin the next transaction. NOTE: NO need to lock the + // mutex, because nobody but an update transaction looks at + // the uncommitted log header. + + uiTransFileNum++; + pUncommittedDbHdr->ui32RflCurrFileNum = (FLMUINT32)uiTransFileNum; + } + + // Generate a new current serial number if bNewKeepState is + // TRUE. Otherwise, move the next serial number into the current + // serial number. + + if (bNewKeepState) + { + if (RC_BAD( rc = f_createSerialNumber( m_ucCurrSerialNum))) + { + goto Exit; + } + } + else + { + f_memcpy( m_ucCurrSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + } + + // Always generate a new next serial number. + + if (RC_BAD( rc = f_createSerialNumber( m_ucNextSerialNum))) + { + goto Exit; + } + + // Set transaction offset to zero. This will force the + // next RFL file to be created on the next transaction + // begin. It will be created even if it is already + // there. + + pUncommittedDbHdr->ui32RflLastTransOffset = 0; + f_memcpy( pUncommittedDbHdr->ucLastTransRflSerialNum, + m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); + f_memcpy( pUncommittedDbHdr->ucNextRflSerialNum, + m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + + // Set the CP file number and CP offset to point into the new file. + // The outer code (FlmDbConfig) has done a checkpoint and the database + // is still locked. We need to set these values here, otherwise + // if we crash before the next checkpoint, recovery will start in the + // old RFL file, causing an NE_XFLM_BAD_RFL_SERIAL_NUM to be returned when + // traversing from the old RFL file to the new RFL file. + // NOTE: These changes must be made to the uncommitted log header AND + // the CP log header (so that they will be written out even + // though we are not forcing a checkpoint). + + if (bNewKeepState) + { + // Do a quick check to see if it looks like we are in a + // checkpointed state + + if( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles && + m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset > 512) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + + f_memcpy( &checkpointDbHdr, + &m_pDatabase->m_checkpointDbHdr, sizeof( XFLM_DB_HDR)); + checkpointDbHdr.ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum; + pUncommittedDbHdr->ui32RflLastCPFileNum = (FLMUINT32)uiTransFileNum; + checkpointDbHdr.ui32RflLastCPOffset = 512; + pUncommittedDbHdr->ui32RflLastCPOffset = 512; + } + + // Write out the db header to disk. + + if (RC_BAD( rc = m_pDatabase->writeDbHdr( pDb->m_pDbStats, pDb->m_pSFileHdl, + pUncommittedDbHdr, + bNewKeepState + ? &checkpointDbHdr + : &m_pDatabase->m_checkpointDbHdr, FALSE))) + { + goto Exit; + } + + // Copy the uncommitted log header back to the committed log header and + // copy the CP log header (if changed above). + + m_pDatabase->lockMutex(); + + f_memcpy( &m_pDatabase->m_lastCommittedDbHdr, pUncommittedDbHdr, + sizeof( XFLM_DB_HDR)); + + if( bNewKeepState) + { + f_memcpy( &m_pDatabase->m_checkpointDbHdr, &checkpointDbHdr, + sizeof( XFLM_DB_HDR)); + } + + m_pDatabase->unlockMutex(); + +Exit: + + if (bDbLocked) + { + (void)pDb->unlockExclusive(); + } + return( rc); +} + +/******************************************************************** +Desc: Finish packet by outputting header information for it. +*********************************************************************/ +RCODE F_Rfl::finishPacket( + F_Db * pDb, + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen, + FLMBOOL bDoNewIfOverLowLimit) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPacketLen; + FLMBYTE * pucPacket; + + uiPacketLen = uiPacketBodyLen + RFL_PACKET_OVERHEAD; + + // See if this packet will cause us to overflow the limits on + // the current file. If so, create a new file. + + if( RC_BAD( rc = seeIfNeedNewFile( pDb, uiPacketLen, bDoNewIfOverLowLimit))) + { + goto Exit; + } + + // Get a pointer to packet header. + + pucPacket = &(m_pCurrentBuf->pIOBuffer->getBuffer()[ + m_pCurrentBuf->uiRflBufBytes]); + + // Set the packet address in the packet header. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + UD2FBA( (FLMUINT32)m_uiPacketAddress, &pucPacket [RFL_PACKET_ADDRESS_OFFSET]); + + // Set the packet type and packet body length. + + pucPacket [RFL_PACKET_TYPE_OFFSET] = (FLMBYTE)uiPacketType; + + UW2FBA( (FLMUINT16)uiPacketBodyLen, + &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Set the checksum for the packet. + + pucPacket [RFL_PACKET_CHECKSUM_OFFSET] = RflCalcChecksum( pucPacket, + uiPacketBodyLen); + + // Increment bytes in the buffer to reflect the fact that this packet + // is now complete. + + m_pCurrentBuf->uiRflBufBytes += uiPacketLen; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Truncate roll-forward log file to a certain size - only + do if not keeping RFL files. +*********************************************************************/ +RCODE F_Rfl::truncate( + F_SEM hWaitSem, + FLMUINT uiTruncateSize) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNum; + + flmAssert( uiTruncateSize >= 512); + + // Keeping of log files better not be enabled. + + flmAssert( !m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles); + + // Better not be in the middle of a transaction. + + flmAssert( !m_ui64CurrTransID); + + // Open the current RFL file. If it does not exist, it is OK - there + // is nothing to truncate. + + uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; + if (RC_BAD( rc = openFile( hWaitSem, uiFileNum, + m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) + { + if (rc == NE_XFLM_IO_PATH_NOT_FOUND || rc == NE_XFLM_IO_INVALID_FILENAME) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if (RC_BAD( rc = m_pFileHdl->Truncate( uiTruncateSize))) + { + m_bRflVolumeOk = FALSE; + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Setup to begin a transaction +*********************************************************************/ +RCODE F_Rfl::setupTransaction( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFileNum; + FLMUINT uiLastTransOffset; + FLMBOOL bCreateFile; + + f_mutexLock( m_hBufMutex); + m_pCurrentBuf->bTransInProgress = TRUE; + f_mutexUnlock( m_hBufMutex); + + // Get the last committed serial numbers from the file's log header + // buffer. + + f_memcpy( m_ucCurrSerialNum, + m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + f_memcpy( m_ucNextSerialNum, + m_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum, + XFLM_SERIAL_NUM_SIZE); + uiFileNum = (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; + uiLastTransOffset = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; + + // If the LOG_RFL_LAST_TRANS_OFFSET is zero, we need to create the + // next RFL file number no matter what. There are two cases where + // this happens: 1) when the database is first created, and 2) after + // a restore operation. + + if (!uiLastTransOffset) + { + bCreateFile = TRUE; + + // Close the current file, just in case we had opened it before. + // At this point, it doesn't matter because we are going to + // overwrite it. + + if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) + { + goto Exit; + } + closeFile(); + } + else if (RC_BAD( rc = openFile( pDb->m_hWaitSem, + uiFileNum, m_ucCurrSerialNum))) + { + if (rc != NE_XFLM_IO_PATH_NOT_FOUND && rc != NE_XFLM_IO_INVALID_FILENAME) + { + goto Exit; + } + bCreateFile = TRUE; + } + else + { + bCreateFile = FALSE; + } + + if (bCreateFile) + { + + // If the log header indicates that data has already been logged + // to the file, we need to return the I/O error rather than just + // re-creating the file. This may mean that someone changed the + // RFL directory without moving the RFL files properly. + + if (uiLastTransOffset > 512) + { + rc = RC_SET( NE_XFLM_RFL_FILE_NOT_FOUND); + goto Exit; + } + + // Create the RFL file if not found. + + // Use the next serial number stored in the FDB's log header + // for the serial number on this RFL file. Use the serial + // number we just generated as the next RFL serial number. + + if (RC_BAD( rc = createFile( pDb, uiFileNum, + m_ucCurrSerialNum, m_ucNextSerialNum, + m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles + ? TRUE + : FALSE))) + { + goto Exit; + } + } + else + { + // Read in enough of the buffer from the RFL file so that + // we are positioned on a 512 byte boundary. + + if (RC_BAD( positionTo( uiLastTransOffset))) + { + goto Exit; + } + } + + // These can only be changed when starting a transaction. + + m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles + ? TRUE + : FALSE; + + m_uiRflMaxFileSize = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMaxFileSize; + + // Round maximum down to nearest 512 boundary. This is necessary + // because we always write a minimum of 512 byte units in direct IO + // mode. If we did not round the maximum down, our last packet could + // end at an offset that is less than the maximum, but greater than + // the nearest 512 byte boundary - technically within the user-specified + // size limit. However, because we always write a full 512 bytes of data + // to fill out the last sector when we are in direct IO mode, we would + // end up with a file that was slightly larger than the user-specified + // limit. The EOF in the header of the file would be below the limit, + // but the actual file size would not be. Thus, the need to round down. + + m_uiRflMaxFileSize = ROUND_DOWN_TO_NEAREST_512( m_uiRflMaxFileSize); + + // The maximum cannot go below a certain threshold - must have room for + // least one packet plus the header. + + if (m_uiRflMaxFileSize < RFL_MAX_PACKET_SIZE + 512) + { + m_uiRflMaxFileSize = RFL_MAX_PACKET_SIZE + 512; + } + else if (m_uiRflMaxFileSize > gv_XFlmSysData.uiMaxFileSize) + { + m_uiRflMaxFileSize = gv_XFlmSysData.uiMaxFileSize; + } + + m_uiRflMinFileSize = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflMinFileSize; + + // Minimum RFL file size should not be allowed to be larger than + // maximum! + + if (m_uiRflMinFileSize > m_uiRflMaxFileSize) + { + m_uiRflMinFileSize = m_uiRflMaxFileSize; + } + + // Set the operation count to zero. + + m_uiOperCount = 0; + + m_pFileHdl->setMaxAutoExtendSize( m_uiRflMaxFileSize); + m_pFileHdl->setExtendSize( m_pDatabase->m_uiFileExtendSize); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log transaction begin. This routine will also make sure + we have opened an RFL file. + NOTE: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logBeginTransaction( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + flmAssert( !(pDb->m_uiFlags & FDB_REPLAYING_RFL)); + + // Better not be in the middle of a transaction. + + flmAssert( !m_ui64CurrTransID); + + if( RC_BAD( rc = setupTransaction( pDb))) + { + goto Exit; + } + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if (!haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID. + + flmEncodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( + pDb, RFL_TRNS_BEGIN_PACKET, uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + // Save the file offset for the start transaction packet. + + m_uiTransStartFile = m_pCurrentBuf->uiCurrFileNum; + m_uiTransStartAddr = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes - + uiPacketBodyLen - RFL_PACKET_OVERHEAD; + m_ui64CurrTransID = pDb->m_ui64CurrTransID; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Do a transaction begin operation during restore or recovery. +*********************************************************************/ +RCODE F_Rfl::recovTransBegin( + F_Db * pDb, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportBeginTrans( + peAction, m_ui64CurrTransID))) + { + goto Exit; + } + + if (*peAction == XFLM_RESTORE_ACTION_STOP) + { + // Need to set m_ui64CurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if (RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Flushes the RFL and sets some things in the log header. +*********************************************************************/ +void F_Rfl::finalizeTransaction( void) +{ + FLMUINT uiRflTransEndOffset; + XFLM_DB_HDR * pDbHdr = &m_pDatabase->m_uncommittedDbHdr; + + // Save the serial numbers and file numbers into the file's + // uncommitted log header. + + pDbHdr->ui32RflCurrFileNum = (FLMUINT32)m_pCurrentBuf->uiCurrFileNum; + + uiRflTransEndOffset = getCurrWriteOffset(); + pDbHdr->ui32RflLastTransOffset = (FLMUINT32)uiRflTransEndOffset; + + f_memcpy( pDbHdr->ucLastTransRflSerialNum, + m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); + + f_memcpy( pDbHdr->ucNextRflSerialNum, + m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); +} + +/******************************************************************** +Desc: Handles the commit and abort log operations. If aborting + the transaction, or if the transaction was empty, we will + simply throw away the entire transaction and not bother + to log it. In that case we will reset transaction pointers, + etc. back to the file and offset where the transaction began. + We will also delete RFL files that were created during the + transaction if necessary. NOTE: It is not essential that + the RFL files be deleted. If they are not successfully + deleted, they will be overwritten if need be when creating + new ones. + NOTE: The prior version of FLAIM (before 4.3) would log + a time and set the RFL_TIME_LOGGED_FLAG bit in the packet + type. This is no longer done. Old code should be + compatible because it reads the flag. +*********************************************************************/ +RCODE F_Rfl::logEndTransaction( + F_Db * pDb, + FLMUINT uiPacketType, + FLMBOOL bThrowLogAway, + FLMBOOL * pbLoggedTransEnd) +{ + RCODE rc = NE_XFLM_OK; + RCODE rc2 = NE_XFLM_OK; + FLMUINT uiLowFileNum; + FLMUINT uiHighFileNum; + char szRflFileName [F_PATH_MAX_SIZE]; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Initialize the "logged trans end" flag + + if( pbLoggedTransEnd) + { + *pbLoggedTransEnd = FALSE; + } + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + flmAssert( m_pFileHdl); + flmAssert( m_pDatabase); + + // If the transaction had no operations, throw it away - don't + // even log the packet. An abort operation may also + // elect to throw the log away even if there were + // operations. That is determined by the bThrowLogAway flag. + // The bThrowLogAway flag may be TRUE when doing a commit if + // the caller knows that nothing happened during the transction. + + if (bThrowLogAway || !m_uiOperCount) + { +Throw_Away_Transaction: + + // If we have switched files, delete all but the file we + // started in. + + if (m_pCurrentBuf->uiCurrFileNum != m_uiTransStartFile) + { + flmAssert( m_pCurrentBuf->uiCurrFileNum > m_uiTransStartFile); + + // File number in uncommitted log header better not + // have been changed yet. It is only supposed to + // be changed when the transaction finishes - i.e., in + // this routine. Up until this point, it should only + // be changed in m_pCurrentBuf->uiCurrFileNum. + + flmAssert( m_uiTransStartFile == + (FLMUINT)m_pDatabase->m_uncommittedDbHdr.ui32RflCurrFileNum); + + uiLowFileNum = m_uiTransStartFile + 1; + uiHighFileNum = m_pCurrentBuf->uiCurrFileNum; + + // Close the current file so it can be deleted. + + if (RC_BAD( rc = waitForCommit( pDb->m_hWaitSem))) + { + goto Exit; + } + closeFile(); + + // Delete as many of the files as possible. Don't worry + // about errors here. + + while (uiLowFileNum <= uiHighFileNum) + { + FLMUINT uiFileNameSize = sizeof( szRflFileName); + FLMBOOL bNameTruncated; + + getFullRflFileName( uiLowFileNum, szRflFileName, + &uiFileNameSize, &bNameTruncated); + if (!bNameTruncated) + { + (void)gv_pFileSystem->Delete( szRflFileName); + } + uiLowFileNum++; + } + } + else + { + // If we are in the file the transaction started in, simply + // reset to where the transaction started. + + if (RC_BAD( rc2 = positionTo( m_uiTransStartAddr))) + { + // If we got to this point because of a + // "goto Throw_Away_Transaction", we don't want to + // clobber the original error code. So, we use rc2 + // temporarily and then determine if its value should + // be set into rc. + + if( RC_OK( rc)) + { + rc = rc2; + } + rc2 = NE_XFLM_OK; + goto Exit; + } + } + } + else + { + // Log a commit or abort packet. + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Throw_Away_Transaction; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID. + + flmEncodeSEN( m_ui64CurrTransID, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, uiPacketType, + uiPacketBodyLen, FALSE))) + { + goto Throw_Away_Transaction; + } + + finalizeTransaction(); + + if( pbLoggedTransEnd) + { + *pbLoggedTransEnd = TRUE; + } + } + +Exit: + + m_ui64CurrTransID = 0; + return( RC_BAD( rc) ? rc : rc2); +} + +/******************************************************************** +Desc: Log a block chain free packet +*********************************************************************/ +RCODE F_Rfl::logBlockChainFree( + F_Db * pDb, + FLMUINT64 ui64MaintDocID, + FLMUINT uiStartBlkAddr, + FLMUINT uiEndBlkAddr, + FLMUINT uiCount) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 4; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the maintenance document ID + + flmEncodeSEN( ui64MaintDocID, &pucPacketBody); + + // Output the starting block address + + flmEncodeSEN( uiStartBlkAddr, &pucPacketBody); + + // Output the ending block address + + flmEncodeSEN( uiEndBlkAddr, &pucPacketBody); + + // Output the block count + + flmEncodeSEN( uiCount, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_BLK_CHAIN_FREE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Free a chain of blocks +*********************************************************************/ +RCODE F_Rfl::recovBlockChainFree( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64MaintDocNum; + FLMUINT uiStartBlkAddr; + FLMUINT uiEndBlkAddr; + FLMUINT uiCount; + FLMUINT uiBlocksFreed; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64MaintDocNum))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiStartBlkAddr))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiEndBlkAddr))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCount))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportBlockChainFree( + peAction, m_ui64CurrTransID, ui64MaintDocNum, uiStartBlkAddr, + uiEndBlkAddr, uiCount))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->maintBlockChainFree( + ui64MaintDocNum, uiCount, uiEndBlkAddr, &uiBlocksFreed))) + { + goto Exit; + } + + if( uiCount != uiBlocksFreed) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_DATA_ERROR); + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log index suspend and resume packets +*********************************************************************/ +RCODE F_Rfl::logIndexSuspendOrResume( + F_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT uiPacketType) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the index number. + + flmAssert( uiIndexNum <= FLM_MAX_UINT16); + flmEncodeSEN( uiIndexNum, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Suspend or resume an index during restore or recovery. +*********************************************************************/ +RCODE F_Rfl::recovIndexSuspendResume( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIndexNum; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiIndexNum))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( uiPacketType == RFL_INDEX_SUSPEND_PACKET) + { + if( RC_BAD( rc = m_pRestoreStatus->reportIndexSuspend( + peAction, m_ui64CurrTransID, uiIndexNum))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pRestoreStatus->reportIndexResume( + peAction, m_ui64CurrTransID, uiIndexNum))) + { + goto Exit; + } + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( uiPacketType == RFL_INDEX_SUSPEND_PACKET) + { + if( RC_BAD( rc = pDb->indexSuspend( uiIndexNum))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->indexResume( uiIndexNum))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Log a reduce packet +*********************************************************************/ +RCODE F_Rfl::logReduce( + F_Db * pDb, + FLMUINT uiCount) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // We need to set up to log this packet as if we + // were logging a transaction. The only difference + // is that we don't log the begin transaction packet. + + if( RC_BAD( rc = setupTransaction( pDb))) + { + goto Exit; + } + + uiMaxPacketBodyLen = 2 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID. + + flmEncodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); + + // Output the count + + flmEncodeSEN( uiCount, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_REDUCE_PACKET, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reduce the database during restore or recovery. +*********************************************************************/ +RCODE F_Rfl::recovReduce( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCount; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCount))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportReduce( + peAction, m_ui64CurrTransID, uiCount))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + // Need to set m_ui64CurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if( RC_BAD( rc = pDb->reduceSize( uiCount, &uiCount))) + { + goto Exit; + } + +Exit: + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: Log a database conversion packet +Note: This routine performs most of the setup for logging a full + transaction, but it does not cause begin and commit packets + to be logged. It is a "standalone" transaction. +*********************************************************************/ +RCODE F_Rfl::logUpgrade( + F_Db * pDb, + FLMUINT uiOldVersion) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // We need to set up to log this packet as if we + // were logging a transaction. The only difference + // is that we don't log the begin transaction packet. + + if( RC_BAD( rc = setupTransaction( pDb))) + { + goto Exit; + } + + uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if (RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID + + flmEncodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); + + // Output the old database version + + flmEncodeSEN( uiOldVersion, &pucPacketBody); + + // Output the new database version + + flmEncodeSEN( XFLM_CURRENT_VERSION_NUM, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_UPGRADE_PACKET, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + // Finalize the transaction (as if we were committing a transaction) + + finalizeTransaction(); + +Exit: + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: Upgrade the database during restore or recovery. +*********************************************************************/ +RCODE F_Rfl::recovUpgrade( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOldDbVersion; + FLMUINT uiNewDbVersion; + + if( uiPacketBodyLen != 8) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + uiOldDbVersion = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + uiNewDbVersion = (FLMUINT)FB2UD( pucPacketBody); + pucPacketBody += 4; + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportUpgrade( + peAction, m_ui64CurrTransID, uiOldDbVersion, uiNewDbVersion))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + // Need to set m_ui64CurrTransID to 0 since it was + // set by getPacket(). We are not going to + // start a transaction because of the user's request + // to exit. + + m_ui64CurrTransID = 0; + goto Exit; + } + } + + // Attempt the conversion if the current version is less + // than the target version and the target version is + // less than or equal to the highest version supported + // by this code. + + if( uiNewDbVersion > XFLM_CURRENT_VERSION_NUM) + { + rc = RC_SET( NE_XFLM_UNALLOWED_UPGRADE); + goto Exit; + } + else if( (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32DbVersion < + uiNewDbVersion) + { + // The logged "new" version may be a lesser version + // than XFLM_CURRENT_VERSION_NUM, which is what FlmDbUpgrade + // upgrades to. This is O.K. because the current version + // should support all packets in the RFL for versions + // that are less than it. Otherwise, the RFL chain + // would have been broken by the original upgrade and it + // would not have logged an upgrade packet. + + if( RC_BAD( rc = pDb->upgrade( NULL))) + { + goto Exit; + } + } + +Exit: + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logEncryptionKey( + F_Db * pDb, + FLMUINT uiPacketType, + FLMBYTE * pucKey, + FLMUINT32 ui32KeyLen) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + flmAssert( uiPacketType == RFL_WRAP_KEY_PACKET || + uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + if( RC_BAD( rc = setupTransaction( pDb))) + { + goto Exit; + } + + uiMaxPacketBodyLen = (2 * FLM_MAX_SEN_LEN) + ui32KeyLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID + + flmEncodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); + + // Store the length of the key + + flmEncodeSEN( ui32KeyLen, &pucPacketBody); + + // If we were built without encryption, the key length will be zero, + // so no need to store the key. + + if( ui32KeyLen) + { + f_memcpy( pucPacketBody, pucKey, ui32KeyLen); + pucPacketBody += ui32KeyLen; + } + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, uiPacketType, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovEncryptionKey( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDBKeyLen; + FLMBOOL bTransStarted = FALSE; + XFLM_DB_HDR * pUncommittedLogHdr = &m_pDatabase->m_uncommittedDbHdr; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiDBKeyLen))) + { + goto Exit; + } + + if( pucPacketBody + uiDBKeyLen != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( uiPacketType == RFL_WRAP_KEY_PACKET) + { + if( RC_BAD( rc = m_pRestoreStatus->reportWrapKey( + peAction, m_ui64CurrTransID))) + { + goto Exit; + } + } + else if( uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET) + { + if( RC_BAD( rc = m_pRestoreStatus->reportEnableEncryption( + peAction, m_ui64CurrTransID))) + { + goto Exit; + } + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if( uiDBKeyLen) + { + // We cannot directly set the key at this point as it may be + // encrypted using a password, which we do not have here. We will + // write the key out to the log header and trust the user to know whether + // or not a password is needed to open the database. + + if( RC_BAD(rc = pDb->transBegin( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + bTransStarted = TRUE; + + if( uiDBKeyLen > XFLM_MAX_ENC_KEY_SIZE) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_ENCKEY_SIZE); + goto Exit; + } + + f_memcpy( &pUncommittedLogHdr->DbKey, pucPacketBody, uiDBKeyLen); + pUncommittedLogHdr->ui32DbKeyLen = (FLMUINT32)uiDBKeyLen; + + pDb->m_bHadUpdOper = TRUE; + + if( RC_BAD( rc = pDb->commitTrans( 0, TRUE))) + { + goto Exit; + } + + bTransStarted = FALSE; + } + +Exit: + + if( bTransStarted) + { + RCODE tmpRc; + + if( RC_BAD( tmpRc = pDb->commitTrans( 0, TRUE))) + { + pDb->abortTrans(); + + if (RC_OK( rc)) + { + rc = tmpRc; + } + } + } + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logEncDefKey( + F_Db * pDb, + FLMUINT uiEncDefId, + FLMBYTE * pucKeyValue, + FLMUINT uiKeyValueLen, + FLMUINT uiKeySize) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (3 * FLM_MAX_SEN_LEN) + uiKeyValueLen; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the encryption definition ID + + flmEncodeSEN( uiEncDefId, &pucPacketBody); + + // Output the key size (number of bits) + + flmEncodeSEN( uiKeySize, &pucPacketBody); + + // Output the key value length + + flmEncodeSEN( uiKeyValueLen, &pucPacketBody); + + // Output the key + + f_memcpy( pucPacketBody, pucKeyValue, uiKeyValueLen); + pucPacketBody += uiKeyValueLen; + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_ENC_DEF_KEY_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovEncDefKey( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction *) // peAction +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + F_DOMNode * pAttr = NULL; + FLMUINT64 ui64RootId; + FLMUINT uiKeySize; + FLMUINT uiKeyValueLen; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64RootId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiKeySize))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiKeyValueLen))) + { + goto Exit; + } + + if( pucPacketBody + uiKeyValueLen != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Retrieve the encryption definition + + if( RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, + ui64RootId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + // Set the key in the DOM node as a binary string. + + if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->removeModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setBinary( pDb, pucPacketBody, uiKeyValueLen))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + // Set the key size + + if( RC_BAD( rc = pNode->createAttribute( pDb, ATTR_ENCRYPTION_KEY_SIZE_TAG, + (IF_DOMNode **)&pAttr))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->removeModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUINT( pDb, uiKeySize))) + { + goto Exit; + } + + if( RC_BAD( rc = pAttr->addModeFlags( pDb, + FDOM_READ_ONLY | FDOM_CANNOT_DELETE))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logRollOverDbKey( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + if( RC_BAD( rc = setupTransaction( pDb))) + { + goto Exit; + } + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the transaction ID + + flmEncodeSEN( pDb->m_ui64CurrTransID, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_ROLL_OVER_DB_KEY_PACKET, + uiPacketBodyLen, TRUE))) + { + goto Exit; + } + + finalizeTransaction(); + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovRollOverDbKey( + F_Db * pDb, + const FLMBYTE *, // pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + + if( uiPacketBodyLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportRollOverDbKey( + peAction, m_ui64CurrTransID))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + m_ui64CurrTransID = 0; + goto Exit; + } + } + + if( RC_BAD( rc = pDb->rollOverDbKey())) + { + goto Exit; + } + +Exit: + + m_ui64CurrTransID = 0; + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logDocumentDone( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64RootId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 2 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the document ID + + flmEncodeSEN( ui64RootId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_DOCUMENT_DONE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovDocumentDone( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64RootId; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64RootId))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if (RC_BAD( rc = m_pRestoreStatus->reportDocumentDone( + peAction, m_ui64CurrTransID, uiCollection, ui64RootId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->documentDone( uiCollection, ui64RootId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 2; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_DELETE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64NodeId; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeDelete( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NodeId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pNode->deleteNode( pDb))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logAttributeDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiAttrName) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 3; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the element ID + + flmEncodeSEN( ui64ElementId, &pucPacketBody); + + // Output the attribute name + + flmEncodeSEN( uiAttrName, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_ATTR_DELETE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovAttributeDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64ElementId; + FLMUINT uiAttrName; + F_DOMNode * pElementNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportAttributeDelete( + peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, uiAttrName))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64ElementId, XFLM_EXACT, &pElementNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pElementNode->deleteAttribute( pDb, uiAttrName))) + { + goto Exit; + } + +Exit: + + if( pElementNode) + { + pElementNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeChildrenDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the name ID + + flmEncodeSEN( uiNameId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_CHILDREN_DELETE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeChildrenDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiNameId; + FLMUINT64 ui64NodeId; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiNameId))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeChildrenDelete( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, + uiNameId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, + XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pNode->deleteChildren( pDb, uiNameId))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeCreate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64RefNodeId, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN) + 2; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the reference node ID + + flmEncodeSEN( ui64RefNodeId, &pucPacketBody); + + // Output the name ID + + flmEncodeSEN( uiNameId, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the node type + + *pucPacketBody = (FLMBYTE)eNodeType; + pucPacketBody++; + + // Output the insert location + + *pucPacketBody = (FLMBYTE)eLocation; + pucPacketBody++; + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_CREATE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeCreate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiNameId; + FLMUINT64 ui64RefNodeId; + FLMUINT64 ui64ExpectedNodeId; + FLMUINT64 ui64ActualNodeId = 0; + eDomNodeType eNodeType; + eNodeInsertLoc eLocation; + IF_DOMNode * pRefNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64RefNodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, + pucEnd, &ui64ExpectedNodeId))) + { + goto Exit; + } + + if( (pucEnd - pucPacketBody) != 2) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + eNodeType = (eDomNodeType)*pucPacketBody; + pucPacketBody++; + + eLocation = (eNodeInsertLoc)*pucPacketBody; + pucPacketBody++; + + if (m_pRestoreStatus) + { + if (RC_BAD( rc = m_pRestoreStatus->reportNodeCreate( + peAction, m_ui64CurrTransID, uiCollection, ui64RefNodeId, + eNodeType, uiNameId, eLocation))) + { + goto Exit; + } + + if (*peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( eLocation == XFLM_ROOT) + { + if( RC_BAD( rc = pDb->createRootNode( uiCollection, + uiNameId, eNodeType, NULL, &ui64ActualNodeId))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64RefNodeId, + &pRefNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pRefNode->createNode( pDb, eNodeType, + uiNameId, eLocation, &pRefNode, &ui64ActualNodeId))) + { + goto Exit; + } + } + + if( ui64ActualNodeId != ui64ExpectedNodeId) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + +Exit: + + if( pRefNode) + { + pRefNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logAttributeCreate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ElementNodeId, + FLMUINT uiNameId, + FLMUINT uiNextAttrNameId) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN); + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the element node ID + + flmEncodeSEN( ui64ElementNodeId, &pucPacketBody); + + // Output the name ID + + flmEncodeSEN( uiNameId, &pucPacketBody); + + // Output the next attribute's name ID + + flmEncodeSEN( uiNextAttrNameId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_ATTR_CREATE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovAttributeCreate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiAttrNameId; + FLMUINT uiNextAttrNameId; + FLMUINT64 ui64ElementId; + IF_DOMNode * pElementNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiNextAttrNameId))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if (m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeCreate( + peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, + ATTRIBUTE_NODE, uiAttrNameId, XFLM_ATTRIBUTE))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64ElementId, &pElementNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pElementNode->createAttribute( pDb, uiAttrNameId, NULL))) + { + goto Exit; + } + +Exit: + + if( pElementNode) + { + pElementNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logInsertBefore( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64Parent, + FLMUINT64 ui64Child, + FLMUINT64 ui64RefChild) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 4 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the parent ID + + flmEncodeSEN( ui64Parent, &pucPacketBody); + + // Output the child ID + + flmEncodeSEN( ui64Child, &pucPacketBody); + + // Output the ref child ID + + flmEncodeSEN( ui64RefChild, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_INSERT_BEFORE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovInsertBefore( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64Parent; + FLMUINT64 ui64NewChild; + FLMUINT64 ui64RefChild; + F_DOMNode * pParent = NULL; + F_DOMNode * pNewChild = NULL; + F_DOMNode * pRefChild = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64Parent))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NewChild))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64RefChild))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportInsertBefore( + peAction, m_ui64CurrTransID, uiCollection, ui64Parent, + ui64NewChild, ui64RefChild))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64Parent, + XFLM_EXACT, &pParent))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NewChild, + XFLM_EXACT, &pNewChild))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( ui64RefChild) + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64RefChild, + XFLM_EXACT, &pRefChild))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + } + + if( RC_BAD( rc = pParent->insertBefore( pDb, pNewChild, pRefChild))) + { + goto Exit; + } + +Exit: + + if( pParent) + { + pParent->Release(); + } + + if( pNewChild) + { + pNewChild->Release(); + } + + if( pRefChild) + { + pRefChild->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logEncryptedNodeUpdate( + F_Db * pDb, + F_CachedNode * pCachedNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiDataLength; + IF_PosIStream * pIStream = NULL; + F_RflOStream rflOStream( this, pDb); + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pCachedNode->getRawIStream( pDb, &pIStream))) + { + goto Exit; + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( pCachedNode->getCollection(), &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( pCachedNode->getNodeId(), &pucPacketBody); + + // Output the stream length + + uiDataLength = (FLMUINT)pIStream->remainingSize(); + flmAssert( uiDataLength); + flmEncodeSEN( uiDataLength, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_ENC_NODE_UPDATE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Log the data + + if( RC_BAD( rc = rflOStream.write( pIStream))) + { + goto Exit; + } + + flmAssert( !pIStream->remainingSize()); + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovEncryptedNodeUpdate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiDataLen; + FLMUINT64 ui64NodeId; + FLMUINT uiPacketType; + const FLMBYTE * pucDataPacketBody; + FLMUINT uiDataPacketBodyLen; + F_Btree * pBTree = NULL; + F_COLLECTION * pCollection = NULL; + FLMBYTE ucKey[ FLM_MAX_NUM_BUF_SIZE]; + FLMUINT uiKeyLen; + FLMBOOL bFirst; + FLMBOOL bLast; + FLMBOOL bUseDataOnlyBlocks; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiDataLen))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeUpdate( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + // Retrieve the node so we can clean up its keys + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NodeId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + goto Exit; + } + + if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNode, + IX_DEL_NODE_VALUE, TRUE))) + { + goto Exit; + } + + // Clear the node cache before directly accessing the B-Tree + + if( RC_BAD( rc = pDb->flushDirtyNodes())) + { + goto Exit; + } + + pDb->m_pDatabase->freeNodeCache(); + + // Open the B-Tree + + if( RC_BAD( rc = gv_XFlmSysData.pBtPool->btpReserveBtree( &pBTree))) + { + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getCollection( + uiCollection, &pCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = pBTree->btOpen( pDb, &pCollection->lfInfo, FALSE, TRUE))) + { + goto Exit; + } + + // Build the B-Tree key + + uiKeyLen = sizeof( ucKey); + if( RC_BAD( rc = flmNumber64ToStorage( ui64NodeId, &uiKeyLen, + ucKey, FALSE, TRUE))) + { + goto Exit; + } + + // Determine if the item should be put into data only blocks + + bUseDataOnlyBlocks = useDataOnlyBlocks( pDb, uiDataLen); + + // Go into a loop processing packets until we have retrieved + // all of the expected data. + + bFirst = TRUE; + bLast = FALSE; + + while( uiDataLen) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiPacketType, &pucDataPacketBody, &uiDataPacketBodyLen))) + { + goto Exit; + } + + if( uiPacketType != RFL_DATA_PACKET) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Packet body length better not be greater than the expected + // data length + + if( uiDataPacketBodyLen > uiDataLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( uiDataLen == uiDataPacketBodyLen) + { + if( bFirst && bUseDataOnlyBlocks) + { + if( RC_BAD( rc = pBTree->btReplaceEntry( ucKey, sizeof( ucKey), + uiKeyLen, pucDataPacketBody, uiDataPacketBodyLen, + bFirst, FALSE))) + { + goto Exit; + } + + uiDataPacketBodyLen = 0; + } + + bLast = TRUE; + } + + if( RC_BAD( rc = pBTree->btReplaceEntry( ucKey, sizeof( ucKey), uiKeyLen, + pucDataPacketBody, uiDataPacketBodyLen, bFirst, bLast))) + { + goto Exit; + } + + uiDataLen -= uiDataPacketBodyLen; + bFirst = FALSE; + } + + pNode->Release(); + pNode = NULL; + + // Re-read the node from the btree + + pDb->m_pDatabase->freeNodeCache(); + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NodeId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + goto Exit; + } + + if (RC_BAD( rc = pDb->updateIndexKeys( uiCollection, pNode, + IX_ADD_NODE_VALUE, TRUE))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pBTree) + { + gv_XFlmSysData.pBtPool->btpReturnBtree( &pBTree); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeSetValue( + F_Db * pDb, + FLMUINT uiPacketType, + F_CachedNode * pCachedNode) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiDataLength; + FLMUINT uiDataType; + IF_PosIStream * pIStream = NULL; + F_RflOStream rflOStream( this, pDb); + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + FLMBOOL bUseDataPackets = FALSE; + F_NodeBufferIStream bufferIStream; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Make sure we have a valid packet type + + flmAssert( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET || + uiPacketType == RFL_NODE_SET_BINARY_VALUE_PACKET); + + // If the data is encrypted we need to call a special logging routine + + if( pCachedNode->getEncDefId()) + { + rc = logEncryptedNodeUpdate( pDb, pCachedNode); + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Get the data stream and determine its length + + if( RC_BAD( rc = pCachedNode->getIStream( pDb, &bufferIStream, &pIStream, + &uiDataType, &uiDataLength))) + { + goto Exit; + } + + if( uiDataLength && uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) + { + FLMBYTE ucSEN; + FLMUINT uiSENLen; + + // No reason to store the leading SEN, so skip it. + + if( RC_BAD( rc = pIStream->read( &ucSEN, 1, NULL))) + { + goto Exit; + } + + uiSENLen = flmGetSENLength( ucSEN); + if( uiSENLen > 1) + { + if( RC_BAD( rc = pIStream->read( NULL, uiSENLen - 1, NULL))) + { + goto Exit; + } + } + + uiDataLength -= uiSENLen; + } + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN); + + if( uiMaxPacketBodyLen + uiDataLength > RFL_MAX_PACKET_BODY_SIZE) + { + bUseDataPackets = TRUE; + } + else + { + uiMaxPacketBodyLen += uiDataLength; + } + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( pCachedNode->getCollection(), &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( pCachedNode->getNodeId(), &pucPacketBody); + + // Output the data length + + flmEncodeSEN( uiDataLength, &pucPacketBody); + + // Output the data packet flag + + flmEncodeSEN( bUseDataPackets ? 1 : 0, &pucPacketBody); + + // Output the data if we aren't using data packets + + if( !bUseDataPackets && uiDataLength) + { + if( RC_BAD( rc = pIStream->read( pucPacketBody, uiDataLength, NULL))) + { + goto Exit; + } + + pucPacketBody += uiDataLength; + } + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, uiPacketType, uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Log the data (if not done above) + + if( bUseDataPackets) + { + flmAssert( uiDataLength); + + if( RC_BAD( rc = rflOStream.write( pIStream))) + { + goto Exit; + } + + flmAssert( !pIStream->remainingSize()); + } + +Exit: + + if( pIStream) + { + pIStream->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeSetValue( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pNode = NULL; + FLMUINT uiCollection; + FLMUINT uiDataLen; + FLMUINT64 ui64NodeId; + const FLMBYTE * pucDataPacketBody; + FLMUINT uiDataPacketType; + FLMUINT uiDataPacketBodyLen; + FLMUINT uiHaveDataPackets; + FLMBOOL bFirst; + FLMBOOL bLast; + FLMBOOL bUseDataOnlyBlocks; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiDataLen))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiHaveDataPackets))) + { + goto Exit; + } + + if( uiHaveDataPackets) + { + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + if( pucPacketBody + uiDataLen != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + } + + if( uiPacketType != RFL_NODE_SET_TEXT_VALUE_PACKET && + uiPacketType != RFL_NODE_SET_BINARY_VALUE_PACKET) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + // Retrieve the node + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NodeId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( !uiHaveDataPackets) + { + if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) + { + if( RC_BAD( rc = pNode->setUTF8( pDb, pucPacketBody, + uiDataLen, TRUE))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->setBinary( pDb, pucPacketBody, + uiDataLen, TRUE))) + { + goto Exit; + } + } + } + else + { + // Determine if the item should be put into data only blocks + + bUseDataOnlyBlocks = useDataOnlyBlocks( pDb, uiDataLen); + + // Go into a loop processing packets until we have retrieved + // all of the expected data. + + bFirst = TRUE; + bLast = FALSE; + + while( uiDataLen) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiDataPacketType, &pucDataPacketBody, + &uiDataPacketBodyLen))) + { + goto Exit; + } + + pucEnd = pucDataPacketBody + uiDataPacketBodyLen; + + if( uiDataPacketType != RFL_DATA_PACKET) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Packet body length better not be greater than the expected + // data length + + if( uiDataPacketBodyLen > uiDataLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( uiDataLen == uiDataPacketBodyLen) + { + if( bFirst && bUseDataOnlyBlocks) + { + if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) + { + if( RC_BAD( rc = pNode->setUTF8( pDb, + pucDataPacketBody, uiDataPacketBodyLen, FALSE))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->setBinary( pDb, + pucDataPacketBody, uiDataPacketBodyLen, FALSE))) + { + goto Exit; + } + } + + uiDataPacketBodyLen = 0; + } + + bLast = TRUE; + } + + if( uiPacketType == RFL_NODE_SET_TEXT_VALUE_PACKET) + { + if( RC_BAD( rc = pNode->setUTF8( pDb, + pucDataPacketBody, uiDataPacketBodyLen, bLast))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->setBinary( pDb, + pucDataPacketBody, uiDataPacketBodyLen, bLast))) + { + goto Exit; + } + } + + uiDataLen -= uiDataPacketBodyLen; + bFirst = FALSE; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeFlagsUpdate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + FLMUINT uiFlags, + FLMBOOL bAdd) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (4 * FLM_MAX_SEN_LEN) + 1; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the attribute name ID + + flmEncodeSEN( uiAttrNameId, &pucPacketBody); + + // Output the flags + + flmEncodeSEN( uiFlags, &pucPacketBody); + + // Output the "add" flag + + *pucPacketBody = bAdd ? 1 : 0; + pucPacketBody++; + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_FLAGS_UPDATE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeFlagsUpdate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiFlags; + FLMUINT64 ui64NodeId; + FLMUINT uiAttrNameId; + FLMBOOL bAdd; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiFlags))) + { + goto Exit; + } + + if( pucPacketBody + 1 != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + bAdd = *pucPacketBody ? TRUE : FALSE; + pucPacketBody++; + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeFlagsUpdate( + peAction, m_ui64CurrTransID, uiCollection, + ui64NodeId, uiFlags, bAdd))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, + ui64NodeId, XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( uiAttrNameId) + { + pNode->m_uiAttrNameId = uiAttrNameId; + } + + if( bAdd) + { + if( RC_BAD( pNode->addModeFlags( pDb, uiFlags))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->removeModeFlags( pDb, uiFlags))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeSetPrefixId( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrName, + FLMUINT uiPrefixId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 4 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the attribute name (if any) + + flmEncodeSEN( uiAttrName, &pucPacketBody); + + // Output the prefix ID + + flmEncodeSEN( uiPrefixId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_PREFIX_ID_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeSetPrefixId( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiAttrName; + FLMUINT uiPrefix; + FLMUINT64 ui64NodeId; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiPrefix))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetPrefixId( + peAction, m_ui64CurrTransID, uiCollection, + ui64NodeId, uiAttrName, uiPrefix))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( uiAttrName) + { + if( RC_BAD( rc = pDb->getAttribute( uiCollection, + ui64NodeId, uiAttrName, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, + XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + } + + if( RC_BAD( rc = pNode->setPrefixId( pDb, uiPrefix))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeSetMetaValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64MetaValue) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the meta value + + flmEncodeSEN( ui64MetaValue, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_META_VALUE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeSetMetaValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64MetaValue; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64MetaValue))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetMetaValue( + peAction, m_ui64CurrTransID, uiCollection, + ui64NodeId, ui64MetaValue))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, + XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pNode->setMetaValue( pDb, ui64MetaValue))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logSetNextNodeId( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = FLM_MAX_SEN_LEN * 2; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the next node ID + + flmEncodeSEN( ui64NextNodeId, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_SET_NEXT_NODE_ID_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovSetNextNodeId( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64NextNodeId; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NextNodeId))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportSetNextNodeId( + peAction, m_ui64CurrTransID, uiCollection, ui64NextNodeId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->setNextNodeId( uiCollection, ui64NextNodeId))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeSetNumberValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64Value, + FLMBOOL bNeg) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (3 * FLM_MAX_SEN_LEN) + 1; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the number + + flmEncodeSEN( ui64Value, &pucPacketBody); + + // Output the sign flag + + *pucPacketBody = bNeg ? 1 : 0; + pucPacketBody++; + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if (RC_BAD( rc = finishPacket( pDb, RFL_NODE_SET_NUMBER_VALUE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeSetNumberValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64Value; + FLMBOOL bNegative; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64Value))) + { + goto Exit; + } + + if( pucPacketBody + 1 != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + bNegative = *pucPacketBody ? TRUE : FALSE; + pucPacketBody++; + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, + XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( !bNegative) + { + if( RC_BAD( rc = pNode->setUINT64( pDb, ui64Value))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->setINT64( pDb, -((FLMINT64)ui64Value)))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logAttrSetValue( + F_Db * pDb, + F_CachedNode * pCachedNode, + FLMUINT uiAttrName) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketBody; + FLMBYTE * pucPacketStart; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + FLMUINT uiPayloadLen; + F_AttrItem * pAttrItem; + FLMBOOL bUseDataPackets = FALSE; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Get the attribute item + + if( (pAttrItem = pCachedNode->getAttribute( uiAttrName, NULL)) == NULL) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_FAILURE); + goto Exit; + } + + uiPayloadLen = pAttrItem->m_uiPayloadLen; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = (7 * FLM_MAX_SEN_LEN) + pAttrItem->m_uiIVLen; + + if( uiMaxPacketBodyLen + uiPayloadLen > RFL_MAX_PACKET_BODY_SIZE) + { + bUseDataPackets = TRUE; + } + else + { + uiMaxPacketBodyLen += uiPayloadLen; + } + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( pCachedNode->getCollection(), &pucPacketBody); + + // Output the element node ID + + flmEncodeSEN( pCachedNode->getNodeId(), &pucPacketBody); + + // Output the name ID + + flmEncodeSEN( uiAttrName, &pucPacketBody); + + // Output the encryption definition ID + + flmEncodeSEN( pAttrItem->m_uiEncDefId, &pucPacketBody); + + // Output the initialization vector length and the + // decrypted data length + + if( pAttrItem->m_uiEncDefId) + { + flmEncodeSEN( pAttrItem->m_uiIVLen, &pucPacketBody); + flmEncodeSEN( pAttrItem->m_uiDecryptedDataLen, &pucPacketBody); + } + + // Output the payload length + + flmEncodeSEN( uiPayloadLen, &pucPacketBody); + + // Output the data packet flag + + flmEncodeSEN( bUseDataPackets ? 1 : 0, &pucPacketBody); + + // Output the data if we aren't using data packets + + if( !bUseDataPackets && uiPayloadLen) + { + f_memcpy( pucPacketBody, pAttrItem->getAttrPayloadPtr(), uiPayloadLen); + pucPacketBody += uiPayloadLen; + } + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_ATTR_SET_VALUE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + + // Log the data (if not done above) + + if( bUseDataPackets) + { + F_RflOStream rflOStream( this, pDb); + + flmAssert( uiPayloadLen); + + if( RC_BAD( rc = rflOStream.write( pAttrItem->getAttrPayloadPtr(), + uiPayloadLen))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovAttrSetValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiAttrNameId; + FLMUINT uiEncDefId; + FLMUINT uiPayloadLen; + FLMUINT uiDecryptedDataLen; + FLMUINT uiIVLen = 0; + FLMUINT uiHaveDataPackets; + FLMUINT uiTmp; + FLMUINT64 ui64ElementId; + FLMBYTE * pucData = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + FLMBYTE ucIV[ 16]; + F_DOMNode * pElementNode = NULL; + F_AttrElmInfo defInfo; + F_BufferIStream bufferIStream; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64ElementId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrNameId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiEncDefId))) + { + goto Exit; + } + + if( uiEncDefId) + { + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiIVLen))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, + &uiDecryptedDataLen))) + { + goto Exit; + } + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiPayloadLen))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiHaveDataPackets))) + { + goto Exit; + } + + if( uiHaveDataPackets) + { + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + if( pucPacketBody + uiPayloadLen != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportAttributeSetValue( + peAction, m_ui64CurrTransID, uiCollection, ui64ElementId, + uiAttrNameId))) + { + goto Exit; + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64ElementId, + XFLM_EXACT, &pElementNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + + if( RC_BAD( rc = pDb->m_pDict->getAttribute( pDb, uiAttrNameId, &defInfo))) + { + goto Exit; + } + + if( !uiHaveDataPackets) + { + if( RC_BAD( rc = bufferIStream.open( pucPacketBody, uiPayloadLen))) + { + goto Exit; + } + } + else + { + FLMUINT uiOffset = 0; + FLMUINT uiDataPacketType; + FLMUINT uiDataPacketBodyLen; + const FLMBYTE * pucDataPacketBody; + + if( RC_BAD( rc = bufferIStream.open( NULL, uiPayloadLen, &pucData))) + { + goto Exit; + } + + // Go into a loop processing packets until we have retrieved + // all of the expected data. + + while( uiOffset < uiPayloadLen) + { + if( RC_BAD( rc = getPacket( + pDb, FALSE, &uiDataPacketType, &pucDataPacketBody, + &uiDataPacketBodyLen))) + { + goto Exit; + } + + if( uiDataPacketType != RFL_DATA_PACKET) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Packet body length better not be greater than the expected + // data length + + if( uiDataPacketBodyLen + uiOffset > uiPayloadLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + f_memcpy( &pucData[ uiOffset], pucDataPacketBody, uiDataPacketBodyLen); + uiOffset += uiDataPacketBodyLen; + } + } + + if( uiEncDefId) + { + if( RC_BAD( rc = bufferIStream.read( ucIV, uiIVLen, NULL))) + { + goto Exit; + } + + flmAssert( pucData); + + if( RC_BAD( rc = pDb->decryptData( + uiEncDefId, ucIV, pucData, (FLMUINT)bufferIStream.remainingSize(), + pucData, (FLMUINT)bufferIStream.remainingSize()))) + { + goto Exit; + } + + bufferIStream.truncate( + (FLMUINT)(bufferIStream.getCurrPosition() + uiDecryptedDataLen)); + } + + switch( defInfo.getDataType()) + { + case XFLM_TEXT_TYPE: + { + FLMUINT uiTextLength = (FLMUINT)bufferIStream.remainingSize(); + const FLMBYTE * pucTmp; + + pucTmp = bufferIStream.getBufferAtCurrentOffset(); + + // Skip the leading SEN + + uiTmp = flmGetSENLength( pucTmp[ 0]); + + if( uiTmp > uiTextLength) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + uiTextLength -= uiTmp; + pucTmp += uiTmp; + + if( RC_BAD( rc = pElementNode->setAttributeValueUTF8( + pDb, uiAttrNameId, pucTmp, uiTextLength))) + { + goto Exit; + } + + break; + } + + case XFLM_BINARY_TYPE: + { + if( RC_BAD( rc = pElementNode->setAttributeValueBinary( + pDb, uiAttrNameId, bufferIStream.getBufferAtCurrentOffset(), + (FLMUINT)bufferIStream.remainingSize()))) + { + goto Exit; + } + + break; + } + + case XFLM_NUMBER_TYPE: + { + FLMUINT64 ui64Value; + FLMBOOL bNeg; + + if( RC_BAD( rc = flmReadStorageAsNumber( &bufferIStream, + XFLM_NUMBER_TYPE, &ui64Value, &bNeg))) + { + goto Exit; + } + + if( bNeg) + { + if( RC_BAD( rc = pElementNode->setAttributeValueINT64( + pDb, uiAttrNameId, -((FLMINT64)ui64Value)))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pElementNode->setAttributeValueUINT64( + pDb, uiAttrNameId, ui64Value))) + { + goto Exit; + } + } + + break; + } + + default: + { + rc = RC_SET_AND_ASSERT( NE_XFLM_NOT_IMPLEMENTED); + break; + } + } + +Exit: + + if( pElementNode) + { + pElementNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::logNodeClearValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrName) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacketStart; + FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen; + FLMUINT uiMaxPacketBodyLen; + + flmAssert( pDb->m_uiFlags & FDB_HAS_FILE_LOCK); +#ifndef FLM_DEBUG + F_UNREFERENCED_PARM( pDb); +#endif + + // Do nothing if logging is disabled. + + if( !isLoggingEnabled()) + { + goto Exit; + } + + // Better be in the middle of a transaction. + + flmAssert( m_ui64CurrTransID); + + // Increment the operation count + + m_uiOperCount++; + + // Determine the maximum packet size + + uiMaxPacketBodyLen = 3 * FLM_MAX_SEN_LEN; + + // Make sure we have space in the RFL buffer for a complete packet. + + if( !haveBuffSpace( uiMaxPacketBodyLen + RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + } + + // Get a pointer to where we will be laying down the packet body. + + pucPacketBody = pucPacketStart = getPacketBodyPtr(); + + // Output the collection number + + flmEncodeSEN( uiCollection, &pucPacketBody); + + // Output the node ID + + flmEncodeSEN( ui64NodeId, &pucPacketBody); + + // Output the attribute name (if any) + + flmEncodeSEN( uiAttrName, &pucPacketBody); + + // Finish the packet + + uiPacketBodyLen = (FLMUINT)(pucPacketBody - pucPacketStart); + flmAssert( uiPacketBodyLen <= uiMaxPacketBodyLen); + + if( RC_BAD( rc = finishPacket( pDb, RFL_NODE_CLEAR_VALUE_PACKET, + uiPacketBodyLen, FALSE))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_Rfl::recovNodeClearValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiAttrName; + FLMUINT64 ui64NodeId; + F_DOMNode * pNode = NULL; + const FLMBYTE * pucEnd = pucPacketBody + uiPacketBodyLen; + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiCollection))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, pucEnd, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN( &pucPacketBody, pucEnd, &uiAttrName))) + { + goto Exit; + } + + if( pucPacketBody != pucEnd) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( m_pRestoreStatus) + { + if( uiAttrName) + { + if( RC_BAD( rc = m_pRestoreStatus->reportAttributeSetValue( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId, uiAttrName))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = m_pRestoreStatus->reportNodeSetValue( + peAction, m_ui64CurrTransID, uiCollection, ui64NodeId))) + { + goto Exit; + } + } + + if( *peAction == XFLM_RESTORE_ACTION_STOP) + { + goto Exit; + } + } + + if( uiAttrName) + { + if( RC_BAD( rc = pDb->getAttribute( uiCollection, + ui64NodeId, uiAttrName, (IF_DOMNode **)&pNode))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDb->getNode( uiCollection, ui64NodeId, + XFLM_EXACT, &pNode))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + } + + if( RC_BAD( rc = pNode->clearNodeValue( pDb))) + { + goto Exit; + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE XFLMAPI F_RflOStream::write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + FLMUINT uiBytesAvail; + FLMBYTE * pucBuffer = (FLMBYTE *)pvBuffer; + + flmAssert( m_pRfl->isLoggingEnabled()); + + if( !m_pRfl->haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = m_pRfl->flush( m_pDb, m_pRfl->m_pCurrentBuf))) + { + goto Exit; + } + } + + while( uiBytesToWrite) + { + if( RC_BAD( rc = m_pRfl->makeRoom( m_pDb, uiBytesToWrite, + &uiPacketLen, RFL_DATA_PACKET, &uiBytesAvail, NULL))) + { + goto Exit; + } + + f_memcpy( m_pRfl->getPacketPtr() + uiPacketLen, pucBuffer, uiBytesAvail); + + pucBuffer += uiBytesAvail; + uiBytesToWrite -= uiBytesAvail; + uiPacketLen += uiBytesAvail; + + if( RC_BAD( rc = m_pRfl->finishPacket( m_pDb, RFL_DATA_PACKET, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + + uiPacketLen = RFL_PACKET_OVERHEAD; + } + + if( puiBytesWritten) + { + *puiBytesWritten = (FLMUINT)(pucBuffer - ((FLMBYTE *)pvBuffer)); + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_RflOStream::write( + IF_PosIStream * pIStream) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiPacketLen = RFL_PACKET_OVERHEAD; + FLMUINT uiBytesToWrite = (FLMUINT)pIStream->remainingSize(); + FLMUINT uiBytesAvail; + + flmAssert( m_pRfl->isLoggingEnabled()); + + if( !m_pRfl->haveBuffSpace( RFL_PACKET_OVERHEAD)) + { + if( RC_BAD( rc = m_pRfl->flush( m_pDb, m_pRfl->m_pCurrentBuf))) + { + goto Exit; + } + } + + while( uiBytesToWrite) + { + if( RC_BAD( rc = m_pRfl->makeRoom( m_pDb, uiBytesToWrite, + &uiPacketLen, RFL_DATA_PACKET, &uiBytesAvail, NULL))) + { + goto Exit; + } + + if( RC_BAD( rc = pIStream->read( m_pRfl->getPacketPtr()+ uiPacketLen, + uiBytesAvail))) + { + goto Exit; + } + + uiBytesToWrite -= uiBytesAvail; + uiPacketLen += uiBytesAvail; + + if( RC_BAD( rc = m_pRfl->finishPacket( m_pDb, RFL_DATA_PACKET, + uiPacketLen - RFL_PACKET_OVERHEAD, FALSE))) + { + goto Exit; + } + + uiPacketLen = RFL_PACKET_OVERHEAD; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Make room in the RFL buffer for the additional bytes. + This is done by flushing the log buffer and shifting down + the bytes already used in the current packet. If that + doesn't make room, the current packet will be finished and + a new one started. +*********************************************************************/ +RCODE F_Rfl::makeRoom( + F_Db * pDb, + FLMUINT uiAdditionalBytesNeeded, + FLMUINT * puiCurrPacketLenRV, + FLMUINT uiPacketType, + FLMUINT * puiBytesAvailableRV, + FLMUINT * puiPacketCountRV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesNeeded; + + uiBytesNeeded = *puiCurrPacketLenRV + uiAdditionalBytesNeeded; + if( uiBytesNeeded <= (FLMUINT)RFL_MAX_PACKET_SIZE) + { + FLMUINT uiTmp = uiBytesNeeded; + + if( haveBuffSpace( uiTmp)) + { + if( puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + else + { + // Bytes requested will fit into a packet, but not the + // buffer, so we need to shift the packets in the buffer + // down. The shiftPacketsDown guarantees that there + // is room in the buffer for a full size packet. + + if( RC_BAD( rc = shiftPacketsDown( pDb, *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + + // If a non-NULL puwBytesAvailableRV is passed in it means that we + // are to return the number of bytes that we can actually output. + // Since we know there is enough for the bytes needed, we simply return + // the number of bytes that were requested. + + if( puiBytesAvailableRV) + { + *puiBytesAvailableRV = uiAdditionalBytesNeeded; + } + } + } + else // (uiBytesNeeded > RFL_MAX_PACKET_SIZE) + { + // This is the case where the bytes needed would overflow the + // maximum packet size. + // If puwBytesAvailableRV is NULL, it means that all of the + // requested additional bytes must fit into the packet. In that + // case, since the requested bytes would put us over the packet + // size limit, we must finish the current packet and then + // flush the packets out of the buffer so we can start a + // new packet. + + if( !puiBytesAvailableRV) + { + // Finish the current packet and start a new one. + + if( puiPacketCountRV) + { + (*puiPacketCountRV)++; + } + + if( RC_BAD( rc = finishPacket( pDb, uiPacketType, + *puiCurrPacketLenRV - RFL_PACKET_OVERHEAD, + FALSE))) + { + goto Exit; + } + + if( RC_BAD( rc = flush( pDb, m_pCurrentBuf))) + { + goto Exit; + } + + *puiCurrPacketLenRV = RFL_PACKET_OVERHEAD; + } + else + { + // When puiBytesAvailableRV is non-NULL, it means we can fill up + // the rest of the packet with part of the bytes. In this case + // we return the number of bytes available and then shift the + // packets down in the buffer to make sure there is room for + // a full-size packet. + + *puiBytesAvailableRV = RFL_MAX_PACKET_SIZE - *puiCurrPacketLenRV; + if( RC_BAD( rc = shiftPacketsDown( pDb, *puiCurrPacketLenRV, FALSE))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Reads a full packet, based on what file offset and read + offset are currently set to. +*********************************************************************/ +RCODE F_Rfl::readPacket( + FLMUINT uiMinBytesNeeded) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiTmpOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + + // If we have enough bytes in the buffer for the minimum bytes + // needed, we don't need to retrieve any more bytes. + + if( m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset && + m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset >= uiMinBytesNeeded) + { + goto Exit; + } + + // If we are doing restore, we have to do only sequential + // reads - cannot depend on doing reads on 512 byte boundaries. + // Otherwise, we read directly from disk on 512 byte boundaries. + + if( m_pRestore) + { + FLMUINT uiCurrFilePos = m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes; + + if( m_uiRflReadOffset > 0 && + m_pCurrentBuf->uiRflBufBytes >= m_uiRflReadOffset) + { + // Move the bytes left in the buffer down to the beginning + // of the buffer. + + f_memmove( m_pCurrentBuf->pIOBuffer->getBuffer(), + &(m_pCurrentBuf->pIOBuffer->getBuffer()[ m_uiRflReadOffset]), + m_pCurrentBuf->uiRflBufBytes - m_uiRflReadOffset); + m_pCurrentBuf->uiRflBufBytes -= m_uiRflReadOffset; + m_pCurrentBuf->uiRflFileOffset += m_uiRflReadOffset; + m_uiRflReadOffset = 0; + } + uiReadLen = m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes; + + // Read enough to fill the rest of the buffer, which is + // guaranteed to hold at least one full packet. + + if( !m_uiFileEOF) + { + if( uiCurrFilePos > (FLMUINT)(-1) - uiReadLen) + { + uiReadLen = (FLMUINT)(-1) - uiCurrFilePos; + } + } + else + { + if( uiCurrFilePos + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - uiCurrFilePos; + } + } + + // If reading will not give us the minimum bytes needed, + // we cannot satisfy this request from the current file. + + if( uiReadLen + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Read enough to get the entire packet. + + if( RC_BAD( rc = m_pRestore->read( uiReadLen, + &(m_pCurrentBuf->pIOBuffer->getBuffer()[ + m_pCurrentBuf->uiRflBufBytes]), &uiBytesRead))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if( m_pRestoreStatus) + { + eRestoreAction eAction; + + if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( + &eAction, m_pCurrentBuf->uiCurrFileNum, uiBytesRead))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + // If we didn't read enough to satisfy the minimum bytes needed, + // we cannot satisfy this request from the current file. + + if( uiBytesRead + m_pCurrentBuf->uiRflBufBytes < uiMinBytesNeeded) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes += uiBytesRead; + } + else + { + // Set offsets so we are on a 512 byte boundary for our + // next read. No need to move data, since we will be + // re-reading it anyway. + + if( m_uiRflReadOffset > 0) + { + uiTmpOffset = m_uiRflReadOffset - (m_uiRflReadOffset & 511); + m_pCurrentBuf->uiRflFileOffset += uiTmpOffset; + m_uiRflReadOffset -= uiTmpOffset; + } + else if( m_pCurrentBuf->uiRflFileOffset & 511) + { + m_uiRflReadOffset = m_pCurrentBuf->uiRflFileOffset & 511; + m_pCurrentBuf->uiRflFileOffset -= m_uiRflReadOffset; + } + + m_pCurrentBuf->uiRflBufBytes = 0; + + // Read enough to fill the rest of the buffer, which is + // guaranteed to hold at least one full packet. + + uiReadLen = m_uiBufferSize; + + // m_uiFileEOF better not be zero at this point - we should + // always know precisely where the RFL file ends when we + // are doing recovery as opposed to doing a restore. + + flmAssert( m_uiFileEOF >= 512); + if( m_pCurrentBuf->uiRflFileOffset + uiReadLen > m_uiFileEOF) + { + uiReadLen = m_uiFileEOF - m_pCurrentBuf->uiRflFileOffset; + } + + // If reading will not give us the minimum number of bytes + // needed, we have a bad packet. + + if( uiReadLen < m_uiRflReadOffset || + uiReadLen - m_uiRflReadOffset < uiMinBytesNeeded) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Read to get the entire packet. + + if( RC_BAD( rc = m_pFileHdl->SectorRead( m_pCurrentBuf->uiRflFileOffset, + uiReadLen, m_pCurrentBuf->pIOBuffer->getBuffer(), + &uiBytesRead))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + else + { + m_bRflVolumeOk = FALSE; + goto Exit; + } + } + + if( uiBytesRead < uiReadLen) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + m_pCurrentBuf->uiRflBufBytes = uiReadLen; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Gets and verifies the next packet from the roll-forward + log file. Packet checksum will be verified. +*********************************************************************/ +RCODE F_Rfl::getPacket( + F_Db * pDb, + FLMBOOL bForceNextFile, + FLMUINT * puiPacketTypeRV, + const FLMBYTE ** ppucPacketBodyRV, + FLMUINT * puiPacketBodyLenRV) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucPacket; + const FLMBYTE * pucPacketBody; + const FLMBYTE * pucPacketBodyEnd; + FLMUINT uiOrigPacketBodyLen; + FLMUINT uiPacketBodyLen; + FLMUINT uiPacketType; + FLMBYTE ucHdr [512]; + FLMUINT uiBytesRead; + + // See if we need to go to the next file. Note that we only + // check for this exactly on packet boundaries. We do not expect + // packets to be split across files. If we are not at the end + // of processing what is in the buffer, we should be able to + // read the rest of the packet from the current file. + +Get_Next_File: + + if( bForceNextFile || + (m_uiFileEOF && m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + m_pCurrentBuf->uiRflBufBytes == + m_uiFileEOF)) + { + if( m_bKeepRflFiles) + { + if( !m_pRestore) + { + // Only doing recovery after a failure, see if we are at + // the last file already. + + if( m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + rc = RC_SET( NE_XFLM_END); + goto Exit; + } + else if( (m_pCurrentBuf->uiCurrFileNum + 1 ) == + m_uiLastRecoverFileNum && + !m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset) + { + // We are going to try to open the last file. Since the log + // header shows a current offset of 0, the file may have been + // created but nothing was logged to it. We don't want to try + // to open it here because it may not have been initialized + // fully at the time of the server crash. + + m_pCurrentBuf->uiCurrFileNum = m_uiLastRecoverFileNum; + rc = RC_SET( NE_XFLM_END); + goto Exit; + } + + // Open the next file in the sequence. + + if( RC_BAD( rc = openFile( pDb->m_hWaitSem, + m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_END); + } + + goto Exit; + } + + // If this is the last RFL file, the EOF is contained + // in the log header. Otherwise, it will be in the RFL + // file's header, and openFile will already have retrieved it. + + if( m_pCurrentBuf->uiCurrFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = + m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; + + // Could be zero if RFL file wasn't created yet. + + if( !m_uiFileEOF) + { + m_uiFileEOF = 512; + } + } + + // By this point, the EOF better be greater than or equal to 512. + + flmAssert( m_uiFileEOF >= 512); + } + else + { + if( RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + + // Ask the recovery object to open the file. + + if( RC_BAD( rc = m_pRestore->openRflFile( + m_pCurrentBuf->uiCurrFileNum + 1))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = RC_SET( NE_XFLM_END); + } + + goto Exit; + } + + if( m_pRestoreStatus) + { + eRestoreAction eAction; + + if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile( + &eAction, m_pCurrentBuf->uiCurrFileNum + 1))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + // Get the first 512 bytes from the file and verify the header. + + if( RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + + if( m_pRestoreStatus) + { + eRestoreAction eAction; + + if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( + &eAction, m_pCurrentBuf->uiCurrFileNum + 1, uiBytesRead))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + if( uiBytesRead < 512) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + + if( RC_BAD( rc = verifyHeader( ucHdr, + m_pCurrentBuf->uiCurrFileNum + 1, m_ucNextSerialNum))) + { + goto Exit; + } + + // We may not know the actual EOF of files during restore + // operations. m_uiFileEOF could be zero here. + + m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr[ RFL_EOF_POS]); + + // File EOF may be zero or >= 512 at this point. + + flmAssert( !m_uiFileEOF || m_uiFileEOF >= 512); + + // Need to increment current file number. + + m_pCurrentBuf->uiCurrFileNum++; + } + + m_pCurrentBuf->uiRflFileOffset = 512; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Get the next packet from the new file. + + if( RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + if( m_uiFileEOF == 512 && m_bKeepRflFiles) + { + // File was empty, try to go to the next file. + + bForceNextFile = TRUE; + goto Get_Next_File; + } + goto Exit; + } + } + else + { + // This is the case where we are not keeping the RFL files. + // So, there is no next file to go to. If we get to this + // point, we had better not be doing a restore. + + flmAssert( m_pRestore == NULL && !bForceNextFile); + rc = RC_SET( NE_XFLM_END); + goto Exit; + } + } + + // Make sure we at least have a packet header in the buffer. + + if (RC_BAD( rc = readPacket( RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + + // Verify the packet address. + + m_uiPacketAddress = m_pCurrentBuf->uiRflFileOffset + m_uiRflReadOffset; + pucPacket = &(m_pCurrentBuf->pIOBuffer->getBuffer()[m_uiRflReadOffset]); + if ((FLMUINT)FB2UD( &pucPacket [RFL_PACKET_ADDRESS_OFFSET]) != + m_uiPacketAddress) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + // Get packet type, time flag, and packet body length + + *puiPacketTypeRV = uiPacketType = + RFL_GET_PACKET_TYPE( pucPacket [RFL_PACKET_TYPE_OFFSET]); + + uiPacketBodyLen = uiOrigPacketBodyLen = + (FLMUINT)FB2UW( &pucPacket [RFL_PACKET_BODY_LENGTH_OFFSET]); + + // Make sure we have the entire packet in the buffer. + + if (RC_BAD( rc = readPacket( uiPacketBodyLen + RFL_PACKET_OVERHEAD))) + { + goto Exit; + } + pucPacket = &(m_pCurrentBuf->pIOBuffer->getBuffer()[m_uiRflReadOffset]); + + // At this point, we are guaranteed to have the entire packet + // in the buffer. + + pucPacketBody = &pucPacket [RFL_PACKET_OVERHEAD]; + pucPacketBodyEnd = pucPacketBody + uiPacketBodyLen; + + // Validate the packet checksum + + if (RflCalcChecksum( pucPacket, uiPacketBodyLen) != + pucPacket [RFL_PACKET_CHECKSUM_OFFSET]) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if (uiPacketType == RFL_TRNS_BEGIN_PACKET || + uiPacketType == RFL_UPGRADE_PACKET || + uiPacketType == RFL_REDUCE_PACKET || + uiPacketType == RFL_WRAP_KEY_PACKET || + uiPacketType == RFL_ENABLE_ENCRYPTION_PACKET || + uiPacketType == RFL_ROLL_OVER_DB_KEY_PACKET) + { + // Current transaction ID better be zero, otherwise, we + // have two or more begin packets in a row. + + if( m_ui64CurrTransID) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, + pucPacketBodyEnd, &m_ui64CurrTransID))) + { + goto Exit; + } + + uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody); + + // Make sure the transaction numbers are ascending + + if( m_ui64CurrTransID <= m_ui64LastTransID) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + } + else + { + // If transaction ID is not zero, we are not inside a + // transaction, and it is likely that we have a corrupt + // packet. + + if( !m_ui64CurrTransID) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( uiPacketType == RFL_TRNS_COMMIT_PACKET || + uiPacketType == RFL_TRNS_ABORT_PACKET) + { + FLMUINT64 ui64Tmp; + + if( RC_BAD( rc = flmDecodeSEN64( &pucPacketBody, + pucPacketBodyEnd, &ui64Tmp))) + { + goto Exit; + } + + if( ui64Tmp != m_ui64CurrTransID) + { + rc = RC_SET( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + uiPacketBodyLen = (FLMUINT)(pucPacketBodyEnd - pucPacketBody); + } + } + + // Set read offset to beginning of next packet. + + m_uiRflReadOffset += (RFL_PACKET_OVERHEAD + uiOrigPacketBodyLen); + *puiPacketBodyLenRV = uiPacketBodyLen; + *ppucPacketBodyRV = pucPacketBody; + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: Restore transactions from the roll-forward log to the + database. +*********************************************************************/ +RCODE F_Rfl::recover( + F_Db * pDb, + IF_RestoreClient * pRestore, + IF_RestoreStatus * pRestoreStatus) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiStartFileNum; + FLMUINT uiStartOffset; + FLMUINT uiOffset; + FLMUINT uiReadLen; + FLMUINT uiBytesRead; + FLMBYTE ucHdr[ 512]; + FLMUINT uiPacketType; + const FLMBYTE * pucPacketBody; + FLMUINT uiPacketBodyLen = 0; + eRestoreAction eAction; + FLMBOOL bTransActive = FALSE; + FLMBOOL bHadOperations = FALSE; + FLMBOOL bLastTransEndedAtFileEOF = FALSE; + FLMBOOL bForceNextFile; + FLMUINT uiRflToken = 0; + + flmAssert( m_pDatabase); + + m_pCurrentBuf = &m_Buf1; + m_ui64LastLoggedCommitTransID = 0; + + // We need to allow all updates logged in the RFL + // (including dictionary updates). + + pDb->m_bItemStateUpdOk = TRUE; + + // Turn off logging. + + disableLogging( &uiRflToken); + + // Set the replay flag on the database. + + pDb->m_uiFlags |= FDB_REPLAYING_RFL; + + // Set the flag as to whether or not we are using multiple RFL files. + + m_bKeepRflFiles = m_pDatabase->m_lastCommittedDbHdr.ui8RflKeepFiles + ? TRUE + : FALSE; + + // If pRestore is NULL, we are doing a database recovery after + // open. In that case, we start from the last checkpoint offset + // and only run until the last transaction offset. + + m_pRestoreStatus = pRestoreStatus; + + if( (m_pRestore = pRestore) == NULL) + { + FLMBYTE * pucCheckSerialNum; + FLMUINT uiEndOffset; + + // Clear the restore status pointer. Assert that the + // pointer is NULL so we can catch the improper use + // of this object. + + if( m_pRestoreStatus) + { + flmAssert( 0); + m_pRestoreStatus = NULL; + } + + uiStartFileNum = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum; + m_uiLastRecoverFileNum = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; + uiStartOffset = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset; + uiEndOffset = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; + + // Could be zero if the file was created, but no transactions were + // ever committed to it. + + if( !uiEndOffset) + { + uiEndOffset = 512; + } + + // Start offset better not be less than 512. + + flmAssert( uiStartOffset >= 512); + flmAssert( uiEndOffset >= 512); + + // If start and end are at the same place, there is nothing + // to recover. + + if( uiStartFileNum == m_uiLastRecoverFileNum && + uiStartOffset == uiEndOffset) + { + goto Finish_Recovery; + } + + // We have not recorded the serial number of the last checkpoint file + // number, so we pass in NULL, unless it happens to be the same as the + // last transaction file number, in which case we can pass in the + // serial number we have stored in the log header. + + pucCheckSerialNum = + (uiStartFileNum == m_uiLastRecoverFileNum) + ? &m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum [0] + : NULL; + + if( RC_BAD( rc = openFile( pDb->m_hWaitSem, + uiStartFileNum, pucCheckSerialNum))) + { + goto Exit; + } + + // If this is the last RFL file, the EOF is contained + // in the log header. Otherwise, it will be in the RFL + // file's header, and openFile will already have retrieved it. + + if( uiStartFileNum == m_uiLastRecoverFileNum) + { + m_uiFileEOF = uiEndOffset; + } + + // At this point, file EOF better be greater than or equal to 512. + + flmAssert( m_uiFileEOF >= 512); + } + else if( !m_bKeepRflFiles) + { + // FlmDbRestore should be checking the "keep" flag and not + // attempting to do a restore of the RFL. + + rc = RC_SET_AND_ASSERT( NE_XFLM_CANNOT_RESTORE_RFL_FILES); + goto Exit; + } + else + { + uiStartFileNum = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum; + uiStartOffset = + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset; + + // Could be zero if the RFL file had never been created. + + if( !uiStartOffset) + { + uiStartOffset = 512; + } + + // Ask the recovery object to open the file. + +Retry_Open: + + flmAssert( uiStartFileNum); + if( RC_BAD( rc = m_pRestore->openRflFile( uiStartFileNum))) + { + if( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + // Need to set m_pCurrentBuf->uiCurrFileNum in case the first + // call to openRflFile fails. This will cause the code at the + // Finish_Recovery label to correctly set up the log + // header. + + if( !uiStartOffset) + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum - 1; + } + else + { + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + } + + rc = NE_XFLM_OK; + goto Finish_Recovery; + } + else + { + goto Exit; + } + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportOpenRflFile( + &eAction, uiStartFileNum))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + // Get the first 512 bytes from the file and verify the header. + + if( RC_BAD( rc = m_pRestore->read( 512, ucHdr, &uiBytesRead))) + { + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( + &eAction, uiStartFileNum, uiBytesRead))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + if( uiBytesRead < 512) + { + rc = RC_SET( NE_XFLM_NOT_RFL); + goto Exit; + } + + if( RC_BAD( rc = verifyHeader( ucHdr, uiStartFileNum, + m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) + { + RCODE tmpRc; + + if( m_pRestoreStatus) + { + if( RC_BAD( tmpRc = m_pRestoreStatus->reportError( &eAction, rc))) + { + rc = tmpRc; + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_RETRY) + { + if( RC_BAD( rc = m_pRestore->close())) + { + goto Exit; + } + + goto Retry_Open; + } + } + + goto Exit; + } + + // We may not know the actual EOF of files during restore operations. + + if( (m_uiFileEOF = (FLMUINT)FB2UD( &ucHdr [RFL_EOF_POS])) == 0) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = (m_uiFileEOF == uiStartOffset) + ? TRUE + : FALSE; + } + + // Position to the start offset. Unfortunately, this means reading + // through the data and discarding it. + + uiOffset = 512; + while( uiOffset < uiStartOffset) + { + uiReadLen = (uiStartOffset - uiOffset); + if( uiReadLen > m_uiBufferSize) + { + uiReadLen = m_uiBufferSize; + } + + if( RC_BAD( rc = m_pRestore->read( uiReadLen, + m_pCurrentBuf->pIOBuffer->getBuffer(), &uiBytesRead))) + { + goto Exit; + } + + if( m_pRestoreStatus) + { + if( RC_BAD( rc = m_pRestoreStatus->reportRflRead( + &eAction, uiStartFileNum, uiBytesRead))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + // RFL file is incomplete if we could not read up to the last + // committed transaction. + + if( uiBytesRead < uiReadLen) + { + rc = RC_SET( NE_XFLM_RFL_INCOMPLETE); + goto Exit; + } + + uiOffset += uiBytesRead; + } + + // Need to set current file number + + m_pCurrentBuf->uiCurrFileNum = uiStartFileNum; + + // Better not be any transactions to recover - last database + // state needs to be a completed checkpoint. + + flmAssert( + m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID == + m_pDatabase->m_lastCommittedDbHdr.ui64CurrTransID); + + // Use uiStartOffset here instead of ui32RflLastTransOffset, + // because ui32RflLastTransOffset may be zero, but we in that + // case we should be comparing to 512, and uiStartOffset will have + // been adjusted to 512 if that is the case. + + flmAssert( + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPOffset == + uiStartOffset); + + flmAssert( + (FLMUINT)m_pDatabase->m_lastCommittedDbHdr.ui32RflLastCPFileNum == + uiStartFileNum); + } + + // Set last transaction ID to the last transaction + // that was checkpointed - transaction numbers should ascend + // from here. + + m_ui64LastTransID = + m_pDatabase->m_lastCommittedDbHdr.ui64RflLastCPTransID; + + // Set the last committed trans ID + + m_ui64LastLoggedCommitTransID = + m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID; + + m_pCurrentBuf->uiRflFileOffset = uiStartOffset; + m_uiRflReadOffset = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + + // Now, read until we are done. + + bForceNextFile = FALSE; + for (;;) + { + flmAssert( pDb->m_uiFlags & FDB_REPLAYING_RFL); + + // Get the next operation from the file. + + rc = getPacket( pDb, bForceNextFile, &uiPacketType, &pucPacketBody, + &uiPacketBodyLen); + bForceNextFile = FALSE; + + if( RC_BAD( rc)) + { + if( rc == NE_XFLM_END) + { + if( !m_pRestore) + { + // If we didn't end exactly where we should have, we have + // an incomplete log. The same is true if we are in the + // middle of a transaction. + + if( m_pCurrentBuf->uiCurrFileNum != m_uiLastRecoverFileNum || + bTransActive) + { + rc = RC_SET( NE_XFLM_RFL_INCOMPLETE); + } + else + { + rc = NE_XFLM_OK; + goto Finish_Recovery; + } + } + else + { + // If we are doing a restore, and we get to the end of the + // log, it is OK - even if we are in the middle of a + // transaction - the transaction will simply be aborted. + + rc = NE_XFLM_OK; + goto Finish_Recovery; + } + } + else if( rc == NE_XFLM_BAD_RFL_PACKET) + { + // If we don't know the current file size, and we + // are doing a restore, it is OK to end on a bad + // packet - we will simply abort the current + // transaction, if any. Then, try to go to the + // next file, because we really don't know where + // this file ends. + + if( m_pRestore && !m_uiFileEOF) + { + if( bTransActive) + { + pDb->transAbort(); + bTransActive = FALSE; + } + + // Set current transaction ID to zero - as if we had encountered + // an abort packet. + + m_ui64CurrTransID = 0; + bLastTransEndedAtFileEOF = TRUE; + + // Force to go to the next file + + bForceNextFile = TRUE; + rc = NE_XFLM_OK; + continue; + } + } + + goto Exit; + } + + // At this point, we know we have a good packet, see what it + // is and handle it. + + bHadOperations = TRUE; + eAction = XFLM_RESTORE_ACTION_CONTINUE; + + switch (uiPacketType) + { + case RFL_TRNS_BEGIN_PACKET: + { + // If we already have a transaction active, we have + // a problem. + + flmAssert( !bTransActive); + + if( uiPacketBodyLen) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + goto Exit; + } + + if( RC_BAD( rc = recovTransBegin( pDb, &eAction))) + { + goto Exit; + } + + if (eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + bTransActive = TRUE; + break; + } + + case RFL_TRNS_COMMIT_PACKET: + { + // Commit the current transaction. + + if (m_pRestoreStatus) + { + if (RC_BAD( rc = m_pRestoreStatus->reportCommitTrans( + &eAction, m_ui64CurrTransID))) + { + goto Exit; + } + + if (eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + pDb->m_uiFlags |= FDB_REPLAYING_COMMIT; + rc = pDb->transCommit(); + pDb->m_uiFlags &= ~FDB_REPLAYING_COMMIT; + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + + m_ui64LastLoggedCommitTransID = m_ui64CurrTransID; + +Finish_Transaction: + + if (!m_uiFileEOF) + { + bLastTransEndedAtFileEOF = TRUE; + } + else + { + bLastTransEndedAtFileEOF = + (m_uiRflReadOffset == m_pCurrentBuf->uiRflBufBytes && + m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes == m_uiFileEOF) + ? TRUE + : FALSE; + } + + m_ui64CurrTransID = 0; + break; + } + + case RFL_TRNS_ABORT_PACKET: + { + // Abort the current transaction. + + if (m_pRestoreStatus) + { + if (RC_BAD( rc = m_pRestoreStatus->reportAbortTrans( + &eAction, m_ui64CurrTransID))) + { + goto Exit; + } + + if (eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + } + + flmAssert( bTransActive); + + m_uiLastLfNum = 0; + m_eLastLfType = XFLM_LF_INVALID; + + rc = pDb->transAbort(); + bTransActive = FALSE; + + if (RC_BAD( rc)) + { + goto Exit; + } + + goto Finish_Transaction; + } + + case RFL_REDUCE_PACKET: + { + if( RC_BAD( rc = recovReduce( pDb, pucPacketBody, + uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + goto Finish_Transaction; + } + + case RFL_UPGRADE_PACKET: + { + if( RC_BAD( rc = recovUpgrade( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + goto Finish_Transaction; + } + + case RFL_INDEX_SUSPEND_PACKET: + case RFL_INDEX_RESUME_PACKET: + { + if( RC_BAD( rc = recovIndexSuspendResume( pDb, + uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_BLK_CHAIN_FREE_PACKET: + { + if( RC_BAD( rc = recovBlockChainFree( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ENABLE_ENCRYPTION_PACKET: + case RFL_WRAP_KEY_PACKET: + { + if( RC_BAD( rc = recovEncryptionKey( pDb, + uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + goto Finish_Transaction; + } + + case RFL_ENC_DEF_KEY_PACKET: + { + if( RC_BAD( rc = recovEncDefKey( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ROLL_OVER_DB_KEY_PACKET: + { + if( RC_BAD( rc = recovRollOverDbKey( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + goto Finish_Transaction; + } + + case RFL_NODE_DELETE_PACKET: + { + if( RC_BAD( rc = recovNodeDelete( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_CHILDREN_DELETE_PACKET: + { + if( RC_BAD( rc = recovNodeChildrenDelete( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_CREATE_PACKET: + { + if( RC_BAD( rc = recovNodeCreate( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ATTR_CREATE_PACKET: + { + if( RC_BAD( rc = recovAttributeCreate( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ATTR_DELETE_PACKET: + { + if( RC_BAD( rc = recovAttributeDelete( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ENC_NODE_UPDATE_PACKET: + { + if( RC_BAD( rc = recovEncryptedNodeUpdate( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_SET_TEXT_VALUE_PACKET: + case RFL_NODE_SET_BINARY_VALUE_PACKET: + { + if( RC_BAD( rc = recovNodeSetValue( pDb, + uiPacketType, pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_SET_NUMBER_VALUE_PACKET: + { + if( RC_BAD( rc = recovNodeSetNumberValue( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_ATTR_SET_VALUE_PACKET: + { + if( RC_BAD( rc = recovAttrSetValue( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_CLEAR_VALUE_PACKET: + { + if( RC_BAD( rc = recovNodeClearValue( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_DOCUMENT_DONE_PACKET: + { + if( RC_BAD( rc = recovDocumentDone( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_INSERT_BEFORE_PACKET: + { + if( RC_BAD( rc = recovInsertBefore( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_FLAGS_UPDATE_PACKET: + { + if( RC_BAD( rc = recovNodeFlagsUpdate( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_SET_PREFIX_ID_PACKET: + { + if( RC_BAD( rc = recovNodeSetPrefixId( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_SET_NEXT_NODE_ID_PACKET: + { + if( RC_BAD( rc = recovSetNextNodeId( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + case RFL_NODE_SET_META_VALUE_PACKET: + { + if( RC_BAD( rc = recovNodeSetMetaValue( pDb, + pucPacketBody, uiPacketBodyLen, &eAction))) + { + goto Exit; + } + + if( eAction == XFLM_RESTORE_ACTION_STOP) + { + bLastTransEndedAtFileEOF = FALSE; + goto Finish_Recovery; + } + + break; + } + + default: + { + // Should not be getting other packet types at this + // point. + + // If we don't know the current file size, and we + // are doing a restore, it is OK to end on a bad + // packet - we will simply abort the current + // transaction, if any. + + if( m_pRestore && !m_uiFileEOF) + { + rc = NE_XFLM_OK; + goto Finish_Recovery; + } + else + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BAD_RFL_PACKET); + } + + goto Exit; + } + } + } + +Finish_Recovery: + + if( bTransActive) + { + pDb->transAbort(); + bTransActive = FALSE; + } + + if( m_pRestore) + { + FLMUINT uiNextRflFileNum = m_pCurrentBuf->uiCurrFileNum + 1; + + // At the end of the restore operation, we need to set things + // up so that the next transaction will begin a new RFL file. + // If we ended the restore in the middle of an RFL file, we + // need to set it up so that the new RFL file will have a new + // serial number. If we ended at the end of an RFL file, we + // can set it up so that the new RFL file will have the next + // serial number. + + // Set up the next RFL file number and offset. + + m_pDatabase->m_lastCommittedDbHdr.ui32RflCurrFileNum = + (FLMINT32)uiNextRflFileNum; + + // Set a zero into the offset, this is a special case which tells + // us that we should create the file no matter what - even if + // it already exists - it should be overwritten. + + m_pDatabase->m_lastCommittedDbHdr.ui32RflLastTransOffset = 0; + + if( bLastTransEndedAtFileEOF) + { + // Move the next serial number of the last RFL file processed into + // into the current RFL serial number so that the log header + // will be correct + + f_memcpy( + m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum, + m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + } + else + { + // Must create a new serial number so that when the new RFL + // file is created, it will have that next serial number. + + if( RC_BAD( rc = f_createSerialNumber( + m_pDatabase->m_lastCommittedDbHdr.ucLastTransRflSerialNum))) + { + goto Exit; + } + } + + // Save the last logged commit transaction ID. + + if( m_ui64LastLoggedCommitTransID) + { + m_pDatabase->m_lastCommittedDbHdr.ui64LastRflCommitID = + m_ui64LastLoggedCommitTransID; + } + + // No matter what, we must generate a new next serial number. This + // is what will be written to the new RFL file's header when it is + // created. + + if( RC_BAD( rc = f_createSerialNumber( + m_pDatabase->m_lastCommittedDbHdr.ucNextRflSerialNum))) + { + goto Exit; + } + } + + if( !bHadOperations) + { + // No transactions were recovered, but still need to + // setup a few things. + + m_pDatabase->m_uiFirstLogCPBlkAddress = 0; + m_pDatabase->m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Save the state of the log header into the checkpointDbHdr buffer. + + f_memcpy( &m_pDatabase->m_checkpointDbHdr, + &m_pDatabase->m_lastCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + } + + // Force a checkpoint to force the log files to be truncated and + // everything to be reset. This is done because during recovery + // the checkpoints that are executed do NOT truncate the RFL file - + // because it is using the log file to recover! + + closeFile(); + + m_pRestore = NULL; + m_pRestoreStatus = NULL; + enableLogging( &uiRflToken); + + pDb->m_uiFlags &= ~FDB_REPLAYING_RFL; + + if( RC_BAD( rc = pDb->doCheckpoint( 0))) + { + goto Exit; + } + +Exit: + + if( bTransActive) + { + pDb->transAbort(); + } + + pDb->m_bItemStateUpdOk = FALSE; + pDb->m_uiFlags &= ~FDB_REPLAYING_RFL; + + m_uiLastLfNum = 0; + return( rc); +} + +/**************************************************************************** +Desc: Returns the name of an RFL file given its number +****************************************************************************/ +void XFLMAPI F_Db::getRflFileName( + FLMUINT uiFileNum, + FLMBOOL bBaseOnly, + char * pszFileName, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated) +{ + if (bBaseOnly) + { + rflGetBaseFileName( uiFileNum, pszFileName, puiFileNameBufSize, + pbNameTruncated); + } + else + { + m_pDatabase->m_pRfl->getFullRflFileName( uiFileNum, + pszFileName, puiFileNameBufSize, + pbNameTruncated); + } +} diff --git a/version5/src/rfl.h b/version5/src/rfl.h new file mode 100644 index 0000000..921d228 --- /dev/null +++ b/version5/src/rfl.h @@ -0,0 +1,884 @@ +//------------------------------------------------------------------------------ +// Desc: This file contains structures and definitions used for roll +// forward logging. +// +// 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: rfl.h 3109 2006-01-19 13:07:07 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef RFL_H +#define RFL_H + +class IXKeyCompare; + +// Packet types for roll forward logging + +#define RFL_TRNS_BEGIN_PACKET 1 +#define RFL_TRNS_COMMIT_PACKET 2 +#define RFL_TRNS_ABORT_PACKET 3 +#define RFL_REDUCE_PACKET 4 +#define RFL_UPGRADE_PACKET 5 +#define RFL_INDEX_SUSPEND_PACKET 6 +#define RFL_INDEX_RESUME_PACKET 7 +#define RFL_BLK_CHAIN_FREE_PACKET 8 +#define RFL_ENABLE_ENCRYPTION_PACKET 9 +#define RFL_WRAP_KEY_PACKET 10 + +#define RFL_NODE_DELETE_PACKET 11 +#define RFL_NODE_CHILDREN_DELETE_PACKET 12 +#define RFL_NODE_CREATE_PACKET 13 +#define RFL_DOCUMENT_DONE_PACKET 14 +#define RFL_INSERT_BEFORE_PACKET 15 +#define RFL_NODE_FLAGS_UPDATE_PACKET 16 +#define RFL_NODE_SET_PREFIX_ID_PACKET 17 +#define RFL_SET_NEXT_NODE_ID_PACKET 18 +#define RFL_ENC_NODE_UPDATE_PACKET 19 +#define RFL_NODE_SET_NUMBER_VALUE_PACKET 20 +#define RFL_NODE_SET_TEXT_VALUE_PACKET 21 +#define RFL_NODE_SET_BINARY_VALUE_PACKET 22 +#define RFL_DATA_PACKET 23 +#define RFL_ROLL_OVER_DB_KEY_PACKET 24 +#define RFL_ENC_DEF_KEY_PACKET 25 +#define RFL_NODE_SET_META_VALUE_PACKET 26 +#define RFL_ATTR_SET_VALUE_PACKET 27 +#define RFL_ATTR_CREATE_PACKET 28 +#define RFL_ATTR_DELETE_PACKET 29 +#define RFL_NODE_CLEAR_VALUE_PACKET 30 + +#define RFL_PACKET_TYPE_MASK 0x7F + +// Flags for all packets + +#define RFL_HAVE_COUNTS_FLAG 0x01 +#define RFL_HAVE_DATA_FLAG 0x02 +#define RFL_FIRST_FLAG 0x04 +#define RFL_LAST_FLAG 0x08 +#define RFL_TRUNCATE_FLAG 0x10 + +#define RFL_GET_PACKET_TYPE(uiPacketType) \ + ((FLMUINT)((uiPacketType) & RFL_PACKET_TYPE_MASK)) + +// Definitions for ROLL FORWARD LOG file header format + +#define RFL_NAME_POS 0 +#define RFL_NAME "RFL5" +#define RFL_NAME_LEN 4 +#define RFL_VERSION_POS RFL_NAME_LEN +#define RFL_VERSION "5.00" +#define RFL_VERSION_LEN 4 +#define RFL_FILE_NUMBER_POS 8 +#define RFL_EOF_POS 12 +#define RFL_DB_SERIAL_NUM_POS 16 +#define RFL_SERIAL_NUM_POS (RFL_DB_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) +#define RFL_NEXT_FILE_SERIAL_NUM_POS (RFL_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) +#define RFL_KEEP_SIGNATURE_POS (RFL_NEXT_FILE_SERIAL_NUM_POS + XFLM_SERIAL_NUM_SIZE) + +#define RFL_KEEP_SIGNATURE "----KeepLog----" +#define RFL_NOKEEP_SIGNATURE "--DontKeepLog--" + +// Buffer size needs to be a multiple of 512 for direct IO writes. + +#define DEFAULT_RFL_WRITE_BUFFERS 8 +#define DEFAULT_RFL_BUFFER_SIZE (65536) + +// Definitions for packet format and sizes. + +#define RFL_PACKET_ADDRESS_OFFSET 0 +#define RFL_PACKET_CHECKSUM_OFFSET 4 +#define RFL_PACKET_TYPE_OFFSET 5 +#define RFL_PACKET_BODY_LENGTH_OFFSET 6 +#define RFL_PACKET_OVERHEAD 8 + +// Direct IO requires that we always write on 512 byte boundaries. +// This means that whenever we write out a packet, we may also +// have to write out up to the last 511 bytes of the prior packet +// in order to be on a 512 byte boundary. Thus, the buffer must +// be able to hold a full packet plus up to 512 bytes of the prior +// packet. + +#define RFL_MAX_PACKET_SIZE (65536 - 1024) +#define RFL_MAX_PACKET_BODY_SIZE (RFL_MAX_PACKET_SIZE - RFL_PACKET_OVERHEAD) + +typedef struct RflWaiterTag * RFL_WAITER_p; + +typedef struct RflWaiterTag +{ + FLMUINT uiThreadId; + FLMBOOL bIsWriter; + F_SEM hESem; + RCODE * pRc; + RFL_WAITER_p pNext; +} RFL_WAITER; + +typedef struct +{ + F_IOBufferMgr * pBufferMgr; // Write buffer manager + F_IOBuffer * pIOBuffer; + FLMUINT uiCurrFileNum; // Current file number. + FLMUINT uiRflBufBytes; // Number of bytes currently in the + // pIOBuffer. Always points to + // where the last packet ends when + // writing to the log file. + FLMUINT uiRflFileOffset; // Current offset in file that the + // zeroeth byte in the buffer + // represents. + FLMBOOL bTransInProgress; // Transaction in progress using + // these buffers. + FLMBOOL bOkToWriteHdrs; // Is it OK to update the DB + // headers with this information + XFLM_DB_HDR dbHdr; // DB header to be written with + // this buffer. + XFLM_DB_HDR cpHdr; // Checkpoint header for this + // buffer. + RFL_WAITER * pFirstWaiter; + RFL_WAITER * pLastWaiter; +} RFL_BUFFER; + +/************************************************************************** +Desc: This class handles all of the roll-forward logging for FLAIM. There + is one of these objects allocated per F_Database. +**************************************************************************/ +class F_Rfl : public XF_RefCount, public XF_Base +{ +public: + + F_Rfl(); + + ~F_Rfl(); + + RCODE setup( + F_Database * pDatabase, + const char * pszRflDir); + + RCODE finishCurrFile( + F_Db * pDb, + FLMBOOL bNewKeepState); + + RCODE logBeginTransaction( + F_Db * pDb); + + RCODE logEndTransaction( + F_Db * pDb, + FLMUINT uiPacketType, + FLMBOOL bThrowLogAway, + FLMBOOL * pbLoggedTransEnd = NULL); + + RCODE logReduce( + F_Db * pDb, + FLMUINT uiCount); + + RCODE logIndexSuspendOrResume( + F_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT uiPacketType); + + RCODE logUpgrade( + F_Db * pDb, + FLMUINT uiOldVersion); + + RCODE logBlockChainFree( + F_Db * pDb, + FLMUINT64 ui64MaintDocID, + FLMUINT uiStartBlkAddr, + FLMUINT uiEndBlkAddr, + FLMUINT uiCount); + + RCODE logEncryptionKey( + F_Db * pDb, + FLMUINT uiPacketType, + FLMBYTE * pucKey, + FLMUINT32 ui32KeyLen); + + RCODE logEncDefKey( + F_Db * pDb, + FLMUINT uiEncDefId, + FLMBYTE * pucKeyValue, + FLMUINT uiKeyValueLen, + FLMUINT uiKeySize); + + RCODE logRollOverDbKey( + F_Db * pDb); + + RCODE logDocumentDone( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64RootId); + + RCODE logNodeDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId); + + RCODE logAttributeDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiAttrName); + + RCODE logNodeChildrenDelete( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiNameId); + + RCODE logNodeCreate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ParentId, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + FLMUINT64 ui64NodeId); + + RCODE logAttributeCreate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiNameId, + FLMUINT uiNextAttrNameId); + + RCODE logAttributeSetValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64ElementNodeId, + void * pvValue, + FLMUINT uiValueLen); + + RCODE logNodeSetValue( + F_Db * pDb, + FLMUINT uiPacketType, + F_CachedNode * pCachedNode); + + RCODE logEncryptedNodeUpdate( + F_Db * pDb, + F_CachedNode * pCachedNode); + + RCODE logNodeSetNumberValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64Value, + FLMBOOL bNegative); + + RCODE logAttrSetValue( + F_Db * pDb, + F_CachedNode * pCachedNode, + FLMUINT uiAttrName); + + RCODE logInsertBefore( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64Parent, + FLMUINT64 ui64Child, + FLMUINT64 ui64RefChild); + + RCODE logNodeFlagsUpdate( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + FLMUINT uiFlags, + FLMBOOL bSetting); + + RCODE logNodeSetPrefixId( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + FLMUINT uiPrefixId); + + RCODE logNodeSetMetaValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64MetaValue); + + RCODE logSetNextNodeId( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId); + + RCODE logNodeClearValue( + F_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId); + + RCODE recover( + F_Db * pDb, + IF_RestoreClient * pRestore, + IF_RestoreStatus * pRestoreStatus); + + FLMBOOL atEndOfLog( void); + + // Returns full log file name associated with number. + // Will return the full path name. + + void getFullRflFileName( + FLMUINT uiFileNum, + char * pszFullRflFileName, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated = NULL); + + // Set the RFL directory. Passing in a NULL or empty + // string will cause the directory to be to the same + // directory where the database is located. + + RCODE setRflDir( + const char * pszRflDir); + + FINLINE const char * getRflDirPtr( void) + { + return &m_szRflDir [0]; + } + + FINLINE FLMBOOL isRflDirSameAsDbDir( void) + { + return m_bRflDirSameAsDb; + } + + FINLINE FLMUINT getCurrFileNum( void) + { + return m_pCurrentBuf->uiCurrFileNum; + } + + FINLINE FLMUINT getCurrWriteOffset( void) + { + return( m_pCurrentBuf->uiRflFileOffset + + m_pCurrentBuf->uiRflBufBytes); + } + + FINLINE FLMUINT getCurrReadOffset( void) + { + return( m_uiRflReadOffset + m_pCurrentBuf->uiRflFileOffset); + } + + FINLINE FLMUINT getCurrPacketAddress( void) + { + return( m_uiPacketAddress); + } + + FINLINE void getCurrSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( pucSerialNum, m_ucCurrSerialNum, XFLM_SERIAL_NUM_SIZE); + } + + FINLINE void setCurrSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( m_ucCurrSerialNum, pucSerialNum, XFLM_SERIAL_NUM_SIZE); + } + + FINLINE void getNextSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( pucSerialNum, m_ucNextSerialNum, XFLM_SERIAL_NUM_SIZE); + } + + FINLINE void setNextSerialNum( + FLMBYTE * pucSerialNum) + { + f_memcpy( m_ucNextSerialNum, pucSerialNum, XFLM_SERIAL_NUM_SIZE); + } + + FINLINE FLMUINT64 getCurrTransID( void) + { + return( m_ui64CurrTransID); + } + + RCODE truncate( + F_SEM hWaitSem, + FLMUINT uiTruncateSize); + + // Public functions, but only intended for internal use + + RCODE makeRoom( + F_Db * pDb, + FLMUINT uiAdditionalBytesNeeded, + FLMUINT * puiCurrPacketLenRV, + FLMUINT uiPacketType, + FLMUINT * puiBytesAvailableRV, + FLMUINT * puiPacketCountRV); + + FINLINE FLMBYTE * getPacketPtr( void) + { + return( &(m_pCurrentBuf->pIOBuffer->getBuffer()[ + m_pCurrentBuf->uiRflBufBytes])); + } + + // Close the current RFL file + + FINLINE void closeFile( void) + { + if( m_pCurrentBuf->pBufferMgr) + { + flmAssert( !m_pCurrentBuf->pBufferMgr->havePendingIO()); + } + if (m_pFileHdl) + { + m_pFileHdl->Close(); + m_pFileHdl->Release(); + m_pFileHdl = NULL; + m_pCurrentBuf->uiCurrFileNum = 0; + m_pCurrentBuf->uiRflBufBytes = 0; + m_pCurrentBuf->uiRflFileOffset = 0; + } + } + + FINLINE FLMBOOL seeIfRflVolumeOk( void) + { + return m_bRflVolumeOk; + } + + FINLINE void setRflVolumeOk( void) + { + m_bRflVolumeOk = TRUE; + m_bRflVolumeFull = FALSE; + } + + FINLINE FLMBOOL isRflVolumeFull( void) + { + return m_bRflVolumeFull; + } + + FINLINE RCODE waitPendingWrites( void) + { + if (m_uiRflWriteBufs > 1) + { + return( m_pCurrentBuf->pBufferMgr->waitForAllPendingIO()); + } + else + { + return( NE_XFLM_OK); + } + } + + RCODE waitForWrites( + F_SEM hWaitSem, + RFL_BUFFER * pBuffer, + FLMBOOL bIsWriter); + + RCODE waitForCommit( + F_SEM hWaitSem); + + FINLINE void commitDbHdrs( + XFLM_DB_HDR * pDbHdr, + XFLM_DB_HDR * pCPHdr) + { + f_memcpy( &m_pCurrentBuf->dbHdr, pDbHdr, sizeof( XFLM_DB_HDR)); + f_memcpy( &m_pCurrentBuf->cpHdr, pCPHdr, sizeof( XFLM_DB_HDR)); + m_pCurrentBuf->bOkToWriteHdrs = TRUE; + } + + FINLINE void clearDbHdrs( void) + { + m_pCurrentBuf->bOkToWriteHdrs = FALSE; + } + + FLMBOOL seeIfRflWritesDone( + F_SEM hWaitSem, + FLMBOOL bForceWait); + + void wakeUpWaiter( + RCODE rc, + FLMBOOL bIsWriter); + + RCODE completeTransWrites( + F_Db * pDb, + FLMBOOL bCommitting, + FLMBOOL bOkToUnlock); + + FINLINE void disableLogging( + FLMUINT * puiToken) + { + *puiToken = ++m_uiDisableCount; + } + + FINLINE void enableLogging( + FLMUINT * puiToken) + { + flmAssert( m_uiDisableCount); + flmAssert( *puiToken && *puiToken == m_uiDisableCount); + + m_uiDisableCount--; + *puiToken = 0; + } + + FINLINE FLMBOOL isLoggingEnabled( void) + { + return( m_uiDisableCount ? FALSE : TRUE); + } + +private: + + FINLINE FLMBYTE * getPacketBodyPtr( void) + { + return( &(m_pCurrentBuf->pIOBuffer->getBuffer()[ + m_pCurrentBuf->uiRflBufBytes + RFL_PACKET_OVERHEAD])); + } + + FINLINE FLMBOOL haveBuffSpace( + FLMUINT uiSpaceNeeded + ) + { + return( (FLMBOOL)((m_uiBufferSize - m_pCurrentBuf->uiRflBufBytes >= + uiSpaceNeeded) + ? (FLMBOOL)TRUE + : (FLMBOOL)FALSE) ); + } + + // Write the header of an RFL file. + + RCODE writeHeader( + FLMUINT uiFileNum, + FLMUINT uiEof, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature); + + // Verify the header of an RFL file. + + RCODE verifyHeader( + FLMBYTE * pucHeader, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum); + + // Open a new RFL file. + + RCODE openFile( + F_SEM hWaitSem, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum); + + // Create a new RFL file + + RCODE createFile( + F_Db * pDb, + FLMUINT uiFileNum, + FLMBYTE * pucSerialNum, + FLMBYTE * pucNextSerialNum, + FLMBOOL bKeepSignature); + + void copyLastSector( + RFL_BUFFER * pBuffer, + FLMBYTE * pucOldBuffer, + FLMBYTE * pucNewBuffer, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile); + + // Position to an offset in the file. + + RCODE positionTo( + FLMUINT uiFileOffset); + + // Flush data to the current RFL file + + RCODE flush( + F_Db * pDb, + RFL_BUFFER * pBuffer, + FLMBOOL bFinalWrite = FALSE, + FLMUINT uiCurrPacketLen = 0, + FLMBOOL bStartingNewFile = FALSE); + + void switchBuffers( void); + + // Flush all packets except the current one to disk. + // Shift the current one down to close to or at the + // beginning of the buffer. + + RCODE shiftPacketsDown( + F_Db * pDb, + FLMUINT uiCurrPacketLen, + FLMBOOL bStartingNewFile); + + // See if we need to generate a new RFL file. + + RCODE seeIfNeedNewFile( + F_Db * pDb, + FLMUINT uiPacketLen, + FLMBOOL bDoNewIfOverLowLimit); + + // Calculate checksum, etc. on current packet. + + RCODE finishPacket( + F_Db * pDb, + FLMUINT uiPacketType, + FLMUINT uiPacketBodyLen, + FLMBOOL bDoNewIfOverLowLimit); + + // Functions for reading log files + + RCODE readPacket( + FLMUINT uiMinBytesNeeded); + + RCODE getPacket( + F_Db * pDb, + FLMBOOL bForceNextFile, + FLMUINT * puiPacketTypeRV, + const FLMBYTE ** ppucPacketBodyRV, + FLMUINT * puiPacketBodyLenRV); + + RCODE setupTransaction( + F_Db * pDb); + + void finalizeTransaction( void); + + RCODE recovTransBegin( + F_Db * pDb, + eRestoreAction * peAction); + + RCODE recovBlockChainFree( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovIndexSuspendResume( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovReduce( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovUpgrade( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovEncryptionKey( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovEncDefKey( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovRollOverDbKey( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovDocumentDone( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovAttributeDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeChildrenDelete( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeCreate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovAttributeCreate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovInsertBefore( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovEncryptedNodeUpdate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeSetValue( + F_Db * pDb, + FLMUINT uiPacketType, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeSetNumberValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovAttrSetValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeFlagsUpdate( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeSetPrefixId( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeSetMetaValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovSetNextNodeId( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + RCODE recovNodeClearValue( + F_Db * pDb, + const FLMBYTE * pucPacketBody, + FLMUINT uiPacketBodyLen, + eRestoreAction * peAction); + + FLMBOOL useDataOnlyBlocks( + F_Db * pDb, + FLMUINT uiDataLen); + + // Member variables + + F_Database * m_pDatabase; // Pointer to database + RFL_BUFFER m_Buf1; + RFL_BUFFER m_Buf2; + F_MUTEX m_hBufMutex; + RFL_BUFFER * m_pCommitBuf; // Current buffer being committedout. + // NULL if no buffer is being committed. + RFL_BUFFER * m_pCurrentBuf; // Current write buffer - points to + // m_Buf1 or m_Buf2 + FLMUINT m_uiRflWriteBufs; // Number of RFL buffers + FLMUINT m_uiBufferSize; // Buffer size + FLMBOOL m_bKeepRflFiles; // Keep RFL files after they are + // no longer needed? + FLMUINT m_uiRflMinFileSize; // Minimum RFL file size. + FLMUINT m_uiRflMaxFileSize; // Maximum RFL file size. + IF_FileHdl * m_pFileHdl; // File handle for writing to roll + // forward log file - only need one for + // the writer because we can only have + // one update transaction at a time. + FLMUINT m_uiLastRecoverFileNum; + // Last file number to go to when + // doing recovery. + FLMBYTE m_ucCurrSerialNum [XFLM_SERIAL_NUM_SIZE]; + // Current file's serial number. + FLMUINT m_uiTransStartFile; // File the current transaction started + // in. + FLMUINT m_uiTransStartAddr; // Offset of start transaction packet. + FLMUINT64 m_ui64CurrTransID; // Current transaction ID. + FLMUINT64 m_ui64LastTransID; // Last transaction ID. + FLMUINT64 m_ui64LastLoggedCommitTransID; + // Last committed transaction that + // was logged to the RFL + FLMUINT m_uiOperCount; // Operations that have been logged for + // this transaction. + FLMUINT m_uiRflReadOffset; // Offset we are reading from in the + // buffer - only used when in reading + // mode. + FLMUINT m_uiPacketAddress; // Current packet's address in the file. + FLMUINT m_uiFileEOF; // End of file for current file. + // Only used when reading. + IF_RestoreClient * m_pRestore; // Restore object. + IF_RestoreStatus * m_pRestoreStatus; + // Restore status object + char m_szRflDir [F_PATH_MAX_SIZE]; + // RFL directory + FLMBOOL m_bRflDirSameAsDb; + FLMBOOL m_bCreateRflDir; + FLMBYTE m_ucNextSerialNum [ XFLM_SERIAL_NUM_SIZE]; + // Next file's serial number. + FLMBOOL m_bRflVolumeOk; // Did we have a problem accessing the + // RFL volume? + FLMBOOL m_bRflVolumeFull; // Did we have a problem accessing the + // RFL volume? + FLMUINT m_uiLastLfNum; // Last logical file used in restore/recover + eLFileType m_eLastLfType; // The Last Lfile Type + IXKeyCompare * m_pIxCompareObject; + IF_ResultSetCompare * m_pCompareObject; + FLMUINT m_uiDisableCount; + +friend class F_RflOStream; +}; + +// Prototypes for roll forward logging functions. + +FLMBYTE RflCalcChecksum( + FLMBYTE * pucPacket, + FLMUINT uiPacketBodyLen); + +void rflGetBaseFileName( + FLMUINT uiFileNum, + char * pszBaseNameOut, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated = NULL); + +RCODE rflGetDirAndPrefix( + const char * pszDbFileName, + const char * pszRflDirIn, + char * pszRflDirOut); + +RCODE rflGetFileName( + const char * szDbName, + const char * szRflDir, + FLMUINT uiFileNum, + char * pszRflFileName); + +FLMBOOL rflGetFileNum( + const char * pszRflFileName, + FLMUINT * puiFileNum); + +#endif // ifdef RFL_H diff --git a/version5/src/scache.cpp b/version5/src/scache.cpp new file mode 100644 index 0000000..5817c18 --- /dev/null +++ b/version5/src/scache.cpp @@ -0,0 +1,8355 @@ +//------------------------------------------------------------------------------ +// Desc: Block Cache routines +// +// Tabs: 3 +// +// Copyright (c) 1997-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: scache.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +#define MAX_BLOCKS_TO_SORT 500 + +FSTATIC void ScaNotify( + FNOTIFY * pNotify, + F_CachedBlock * pUseSCache, + RCODE NotifyRc); + +FSTATIC void scaWriteComplete( + F_IOBuffer * pIOBuffer); + +#ifdef SCACHE_LINK_CHECKING +FSTATIC void scaVerify( + int iPlace); +#else +#define scaVerify(iPlace) +#endif + +/*************************************************************************** +Desc: Compare two cache blocks to determine which one has lower address. +*****************************************************************************/ +FINLINE FLMINT scaCompare( + F_CachedBlock * pSCache1, + F_CachedBlock * pSCache2) +{ + if (FSAddrIsAtOrBelow( pSCache1->blkAddress(), pSCache2->blkAddress())) + { + flmAssert( pSCache1->blkAddress() != pSCache2->blkAddress()); + return( -1); + } + else + { + return( 1); + } +} + +/*************************************************************************** +Desc: Compare two cache blocks during a sort to determine which + one has lower address. +*****************************************************************************/ +FINLINE FLMINT scaSortCompare( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + return( scaCompare( ((F_CachedBlock **)pvBuffer)[ uiPos1], + ((F_CachedBlock **)pvBuffer)[ uiPos2])); +} + +/*************************************************************************** +Desc: Swap two entries in cache table during sort. +*****************************************************************************/ +FINLINE void scaSortSwap( + void * pvBuffer, + FLMUINT uiPos1, + FLMUINT uiPos2) +{ + F_CachedBlock ** ppSCacheTbl = (F_CachedBlock **)pvBuffer; + F_CachedBlock * pTmpSCache = ppSCacheTbl[ uiPos1]; + + ppSCacheTbl[ uiPos1] = ppSCacheTbl[ uiPos2]; + ppSCacheTbl[ uiPos2] = pTmpSCache; +} + +/**************************************************************************** +Desc: Link a cache block into the list of F_Database blocks that need one or + more versions of the block to be logged. This routine assumes that + the block cache mutex is locked. +*****************************************************************************/ +void F_CachedBlock::linkToLogList( void) +{ + FLMUINT uiPrevBlkAddress; + + flmAssert( m_pDatabase); + flmAssert( m_ui16Flags & CA_DIRTY); + flmAssert( !(m_ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + flmAssert( !m_pPrevInReplaceList); + flmAssert( !m_pNextInReplaceList); + + uiPrevBlkAddress = getPriorImageAddress(); + if( uiPrevBlkAddress || !m_pNextInVersionList) + { + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if ((m_pNextInReplaceList = m_pDatabase->m_pFirstInLogList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pLastInLogList = this; + } + + setFlags( CA_IN_FILE_LOG_LIST); + m_pPrevInReplaceList = NULL; + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + m_pDatabase->m_pFirstInLogList = this; + m_pDatabase->m_uiLogListCount++; + +Exit: + + return; +} + +/**************************************************************************** +Desc: Unlinks a cache block from the F_Database's log list + NOTE: This function assumes that the block cache mutex is locked. +****************************************************************************/ +void F_CachedBlock::unlinkFromLogList( void) +{ + flmAssert( m_pDatabase); + flmAssert( m_ui16Flags & CA_IN_FILE_LOG_LIST); + flmAssert( m_pDatabase->m_uiLogListCount); + + if (m_pNextInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pLastInLogList = m_pPrevInReplaceList; + } + + if (m_pPrevInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->unprotectCachedItem(); +#endif + m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pFirstInLogList = m_pNextInReplaceList; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInReplaceList = NULL; + m_pPrevInReplaceList = NULL; + clearFlags( CA_IN_FILE_LOG_LIST); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + m_pDatabase->m_uiLogListCount--; +} + +/**************************************************************************** +Desc: Link a cache block into the list of F_Database blocks that are beyond + the EOF. The blocks are linked to the end of the list so that they + are kept in ascending order. + NOTE: This function assumes that the block cache mutex is locked. +*****************************************************************************/ +void F_CachedBlock::linkToNewList( void) +{ + flmAssert( m_pDatabase); + + flmAssert( m_ui64HighTransID == ~((FLMUINT64)0)); + flmAssert( m_ui16Flags & CA_DIRTY); + flmAssert( !(m_ui16Flags & (CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + flmAssert( !m_pPrevInReplaceList); + flmAssert( !m_pNextInReplaceList); + + if ((m_pPrevInReplaceList = m_pDatabase->m_pLastInNewList) != NULL) + { + flmAssert( scaCompare( m_pDatabase->m_pLastInNewList, this) < 0); + +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->unprotectCachedItem(); +#endif + m_pPrevInReplaceList->m_pNextInReplaceList = this; +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pFirstInNewList = this; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInReplaceList = NULL; + m_pDatabase->m_pLastInNewList = this; + setFlags( CA_IN_NEW_LIST); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + m_pDatabase->m_uiNewCount++; +} + +/**************************************************************************** +Desc: Unlinks a cache block from the F_Database's new block list + NOTE: This function assumes that the block cache mutex is locked. +****************************************************************************/ +void F_CachedBlock::unlinkFromNewList( void) +{ + flmAssert( m_pDatabase); + + flmAssert( m_ui16Flags & CA_IN_NEW_LIST); + flmAssert( m_pDatabase->m_uiNewCount); + + if (m_pNextInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pLastInNewList = m_pPrevInReplaceList; + } + + if (m_pPrevInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->unprotectCachedItem(); +#endif + m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pFirstInNewList = m_pNextInReplaceList; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInReplaceList = NULL; + m_pPrevInReplaceList = NULL; + clearFlags( CA_IN_NEW_LIST); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + m_pDatabase->m_uiNewCount--; +} + +/**************************************************************************** +Desc: Unlinks a cache block from the replace list + NOTE: This function assumes that the block cache mutex is locked. +****************************************************************************/ +void F_CachedBlock::unlinkFromReplaceList( void) +{ + FLMUINT uiSize = memSize(); + + flmAssert( !m_ui16Flags); + + if( m_pNextInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->unprotectCachedItem(); +#endif + m_pNextInReplaceList->m_pPrevInReplaceList = m_pPrevInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pNextInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pLRUReplace = m_pPrevInReplaceList; + } + + if( m_pPrevInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->unprotectCachedItem(); +#endif + m_pPrevInReplaceList->m_pNextInReplaceList = m_pNextInReplaceList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInReplaceList->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pMRUReplace = m_pNextInReplaceList; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInReplaceList = NULL; + m_pPrevInReplaceList = NULL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount); + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableCount--; + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes >= uiSize); + gv_XFlmSysData.pBlockCacheMgr->m_uiReplaceableBytes -= uiSize; +} + +/**************************************************************************** +Desc: Check hash links. + This routine assumes that the block cache mutex has already been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +void F_CachedBlock::checkHashLinks( + F_CachedBlock ** ppSCacheBucket) +{ + F_CachedBlock * pBlock; + + if (!m_pDatabase) + { + f_breakpoint( 1); + } + + if (m_pPrevInVersionList) + { + f_breakpoint( 2); + } + + if (m_pNextInVersionList == this) + { + f_breakpoint( 3); + } + + if (m_pPrevInVersionList == this) + { + f_breakpoint( 4); + } + + // Make sure that the block isn't added into the list a second time. + + for (pBlock = *ppSCacheBucket; + pBlock; + pBlock = pBlock->m_pNextInHashBucket) + { + if (this == pBlock) + { + f_breakpoint( 5); + } + } + + // Make sure the block is not in the transaction + // log list. + + for (pBlock = m_pDatabase->getTransLogList(); + pBlock; + pBlock = pBlock->m_pNextInHashBucket) + { + if (this == pBlock) + { + f_breakpoint( 6); + } + } +} +#endif + +/**************************************************************************** +Desc: Unlink a cache block from its hash bucket. This routine assumes + that the block cache mutex has already been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +void F_CachedBlock::checkHashUnlinks( + F_CachedBlock ** ppSCacheBucket) +{ + F_CachedBlock * pTmpSCache; + + // Make sure the cache is actually in this bucket + + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if (!pTmpSCache) + { + f_breakpoint( 333); + } + + for (pTmpSCache = m_pDatabase->getTransLogList(); + pTmpSCache; + pTmpSCache = pTmpSCache->m_pNextInHashBucket) + { + if (this == pTmpSCache) + { + f_breakpoint( 334); + } + } +} +#endif + +/**************************************************************************** +Desc: Link a cache block to its F_Database structure. This routine assumes + that the block cache mutex has already been locked. +****************************************************************************/ +void F_CachedBlock::linkToDatabase( + F_Database * pDatabase) +{ + flmAssert( !m_pDatabase); +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_ui16Flags & CA_WRITE_PENDING) + { + if ((m_pNextInDatabase = pDatabase->m_pPendingWriteList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + + pDatabase->m_pPendingWriteList = this; + setFlags( CA_IN_WRITE_PENDING_LIST); + } + else + { + F_CachedBlock * pPrevSCache; + F_CachedBlock * pNextSCache; + + // Link at end of dirty blocks. + + if (pDatabase->m_pLastDirtyBlk) + { + pPrevSCache = pDatabase->m_pLastDirtyBlk; + pNextSCache = pPrevSCache->m_pNextInDatabase; + } + else + { + // No dirty blocks, so link to head of list. + + pPrevSCache = NULL; + pNextSCache = pDatabase->m_pSCacheList; + } + + // If the block is dirty, change the last dirty block pointer. + + if (m_ui16Flags & CA_DIRTY) + { + pDatabase->m_pLastDirtyBlk = this; + } + + if ((m_pNextInDatabase = pNextSCache) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pNextSCache->unprotectCachedItem(); +#endif + pNextSCache->m_pPrevInDatabase = this; +#ifdef FLM_CACHE_PROTECT + pNextSCache->protectCachedItem(); +#endif + } + + if ((m_pPrevInDatabase = pPrevSCache) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pPrevSCache->unprotectCachedItem(); +#endif + pPrevSCache->m_pNextInDatabase = this; +#ifdef FLM_CACHE_PROTECT + pPrevSCache->protectCachedItem(); +#endif + } + else + { + pDatabase->m_pSCacheList = this; + } + } + + m_pDatabase = pDatabase; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/**************************************************************************** +Desc: Unlink a cache block from its F_Database object. This routine assumes + that the block cache mutex has already been locked. +****************************************************************************/ +void F_CachedBlock::unlinkFromDatabase( void) +{ + flmAssert( m_pDatabase); +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + + if (m_ui16Flags & CA_IN_WRITE_PENDING_LIST) + { + if (m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->unprotectCachedItem(); +#endif + m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pPendingWriteList = m_pNextInDatabase; + } + + if (m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + + clearFlags( CA_IN_WRITE_PENDING_LIST); + } + else + { + if (this == m_pDatabase->m_pLastDirtyBlk) + { + m_pDatabase->m_pLastDirtyBlk = m_pDatabase->m_pLastDirtyBlk->m_pPrevInDatabase; +#ifdef FLM_DEBUG + + // If m_pLastDirtyBlk is non-NULL, it had better be pointing + // to a dirty block. + + if (m_pDatabase->m_pLastDirtyBlk) + { + flmAssert( m_pDatabase->m_pLastDirtyBlk->m_ui16Flags & CA_DIRTY); + } +#endif + } + + if (m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + + if (m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->unprotectCachedItem(); +#endif + m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pSCacheList = m_pNextInDatabase; + } + + m_pNextInDatabase = NULL; + m_pPrevInDatabase = NULL; + } + + m_pDatabase = NULL; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/**************************************************************************** +Desc: Link a cache block to the free list. This routine assumes that the + block cache mutex is locked. +****************************************************************************/ +void F_CachedBlock::linkToFreeList( + FLMUINT uiFreeTime) +{ + flmAssert( !m_ui16Flags); + flmAssert( !m_pDatabase); + flmAssert( !m_pPrevInReplaceList); + flmAssert( !m_pNextInReplaceList); + + if (m_ui64HighTransID != ~((FLMUINT64)0)) + { + // Set the transaction ID to ~((FLMUINT64)0) so that the old version + // counts will be decremented if this is an old version + // of the block. Also, we want the transaction ID to be + // ~((FLMUINT64)0) so that when the block is re-used in allocBlock() + // the old version counts won't be decremented again. + + setTransID( ~((FLMUINT64)0)); + } + + if ((m_pNextInDatabase = gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree) != NULL) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = this; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pLastFree = this; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pPrevInDatabase = NULL; + m_uiBlkAddress = uiFreeTime; + m_ui16Flags = CA_FREE; +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree = this; + gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes += memSize(); + gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount++; +} + +/**************************************************************************** +Desc: Unlink a cache block from the free list. This routine assumes + that the block cache mutex has already been locked. +****************************************************************************/ +void F_CachedBlock::unlinkFromFreeList( void) +{ + FLMUINT uiSize = memSize(); + + flmAssert( !m_uiUseCount); + flmAssert( m_ui16Flags == CA_FREE); + + if( m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->unprotectCachedItem(); +#endif + m_pNextInDatabase->m_pPrevInDatabase = m_pPrevInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pNextInDatabase->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pLastFree = m_pPrevInDatabase; + } + + if( m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->unprotectCachedItem(); +#endif + m_pPrevInDatabase->m_pNextInDatabase = m_pNextInDatabase; +#ifdef FLM_CACHE_PROTECT + m_pPrevInDatabase->protectCachedItem(); +#endif + } + else + { + gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree = m_pNextInDatabase; + } + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); +#endif + m_pNextInDatabase = NULL; + m_pPrevInDatabase = NULL; + m_ui16Flags &= ~CA_FREE; + flmAssert( !m_ui16Flags); +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif + + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes >= uiSize); + gv_XFlmSysData.pBlockCacheMgr->m_uiFreeBytes -= uiSize; + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount); + gv_XFlmSysData.pBlockCacheMgr->m_uiFreeCount--; +} + +/**************************************************************************** +Desc: This routine notifies threads waiting for a pending read or write + to complete. + NOTE: This routine assumes that the block cache mutex is already + locked. +****************************************************************************/ +FSTATIC void ScaNotify( + FNOTIFY * pNotify, + F_CachedBlock * pUseSCache, + RCODE NotifyRc) +{ + while (pNotify) + { + F_SEM hSem; + + *(pNotify->pRc) = NotifyRc; + if (RC_OK( NotifyRc)) + { + if (pNotify->pvUserData) + { + *((F_CachedBlock **)pNotify->pvUserData) = pUseSCache; + } + if (pUseSCache) + { + pUseSCache->useForThread( pNotify->uiThreadId); + } + } + hSem = pNotify->hSem; + pNotify = pNotify->pNext; + f_semSignal( hSem); + } +} + +/**************************************************************************** +Desc: This routine logs information about changes to a cache block's flags +****************************************************************************/ +#ifdef FLM_DBG_LOG +void F_CachedBlock::logFlgChange( + FLMUINT16 ui16OldFlags, + char cPlace + ) +{ + char szBuf [60]; + char * pszTmp; + FLMUINT16 ui16NewFlags = m_ui16Flags; + + szBuf [0] = cPlace; + szBuf [1] = '-'; + f_strcpy( &szBuf[2], "FLG:"); + pszTmp = &szBuf [6]; + + if (ui16OldFlags & CA_DIRTY) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_DIRTY)) + { + *pszTmp++ = '-'; + } + *pszTmp++ = 'D'; + *pszTmp = 0; + } + else if (ui16NewFlags & CA_DIRTY) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+D"); + pszTmp += 2; + } + + if (ui16OldFlags & CA_WRITE_INHIBIT) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WRITE_INHIBIT)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WI"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WRITE_INHIBIT) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WI"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_READ_PENDING) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_READ_PENDING)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "RD"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_READ_PENDING) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+RD"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_WRITE_TO_LOG) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WRITE_TO_LOG)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WL"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WRITE_TO_LOG) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WL"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_LOG_FOR_CP) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_LOG_FOR_CP)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "CP"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_LOG_FOR_CP) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+CP"); + pszTmp += 3; + } + + if (ui16OldFlags & CA_WAS_DIRTY) + { + *pszTmp++ = ' '; + if (!(ui16NewFlags & CA_WAS_DIRTY)) + { + *pszTmp++ = '-'; + } + f_strcpy( pszTmp, "WD"); + pszTmp += 2; + } + else if (ui16NewFlags & CA_WAS_DIRTY) + { + *pszTmp++ = ' '; + f_strcpy( pszTmp, "+WD"); + pszTmp += 3; + } + + if (pszTmp != &szBuf [6]) + { + flmDbgLogWrite( m_pDatabase, m_uiBlkAddress, 0, getLowTransID(), szBuf); + } +} +#endif + +/**************************************************************************** +Desc: This routine frees the memory for a cache block and decrements the + necessary counters in the cache manager. + NOTE: This routine assumes that the block cache mutex is already locked. +****************************************************************************/ +F_CachedBlock::~F_CachedBlock() +{ + FLMUINT uiSize = memSize(); + + if (m_ui64HighTransID != ~((FLMUINT64)0)) + { + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes >= uiSize); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerBytes -= uiSize; + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiOldVerCount--; + } + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiByteCount >= uiSize); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiByteCount -= uiSize; + flmAssert( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount); + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount--; + if (shouldRehash( gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCount, + gv_XFlmSysData.pBlockCacheMgr->m_uiNumBuckets)) + { + if (checkHashFailTime( &gv_XFlmSysData.pBlockCacheMgr->m_uiHashFailTime)) + { + (void)gv_XFlmSysData.pBlockCacheMgr->rehash(); + } + } +} + +/**************************************************************************** +Desc: This routine unlinks a cache block from all of its lists and then + optionally frees it. NOTE: This routine assumes that the block cache + mutex is already locked. +****************************************************************************/ +void F_CachedBlock::unlinkCache( + FLMBOOL bFreeIt, + RCODE NotifyRc) +{ +#ifdef FLM_CACHE_PROTECT + FLMBOOL bProtectItem = FALSE; +#endif +#ifdef FLM_DEBUG + SCACHE_USE * pUse; +#endif + + // Cache block better not be dirty and better not need to be written + // to the log. + +#ifdef FLM_DEBUG + if( RC_OK( NotifyRc)) + { + flmAssert (!(m_ui16Flags & + (CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | + CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + } +#endif + +#ifdef FLM_CACHE_PROTECT + unprotectCachedItem(); + bProtectItem = TRUE; +#endif + + unlinkFromGlobalList(); + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( m_pDatabase, m_uiBlkAddress, 0, getLowTransID(), "UNLINK"); +#endif + + // If cache block has no previous versions linked to it, it + // is in the hash bucket and needs to be unlinked from it. + // Otherwise, it only needs to be unlinked from the version list. + + if (m_pDatabase) + { + if (!m_pPrevInVersionList) + { + F_CachedBlock ** ppSCacheBucket; + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_pDatabase->getSigBitsInBlkSize(), + (FLMUINT)m_uiBlkAddress); + unlinkFromHashBucket( ppSCacheBucket); + if (m_pNextInVersionList) + { + // Older version better not be needing to be logged + +#ifdef FLM_DEBUG + if( RC_OK( NotifyRc)) + { + flmAssert( !(m_pNextInVersionList->m_ui16Flags & + (CA_WRITE_TO_LOG | CA_DIRTY | + CA_WAS_DIRTY | CA_IN_FILE_LOG_LIST | CA_IN_NEW_LIST))); + } +#endif +#ifdef FLM_CACHE_PROTECT + m_pNextInVersionList->unprotectCachedItem(); +#endif + m_pNextInVersionList->m_pPrevInVersionList = NULL; +#ifdef FLM_CACHE_PROTECT + m_pNextInVersionList->protectCachedItem(); +#endif + + m_pNextInVersionList->linkToHashBucket( ppSCacheBucket); + m_pNextInVersionList->verifyCache( 2100); + m_pNextInVersionList = NULL; + } + } + else + { + verifyCache( 2000); + savePrevBlkAddress(); + +#ifdef FLM_CACHE_PROTECT + m_pPrevInVersionList->unprotectCachedItem(); +#endif + m_pPrevInVersionList->m_pNextInVersionList = m_pNextInVersionList; +#ifdef FLM_CACHE_PROTECT + m_pPrevInVersionList->protectCachedItem(); +#endif + + m_pPrevInVersionList->verifyCache( 2200); + if (m_pNextInVersionList) + { + // Older version better not be dirty or not yet logged. + +#ifdef FLM_DEBUG + if( RC_OK( NotifyRc)) + { + flmAssert( !(m_pNextInVersionList->m_ui16Flags & + (CA_WRITE_TO_LOG | CA_DIRTY | CA_WAS_DIRTY))); + } +#endif +#ifdef FLM_CACHE_PROTECT + m_pNextInVersionList->unprotectCachedItem(); +#endif + m_pNextInVersionList->m_pPrevInVersionList = m_pPrevInVersionList; +#ifdef FLM_CACHE_PROTECT + m_pNextInVersionList->protectCachedItem(); +#endif + + m_pNextInVersionList->verifyCache( 2300); + } + + m_pNextInVersionList = NULL; + m_pPrevInVersionList = NULL; + } +#ifdef SCACHE_LINK_CHECKING + + // Verify that the thing is not in a hash bucket. + { + F_CachedBlock ** ppSCacheBucket; + F_CachedBlock * pTmpSCache; + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_pDatabase->getSigBitsInBlkSize(), + m_uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && this != pTmpSCache) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if (pTmpSCache) + { + f_breakpoint( 4); + } + } +#endif + + unlinkFromDatabase(); + } + + if (bFreeIt) + { + + // Free the notify list associated with the cache block. + // NOTE: If there is actually a notify list, NotifyRc WILL ALWAYS + // be something other than NE_XFLM_OK. If there is a notify list, + // the notified threads will thus get a non-OK return code + // in every case. + +#ifdef FLM_DEBUG + if( NotifyRc == NE_XFLM_OK) + { + flmAssert( m_pNotifyList == NULL); + } +#endif + + ScaNotify( m_pNotifyList, NULL, NotifyRc); + m_pNotifyList = NULL; + +#ifdef FLM_DEBUG + + // Free the use list associated with the cache block + + pUse = m_pUseList; + while (pUse) + { + SCACHE_USE * pTmp; + + pTmp = pUse; + pUse = pUse->pNext; + f_free( &pTmp); + } +#endif + +#ifdef FLM_CACHE_PROTECT + bProtectItem = FALSE; +#endif + delete this; + } + +#ifdef FLM_CACHE_PROTECT + if( bProtectItem) + { + protectCachedItem(); + } +#endif +} + +/**************************************************************************** +Desc: Unlink all log blocks for a file that were logged during the transaction. + NOTE: This is only called when a transaction is aborted. + WHEN A TRANSACTION IS ABORTED, THIS FUNCTION SHOULD BE CALLED BEFORE + FREEING DIRTY BLOCKS. OTHERWISE, THE m_pPrevInVersionList POINTER + WILL BE NULL AND WILL CAUSE AN ABEND WHEN IT IS ACCESSED. + NOTE: This routine assumes that the block cache mutex has been + locked. +****************************************************************************/ +void F_Database::unlinkTransLogBlocks( void) +{ + F_CachedBlock * pSCache; + F_CachedBlock * pNextSCache; + + pSCache = m_pTransLogList; + while (pSCache) + { +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; +#endif + + if (pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( m_uiLogCacheCount); + m_uiLogCacheCount--; + } + + pSCache->clearFlags( CA_WRITE_TO_LOG | CA_LOG_FOR_CP); + pNextSCache = pSCache->m_pNextInHashBucket; + + if (pSCache->m_ui16Flags & CA_WAS_DIRTY) + { + flmAssert( this == pSCache->m_pDatabase); + pSCache->setDirtyFlag( this); + pSCache->clearFlags( CA_WAS_DIRTY); + + // Move the block into the dirty blocks. + + pSCache->unlinkFromDatabase(); + pSCache->linkToDatabase( this); + } + +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'A'); +#endif + + // Perhaps we don't really need to set these pointers to NULL, + // but it helps keep things clean. + +#ifdef FLM_CACHE_PROTECT + pSCache->unprotectCachedItem(); +#endif + pSCache->m_pNextInHashBucket = NULL; + pSCache->m_pPrevInHashBucket = NULL; +#ifdef FLM_CACHE_PROTECT + pSCache->protectCachedItem(); +#endif + + pSCache = pNextSCache; + } + m_pTransLogList = NULL; +} + +/**************************************************************************** +Desc: Unlink a cache block from the list of cache blocks that are in the log + list for the current transaction. +****************************************************************************/ +void F_CachedBlock::unlinkFromTransLogList( void) +{ + +#ifdef SCACHE_LINK_CHECKING + + // Make sure the block is not in a hash bucket + + { + F_CachedBlock ** ppSCacheBucket; + F_CachedBlock * pTmpSCache; + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_pDatabase->m_uiSigBitsInBlkSize, + m_uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if (pTmpSCache) + { + f_breakpoint( 1001); + } + + // Make sure the block is in the log list. + + pTmpSCache = m_pDatabase->m_pTransLogList; + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if (!pTmpSCache) + { + f_breakpoint( 1002); + } + } +#endif + + if (m_pPrevInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pPrevInHashBucket->unprotectCachedItem(); +#endif + m_pPrevInHashBucket->m_pNextInHashBucket = m_pNextInHashBucket; +#ifdef FLM_CACHE_PROTECT + m_pPrevInHashBucket->protectCachedItem(); +#endif + } + else + { + m_pDatabase->m_pTransLogList = m_pNextInHashBucket; + } + + if (m_pNextInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->unprotectCachedItem(); +#endif + m_pNextInHashBucket->m_pPrevInHashBucket = m_pPrevInHashBucket; +#ifdef FLM_CACHE_PROTECT + m_pNextInHashBucket->protectCachedItem(); +#endif + } + + m_pNextInHashBucket = NULL; + m_pPrevInHashBucket = NULL; +} + +/**************************************************************************** +Desc: The block pointed to by pSCache is about to be removed from from the + version list for a particular block address because it is no longer + needed. Before doing that, the previous block address should be + moved to the next newer version's block header so that it will not be + lost, but only if the next newer version's block header is not already + pointing to a prior version of the block. + This method assumes the block cache mutex is locked. +****************************************************************************/ +void F_CachedBlock::savePrevBlkAddress( void) +{ + FLMUINT uiPrevBlkAddress = getPriorImageAddress(); + F_CachedBlock * pNewerSCache; + FLMUINT uiNewerBlkPrevAddress; + + // NOTE: If a block is being read in from disk, it has to have a + // previous block address in its header. Otherwise, it could never + // have been written out to disk and removed from cache in the first + // place. This is obvious for versions being read from the rollback + // log - it would be impossible to retrieve them from the rollback + // log if they weren't already part of a version chain! It is also + // true for the most current version of a block. The most current + // version of a block can never be written out and removed from + // cache without having a pointer to the chain of older versions that + // may still be needed by read transactions - or to rollback the + // transaction - or to recover a checkpoint. + + if ((uiPrevBlkAddress) && + ((pNewerSCache = m_pPrevInVersionList) != NULL) && + (!(pNewerSCache->m_ui16Flags & CA_READ_PENDING))) + { + + // Only move the older version's previous block address to the + // newer version, if the newer version doesn't already have a + // previous block address. Also need to set the previous + // transaction ID. + // + // NOTE: The newer block may or may not be dirty. It is OK + // to change the prior version address in the header of a + // non-dirty block in this case. This is because the block + // may or may not be written out to the roll-back log. If it + // is, we want to make sure it has the correct prior version + // address. If it isn't ever written out to the log, it + // will eventually fall out of cache because it is no longer + // needed. + + uiNewerBlkPrevAddress = pNewerSCache->getPriorImageAddress(); + if (!uiNewerBlkPrevAddress) + { + + // Need to temporarily use the newer version of the block + // before changing its prior image block address. + + pNewerSCache->useForThread( 0); + flmAssert( uiPrevBlkAddress); + + pNewerSCache->m_pBlkHdr->ui32PriorBlkImgAddr = + (FLMUINT32)uiPrevBlkAddress; + + // Need to remove the newer block from the file log + // list, since it no longer needs to be logged + + if( pNewerSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + pNewerSCache->unlinkFromLogList(); + } + + pNewerSCache->releaseForThread(); + } + } +} + +/**************************************************************************** +Desc: See if we should force a checkpoint. +****************************************************************************/ +FINLINE FLMBOOL scaSeeIfForceCheckpoint( + FLMUINT uiCurrTime, + FLMUINT uiLastCheckpointTime, + CP_INFO * pCPInfo) +{ + if (FLM_ELAPSED_TIME( uiCurrTime, uiLastCheckpointTime) >= + gv_XFlmSysData.uiMaxCPInterval) + { + if (pCPInfo) + { + pCPInfo->bForcingCheckpoint = TRUE; + pCPInfo->iForceCheckpointReason = XFLM_CP_TIME_INTERVAL_REASON; + pCPInfo->uiForceCheckpointStartTime = (FLMUINT)FLM_GET_TIMER(); + } + + return( TRUE); + } + + return( FALSE); +} + +/**************************************************************************** +Desc: Allocate the array that keeps track of blocks written or logged. +****************************************************************************/ +RCODE F_Database::allocBlocksArray( + FLMUINT uiNewSize, + FLMBOOL bOneArray) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiOldSize = m_uiBlocksDoneArraySize; + + if (!uiNewSize) + { + uiNewSize = uiOldSize + 500; + } + + // Re-alloc the array + + if (RC_BAD( rc = f_realloc( + (FLMUINT)(uiNewSize * + (sizeof( F_CachedBlock *) + + sizeof( F_CachedBlock *))), + &m_ppBlocksDone))) + { + goto Exit; + } + + // Copy the old stuff into the two new areas of the new array. + + if (uiOldSize && !bOneArray) + { + f_memmove( &m_ppBlocksDone [uiNewSize], + &m_ppBlocksDone [uiOldSize], + uiOldSize * sizeof( F_CachedBlock *)); + } + + // Set the new array size + + m_uiBlocksDoneArraySize = uiNewSize; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Write out log blocks to the rollback log for a database. +****************************************************************************/ +RCODE F_Database::flushLogBlocks( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bIsCPThread, + FLMUINT uiMaxDirtyCache, + FLMBOOL * pbForceCheckpoint, + FLMBOOL * pbWroteAll) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLogEof; + XFLM_DB_HDR * pDbHdr; + F_CachedBlock * pTmpSCache; + F_CachedBlock * pLastBlockToLog; + F_CachedBlock * pFirstBlockToLog; + F_CachedBlock * pDirtySCache; + F_CachedBlock * pSavedSCache = NULL; + FLMUINT uiDirtyCacheLeft; + FLMUINT uiPrevBlkAddress; + FLMBOOL bMutexLocked = TRUE; + FLMBOOL bLoggedFirstBlk = FALSE; + FLMBOOL bLoggedFirstCPBlk = FALSE; + FLMUINT uiCurrTime; + FLMUINT uiSaveEOFAddr; + FLMUINT uiSaveFirstCPBlkAddr = 0; + FLMBOOL bDone = FALSE; + F_CachedBlock * pUsedSCache; + F_CachedBlock * pNextSCache = NULL; + F_CachedBlock ** ppUsedBlocks = (F_CachedBlock **)((m_ppBlocksDone) + ? &m_ppBlocksDone [m_uiBlocksDoneArraySize] + : (F_CachedBlock **)NULL); + FLMUINT uiTotalLoggedBlocks = 0; + FLMBOOL bForceCheckpoint = *pbForceCheckpoint; + FLMBOOL bDoAsync; +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + m_uiCurrLogWriteOffset = 0; + bDoAsync = (gv_XFlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + // Get the correct log header. If we are in an update transaction, + // need to use the uncommitted log header. Otherwise, use the last + // committed log header. + + pDbHdr = bIsCPThread + ? &m_lastCommittedDbHdr + : &m_uncommittedDbHdr; + + uiLogEof = (FLMUINT)pDbHdr->ui32RblEOF; + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + pDirtySCache = m_pFirstInLogList; + uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + flmAssert( m_pCurrLogBuffer == NULL); + + uiDirtyCacheLeft = (m_uiDirtyCacheCount + m_uiLogCacheCount) * + m_uiBlockSize; + + for (;;) + { + if( !pDirtySCache) + { + bDone = TRUE; + goto Write_Log_Blocks; + } + + flmAssert( pDirtySCache->m_ui16Flags & CA_DIRTY); + flmAssert( pDirtySCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); + + // See if we should give up our write lock. Will do so if we + // are not forcing a checkpoint and we have not exceeded the + // maximum time since the last checkpoint AND the dirty cache + // left is below the maximum. + + if (!bForceCheckpoint && bIsCPThread) + { + if (scaSeeIfForceCheckpoint( uiCurrTime, m_uiLastCheckpointTime, + m_pCPInfo)) + { + bForceCheckpoint = TRUE; + } + else + { + if (m_pWriteLockObj->ThreadWaitingLock() && + uiDirtyCacheLeft <= uiMaxDirtyCache) + { + bDone = TRUE; + *pbWroteAll = FALSE; + goto Write_Log_Blocks; + } + } + } + + uiPrevBlkAddress = pDirtySCache->getPriorImageAddress(); + if (uiPrevBlkAddress) + { + // We shouldn't find anything in the log list that has + // already been logged. However, if we do find something, + // we will deal with it rather than returning an error. + + flmAssert( 0); + pTmpSCache = pDirtySCache->m_pNextInReplaceList; + pDirtySCache->unlinkFromLogList(); + pDirtySCache = pTmpSCache; + continue; + } + + // The replace list pointers are used to maintain links + // between items in the file log list + + pTmpSCache = pDirtySCache->m_pNextInVersionList; + pLastBlockToLog = NULL; + pFirstBlockToLog = NULL; + + // Grab the next block in the chain and see if we are done. + // NOTE: pDirtySCache should not be accessed in the loop + // below, because it has been changed to point to the + // next cache block in the log list. If you need to access + // the current block, use pSavedSCache. + + pSavedSCache = pDirtySCache; + if ((pDirtySCache = pDirtySCache->m_pNextInReplaceList) == NULL) + { + bDone = TRUE; + } +#ifdef FLM_DEBUG + else + { + flmAssert( pDirtySCache->m_ui16Flags & CA_DIRTY); + flmAssert( pDirtySCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); + } +#endif + + // Traverse down the list of prior versions of the block until + // we hit one that has a prior version on disk. Throw out + // any not marked as CA_WRITE_TO_LOG, CA_LOG_FOR_CP, and + // not needed by a read transaction. + + while (pTmpSCache) + { + pNextSCache = pTmpSCache->m_pNextInVersionList; + FLMBOOL bWillLog; + + uiPrevBlkAddress = pTmpSCache->getPriorImageAddress(); + + // If we determine that we need to log a block, put a use on the + // newer version of the block to prevent other threads from verifying + // their checksums while we are writing the older versions to + // the log. This is because lgOutputBlock may modify information + // in the newer block's header area. + + if (pTmpSCache->m_ui16Flags & CA_READ_PENDING) + { + + // No need to go further down the list if this block is + // being read in. If it is being read in, every older + // version has a path to it - otherwise, it would never + // have been written out so that it would need to be + // read back in. + + break; + } + else if (pTmpSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + bWillLog = TRUE; + } + + // Even if the block is not needed by a read transaction, if it + // has a use count, we need to log it so that all blocks between + // pFirstBlockToLog and pLastBlockToLog are logged. This is + // necessary to ensure that previous block addresses carry all + // the way up the version chain. Also, the loop that does the + // actual logging below assumes that the links from pLastBlockToLog + // to pFirstBlockToLog will NOT be altered - even though the mutex + // is not locked. This can only be ensured if every block between + // the two points is guaranteed to be logged - which also guarantees + // that it will not be moved out of the list - because of the fact + // that some sort of logging bit has been set. + // Note that a block can have a use count even though it is no + // longer needed by a read transaction because another thread + // may have temporarily put a use on it while traversing down + // the chain - or for any number of other reasons. + + else if (pTmpSCache->neededByReadTrans() || + pTmpSCache->m_uiUseCount) + { + bWillLog = TRUE; + } + else + { + bWillLog = FALSE; + + // Since the block is no longer needed by a read transaction, + // and it is not in use, free it + + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + + // Add this block to the list of those we will be logging if the + // bWillLog flag got set above. + + if (bWillLog) + { + if (uiTotalLoggedBlocks >= m_uiBlocksDoneArraySize) + { + if (RC_BAD( rc = allocBlocksArray( 0, FALSE))) + { + goto Exit; + } + ppUsedBlocks = &m_ppBlocksDone [m_uiBlocksDoneArraySize]; + } + + pLastBlockToLog = pTmpSCache; + if (!pFirstBlockToLog) + { + pFirstBlockToLog = pLastBlockToLog; + } + + pTmpSCache->m_pPrevInVersionList->useForThread( 0); + pTmpSCache->useForThread( 0); + m_ppBlocksDone [uiTotalLoggedBlocks] = pTmpSCache; + ppUsedBlocks [uiTotalLoggedBlocks] = pTmpSCache->m_pPrevInVersionList; + uiTotalLoggedBlocks++; + } + + // No need to go further down the list if this block has + // has a previous block address. + + if (uiPrevBlkAddress) + { + break; + } + pTmpSCache = pNextSCache; + } + +#ifdef FLM_DEBUG + while (pNextSCache) + { + flmAssert( !(pNextSCache->m_ui16Flags & + (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))); + pNextSCache = pNextSCache->m_pNextInVersionList; + + } +#endif + + // If nothing to log for the block, unlink it from the + // log list. We check CA_IN_FILE_LOG_LIST again, because + // savePrevBlkAddress may have been called during an + // unlink above. savePrevBlkAddress will remove + // the dirty cache block from the log list if it determines + // that there is no need to log prior versions + + if (!pLastBlockToLog) + { + if (pSavedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + pSavedSCache->unlinkFromLogList(); + } + continue; + } + + // Don't want the mutex locked while we do the I/O + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + // Write the log blocks to the rollback log. + // Do all of the blocks from oldest to most current. Stop when we + // hit the first log block. + + while (pLastBlockToLog) + { + FLMUINT uiLogPos = uiLogEof; + + if (RC_BAD( rc = lgOutputBlock( pDbStats, pSFileHdl, + pLastBlockToLog, + pLastBlockToLog->m_pPrevInVersionList->m_pBlkHdr, + bDoAsync, &uiLogEof))) + { + goto Exit; + } + + if (pLastBlockToLog->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( uiDirtyCacheLeft >= m_uiBlockSize); + uiDirtyCacheLeft -= m_uiBlockSize; + } + + // If we are logging a block for the current update + // transaction, and this is the first block we have logged, + // remember the block address where we logged it. + + if ((pLastBlockToLog->m_ui16Flags & CA_WRITE_TO_LOG) && + !m_uiFirstLogBlkAddress) + { + // This better not EVER happen in the CP thread. + + flmAssert( !bIsCPThread); + bLoggedFirstBlk = TRUE; + m_uiFirstLogBlkAddress = uiLogPos; + } + + // If we are logging the checkpoint version of the + // block, and this is the first block we have logged + // since the last checkpoint, remember its position so + // that we can write it out to the log header when we + // complete the checkpoint. + + if ((pLastBlockToLog->m_ui16Flags & CA_LOG_FOR_CP) && + !m_uiFirstLogCPBlkAddress) + { + bLoggedFirstCPBlk = TRUE; + m_uiFirstLogCPBlkAddress = uiLogPos; + } + + // Break when we hit the first log block. + + if (pLastBlockToLog == pFirstBlockToLog) + { + break; + } + + pLastBlockToLog = pLastBlockToLog->m_pPrevInVersionList; + } + + // If we have logged some blocks, force the log header to be + // updated on one of the following conditions: + + // 1. We have logged over 2000 blocks. We do this to keep + // our array of logged blocks from growing too big. + // 2. We are done logging. + +Write_Log_Blocks: + + if (uiTotalLoggedBlocks && // Must be at least one logged block + (uiTotalLoggedBlocks >= 2000 || bDone)) + { + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Flush the last log buffer, if not already flushed. + + if (m_uiCurrLogWriteOffset) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + bDoAsync))) + { + goto Exit; + } + } + + // If doing async, wait for pending writes to complete before writing + // the log header. + + if (bDoAsync) + { + if (RC_BAD( rc = m_pBufferMgr->waitForAllPendingIO())) + { + goto Exit; + } + } + + // Must wait for all RFL writes before writing out log header. + + if (!bIsCPThread) + { + (void)m_pRfl->seeIfRflWritesDone( hWaitSem, TRUE); + } + + // Save the EOF address so we can restore it if + // the write fails. + + uiSaveEOFAddr = (FLMUINT)pDbHdr->ui32RblEOF; + pDbHdr->ui32RblEOF = (FLMUINT32)uiLogEof; + + if (bLoggedFirstCPBlk) + { + uiSaveFirstCPBlkAddr = pDbHdr->ui32RblFirstCPBlkAddr; + pDbHdr->ui32RblFirstCPBlkAddr = + (FLMUINT32)m_uiFirstLogCPBlkAddress; + } + + if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, + pDbHdr, &m_checkpointDbHdr, FALSE))) + { + + // If the write of the log header fails, + // we want to restore the log header to what it was before + // because we always use the log header from memory instead + // of reading it from disk. The one on disk is only + // current for many fields as of the last checkpoint. + + pDbHdr->ui32RblEOF = (FLMUINT32)uiSaveEOFAddr; + if (bLoggedFirstCPBlk) + { + pDbHdr->ui32RblFirstCPBlkAddr = (FLMUINT32)uiSaveFirstCPBlkAddr; + } + goto Exit; + } + + // Need to update the committed log header when we are operating in + // an uncommitted transaction so that if the transaction turns out + // to be empty, we will have the correct values in the committed + // log header for subsequent transactions or the checkpoint thread + // itself. + + if (!bIsCPThread) + { + m_lastCommittedDbHdr.ui32RblEOF = pDbHdr->ui32RblEOF; + + if (bLoggedFirstCPBlk) + { + m_lastCommittedDbHdr.ui32RblFirstCPBlkAddr = + pDbHdr->ui32RblFirstCPBlkAddr; + } + } + + // Once the write is safe, we can reset things to start over. + + bLoggedFirstBlk = FALSE; + bLoggedFirstCPBlk = FALSE; + + // Clean up the log blocks array - releasing blocks, etc. + + if (m_pCPInfo) + { + lockMutex(); + m_pCPInfo->uiLogBlocksWritten += uiTotalLoggedBlocks; + unlockMutex(); + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + + while (uiTotalLoggedBlocks) + { + uiTotalLoggedBlocks--; + pTmpSCache = m_ppBlocksDone [uiTotalLoggedBlocks]; +#ifdef FLM_DBG_LOG + ui16OldFlags = pTmpSCache->m_ui16Flags; +#endif + pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; + + // Newer block should be released, whether we succeeded + // or not - because it will always have been used. + + pUsedSCache->releaseForThread(); + pTmpSCache->releaseForThread(); + + // The current version of the block may have already been removed from + // the file log list if more than one block in the version chain + // needed to be logged. If the block is still in the file log list, + // it will be removed. Otherwise, the prior image address better + // be a non-zero value. + + if( pUsedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + pUsedSCache->unlinkFromLogList(); + } + + flmAssert( pUsedSCache->getPriorImageAddress()); + + // Unlink from list of transaction log blocks + + if (pTmpSCache->m_ui16Flags & CA_WRITE_TO_LOG) + { + pTmpSCache->unlinkFromTransLogList(); + } + + // Unset logging flags on logged block. + + if (pTmpSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( m_uiLogCacheCount); + m_uiLogCacheCount--; + } + + pTmpSCache->clearFlags( CA_LOG_FOR_CP | CA_WRITE_TO_LOG | CA_WAS_DIRTY); + +#ifdef FLM_DBG_LOG + pTmpSCache->logFlgChange( ui16OldFlags, 'D'); +#endif + + if (!pTmpSCache->m_uiUseCount && + !pTmpSCache->m_ui16Flags && + !pTmpSCache->neededByReadTrans()) + { + flmAssert( pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0)); + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + } + + uiDirtyCacheLeft = + (m_uiDirtyCacheCount + m_uiLogCacheCount) * + m_uiBlockSize; + + // When the current set of log blocks were flushed, they were + // also unlinked from the file log list. So, we need to + // start at the beginning of the log list to pick up + // where we left off. + + pDirtySCache = m_pFirstInLogList; + } + else if (!bDone) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + // Need to reset pDirtySCache here because the background cache + // cleanup thread may have unlinked it from the log list and + // cleaned up any prior versions if it determined that the blocks + // were no longer needed. + + if( (pDirtySCache = pSavedSCache->m_pNextInReplaceList) == NULL) + { + bDone = TRUE; + goto Write_Log_Blocks; + } + } + + if (bDone) + { + break; + } + + flmAssert( bMutexLocked); + } + +#ifdef FLM_DEBUG + if( bForceCheckpoint || !bIsCPThread || + (!bForceCheckpoint && bIsCPThread && *pbWroteAll)) + { + flmAssert( !m_uiLogListCount); + flmAssert( !m_uiLogCacheCount); + } +#endif + +Exit: + + if (RC_BAD( rc)) + { + // Flush the last log buffer, if not already flushed. + + if (m_uiCurrLogWriteOffset) + { + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Don't care what rc is at this point. Just calling + // lgFlushLogBuffer to clear the buffer. + + (void)lgFlushLogBuffer( pDbStats, pSFileHdl, bDoAsync); + } + + // Need to wait for any async writes to complete. + + if (bDoAsync) + { + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Don't care about rc here, but we don't want to leave + // this routine until all pending IO is taken care of. + + (void)m_pBufferMgr->waitForAllPendingIO(); + } + + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + // Clean up the log blocks array - releasing blocks, etc. + + while (uiTotalLoggedBlocks) + { + uiTotalLoggedBlocks--; + pTmpSCache = m_ppBlocksDone [uiTotalLoggedBlocks]; + pUsedSCache = ppUsedBlocks [uiTotalLoggedBlocks]; + +#ifdef FLM_DEBUG + + // If this is the most current version of the block, it + // should still be in the file log list. + + if( !pUsedSCache->m_pPrevInVersionList) + { + flmAssert( pUsedSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST); + } +#endif + + // Used blocks should be released, whether we succeeded + // or not. + + pUsedSCache->releaseForThread(); + pTmpSCache->releaseForThread(); + + // If we quit before logging the blocks, we don't really + // want to change anything on the block, but we do want + // to set the previous block address back to zero on the + // block that is just newer than this one. + + // Must put a USE on the block so that the memory cache + // verifying code will not barf when we change the + // data in the block - checksum is calculated and set when + // the use count goes from one to zero, and then verified + // when it goes from zero to one. + + pTmpSCache->m_pPrevInVersionList->useForThread( 0); + pTmpSCache->m_pPrevInVersionList->m_pBlkHdr->ui32PriorBlkImgAddr = 0; + pTmpSCache->m_pPrevInVersionList->releaseForThread(); + } + +#ifdef SCACHE_LINK_CHECKING + + // If above logic changes where mutex might not be locked at + // this point, be sure to modify this code to re-lock it. + + flmAssert( bMutexLocked); + scaVerify( 100); +#endif + + // Things to restore to their original state if we had an error. + + if (bLoggedFirstBlk) + { + m_uiFirstLogBlkAddress = 0; + } + if (bLoggedFirstCPBlk) + { + m_uiFirstLogCPBlkAddress = 0; + } + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Better not be any incomplete writes at this point. + + flmAssert( !m_pBufferMgr->havePendingIO()); + flmAssert( m_pCurrLogBuffer == NULL); + + *pbForceCheckpoint = bForceCheckpoint; + return( rc); +} + +/**************************************************************************** +Desc: This routine is called whenever a write of a dirty block completes. +****************************************************************************/ +FSTATIC void scaWriteComplete( + F_IOBuffer * pIOBuffer) +{ + RCODE rc = pIOBuffer->getCompletionCode(); + FLMUINT uiNumBlocks = pIOBuffer->getBufferSize() / + pIOBuffer->getBlockSize(); + F_CachedBlock * pSCache; + F_Database * pDatabase; + XFLM_DB_STATS * pDbStats = pIOBuffer->getDbStats(); + FLMUINT uiMilliPerBlock = 0; + FLMUINT uiExtraMilli = 0; + +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + if (pDbStats) + { + FLMUINT64 ui64ElapMilli = pIOBuffer->getElapTime(); + + uiMilliPerBlock = (FLMUINT)(ui64ElapMilli / (FLMUINT64)uiNumBlocks); + uiExtraMilli = (FLMUINT)(ui64ElapMilli % (FLMUINT64)uiNumBlocks); + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + while (uiNumBlocks) + { + uiNumBlocks--; + pSCache = (F_CachedBlock *)pIOBuffer->getCompletionCallbackData( uiNumBlocks); + pDatabase = pSCache->getDatabase(); + + if (pDbStats) + { + F_BLK_HDR * pBlkHdr = pSCache->getBlockPtr(); + XFLM_LFILE_STATS * pLFileStats; + XFLM_BLOCKIO_STATS * pBlockIOStats; + + if (!blkIsBTree( pBlkHdr)) + { + pLFileStats = NULL; + } + else + { + if (RC_BAD( flmStatGetLFile( pDbStats, + (FLMUINT)((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile, + getBlkLfType( (F_BTREE_BLK_HDR *)pBlkHdr), + 0, &pLFileStats, NULL, NULL))) + { + pLFileStats = NULL; + } + } + if ((pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, + pLFileStats, (FLMBYTE *)pBlkHdr)) != NULL) + { + pBlockIOStats->BlockWrites.ui64Count++; + pBlockIOStats->BlockWrites.ui64TotalBytes += + pDatabase->getBlockSize(); + if (uiExtraMilli) + { + pBlockIOStats->BlockWrites.ui64ElapMilli += + (uiMilliPerBlock + 1); + uiExtraMilli--; + } + else + { + pBlockIOStats->BlockWrites.ui64ElapMilli += + uiMilliPerBlock; + } + } + } + + pSCache->releaseForThread(); + if (pSCache->getModeFlags() & CA_DIRTY) + { + flmAssert( pSCache->getModeFlags() & CA_WRITE_PENDING); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->getModeFlags(); +#endif + pSCache->clearFlags( CA_WRITE_PENDING); + if (RC_OK( rc)) + { + pSCache->unsetDirtyFlag(); + } + +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'H'); +#endif + + // If there are more dirty blocks after this + // one, move this one out of the dirty + // blocks. + + pSCache->unlinkFromDatabase(); + pSCache->linkToDatabase( pDatabase); + } + else + { + flmAssert( !(pSCache->getModeFlags() & CA_WRITE_PENDING)); + } + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BlockCacheMgr::cleanupLRUCache( void) +{ + FLMUINT uiByteThreshold; + FLMUINT uiSlabThreshold; + FLMUINT uiSlabSize; + F_CachedBlock * pPrevSCache; + F_CachedBlock * pTmpSCache; + FLMBOOL bDefragNeeded = FALSE; + + // Remove non-dirty blocks from the LRU end of the cache + + uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; + uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); + + // If the cache isn't over its slab threshold, we are done + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + + uiByteThreshold = m_Usage.uiByteCount > uiSlabSize + ? m_Usage.uiByteCount - uiSlabSize + : 0; + + pTmpSCache = (F_CachedBlock *)m_MRUList.m_pLRUItem; + while( pTmpSCache) + { + // Save the pointer to the previous entry in the list because + // we may end up unlinking pTmpSCache below, in which case we would + // have lost the next entry. + + pPrevSCache = (F_CachedBlock *)pTmpSCache->m_pPrevInGlobal; + + // Block must not currently be in use, cannot be dirty in any way, + // cannot be in the process of being read in from disk, + // and must not be needed by a read transaction. + + if( !pTmpSCache->m_uiUseCount && !pTmpSCache->m_ui16Flags && + (!pTmpSCache->m_pDatabase || !pTmpSCache->neededByReadTrans())) + { + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + bDefragNeeded = TRUE; + + if( m_Usage.uiByteCount <= uiByteThreshold) + { + if( pPrevSCache) + { + pPrevSCache->useForThread( 0); + } + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + bDefragNeeded = FALSE; + + if( !pPrevSCache) + { + break; + } + + pPrevSCache->releaseForThread(); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + + uiByteThreshold = m_Usage.uiByteCount > uiSlabSize + ? m_Usage.uiByteCount - uiSlabSize + : 0; + + } + } + + pTmpSCache = pPrevSCache; + } + + if( bDefragNeeded) + { + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Cleanup old blocks in cache that are no longer needed by any + transaction. This routine assumes that the block cache mutex has + been locked. +****************************************************************************/ +void F_BlockCacheMgr::cleanupReplaceList( void) +{ + F_CachedBlock * pTmpSCache; + F_CachedBlock * pPrevSCache; + + pTmpSCache = m_pLRUReplace; + + for (;;) + { + // Stop when we reach end of list or all old blocks have + // been freed. + + if (!pTmpSCache || !m_Usage.uiOldVerBytes) + { + break; + } + + // Shouldn't encounter anything with CA_FREE set + + flmAssert( !(pTmpSCache->m_ui16Flags & CA_FREE)); + + // Save the pointer to the previous entry in the list because + // we may end up unlinking pTmpSCache below, in which case we would + // have lost the next entry. + + pPrevSCache = pTmpSCache->m_pPrevInReplaceList; + + // Block must not currently be in use, + // Must not be the most current version of a block, + // Cannot be dirty in any way, + // Cannot be in the process of being read in from disk, + // And must not be needed by a read transaction. + + if (!pTmpSCache->m_uiUseCount && + pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0) && + !pTmpSCache->m_ui16Flags && + (!pTmpSCache->m_pDatabase || + !pTmpSCache->neededByReadTrans())) + { + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + pTmpSCache = pPrevSCache; + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_BlockCacheMgr::cleanupFreeCache( void) +{ + F_CachedBlock * pSCache = m_pLastFree; + F_CachedBlock * pPrevSCache; + + while( pSCache) + { + pPrevSCache = pSCache->m_pPrevInDatabase; + if( !pSCache->m_uiUseCount) + { + pSCache->unlinkFromFreeList(); + delete pSCache; + } + pSCache = pPrevSCache; + } +} + +/**************************************************************************** +Desc: This routine will reduce the number of blocks in the reuse list + until cache is below its limit. + NOTE: This routine assumes that the block cache mutex is already locked. +****************************************************************************/ +void F_BlockCacheMgr::reduceReuseList( void) +{ + F_CachedBlock * pTmpSCache; + F_CachedBlock * pPrevSCache; + FLMUINT uiSlabThreshold; + FLMUINT uiSlabSize; + FLMUINT uiByteThreshold; + + // Determine if the block limit for block cache been exceeded + + uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) + { + goto Exit; + } + + // Remove items from cache starting from the LRU + + pTmpSCache = m_pLRUReplace; + uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); + uiByteThreshold = m_Usage.uiByteCount > uiSlabSize + ? m_Usage.uiByteCount - uiSlabSize + : 0; + + while( pTmpSCache) + { + // Need to save the pointer to the previous entry in the list because + // we may end up freeing pTmpNode below. + + pPrevSCache = pTmpSCache->m_pPrevInReplaceList; + + // See if the item can be freed. + + if( pTmpSCache->canBeFreed()) + { + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + + if( m_Usage.uiByteCount <= uiByteThreshold) + { + if( pPrevSCache) + { + pPrevSCache->useForThread( 0); + } + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + if( !pPrevSCache) + { + break; + } + + pPrevSCache->releaseForThread(); + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) + { + goto Exit; + } + + uiByteThreshold = m_Usage.uiByteCount > uiSlabSize + ? m_Usage.uiByteCount - uiSlabSize + : 0; + } + } + + pTmpSCache = pPrevSCache; + } + +Exit: + + return; +} + +/**************************************************************************** +Desc: Reduce cache to below the cache limit. NOTE: This routine assumes + that the block cache mutex is locked. It may temporarily unlock the mutex + to write out dirty blocks, but it will always return with the mutex + still locked. +****************************************************************************/ +RCODE F_BlockCacheMgr::reduceCache( + F_Db * pDb) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb ? pDb->m_pDatabase : NULL; + FLMBOOL bForceCheckpoint; + FLMBOOL bWroteAll; + FLMUINT uiSlabSize; + FLMUINT uiSlabThreshold; + FLMBOOL bDoingReduce = FALSE; + + // If cache is not full, we are done. + + if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit() || m_bReduceInProgress) + { + goto Exit; + } + + m_bReduceInProgress = TRUE; + bDoingReduce = TRUE; + + // Determine the cache threshold + + uiSlabThreshold = gv_XFlmSysData.pGlobalCacheMgr->m_uiMaxSlabs >> 1; + uiSlabSize = gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager->getSlabSize(); + + // Are we over the threshold? + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold) + { + goto Exit; + } + + // Try cleaning up the replace list + + if( m_Usage.uiOldVerBytes) + { + cleanupReplaceList(); + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + } + + // Clean up the free list + + if( m_uiFreeBytes) + { + cleanupFreeCache(); + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + } + + // Clean up the LRU list + + cleanupLRUCache(); + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + + // If this isn't an update transaction, there isn't anything else + // that can be done to reduce cache. + + if( !pDb || + (pDb->m_eTransType != XFLM_UPDATE_TRANS && !pDatabase->m_bTempDb)) + { + goto Exit; + } + + // Flush log blocks + + if( pDatabase->m_pFirstInLogList) + { + bForceCheckpoint = FALSE; + bWroteAll = TRUE; + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + if( RC_BAD( rc = pDatabase->flushLogBlocks( + pDb->m_hWaitSem, pDb->m_pDbStats, pDb->m_pSFileHdl, FALSE, 0, + &bForceCheckpoint, &bWroteAll))) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + cleanupFreeCache(); + reduceReuseList(); + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + } + + // Flush new blocks + + for( ;;) + { + FLMUINT uiNewBlocks = 0; + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + if( RC_BAD( rc = pDatabase->reduceNewBlocks( + pDb->m_pDbStats, pDb->m_pSFileHdl, &uiNewBlocks))) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + goto Exit; + } + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + if( !uiNewBlocks) + { + break; + } + + cleanupFreeCache(); + reduceReuseList(); + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + } + + // Flush dirty blocks + + flmAssert( !pDatabase->m_pFirstInLogList); + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + if( RC_BAD( rc = pDatabase->reduceDirtyCache( + pDb->m_pDbStats, pDb->m_pSFileHdl))) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + goto Exit; + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + cleanupFreeCache(); + reduceReuseList(); + + gv_XFlmSysData.pBlockCacheMgr->defragmentMemory( TRUE); + + // We're going to quit when we get under 50 percent for block cache + // or we aren't over the global limit. Note that this means we + // may quit reducing before we get under the global limit. We + // don't want to get into a situation where we are starving block + // cache because node cache is over its limit. + + if( m_Usage.slabUsage.ui64Slabs <= uiSlabThreshold || + !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + goto Exit; + } + + // Try cleaning up the LRU again + + cleanupLRUCache(); + +Exit: + + if( RC_BAD( rc) && pDb) + { + pDb->setMustAbortTrans( rc); + } + + if( bDoingReduce) + { + m_bReduceInProgress = FALSE; + } + + return( rc); +} + +/**************************************************************************** +Desc: Constructor for cached block. +****************************************************************************/ +F_CachedBlock::F_CachedBlock( + FLMUINT uiBlockSize) +{ + m_pPrevInDatabase = NULL; + m_pNextInDatabase = NULL; + m_pBlkHdr = (F_BLK_HDR *)((FLMBYTE *)this + sizeof( F_CachedBlock)); + m_pDatabase = NULL; + m_uiBlkAddress = 0; + m_pNextInReplaceList = NULL; + m_pPrevInReplaceList = NULL; + m_pPrevInHashBucket = NULL; + m_pNextInHashBucket = NULL; + m_pPrevInVersionList = NULL; + m_pNextInVersionList = NULL; + m_pNotifyList = NULL; + + // Need to set high transaction ID to 0xFFFFFFFF. This indicates that + // the block is not currently counted in the Usage.uiOldVerBytes tally - + // seeing as how it was just allocated. + // DO NOT USE setTransID routine here because that routine + // will adjust the tally. The caller of this routine should call + // setTransID to ensure that the tally is set appropriately. + // This is the only place in the code where it is legal to set + // ui64HighTransID without calling setTransID. + + m_ui64HighTransID = ~((FLMUINT64)0); + m_uiUseCount = 0; + m_ui16Flags = 0; + m_ui16BlkSize = (FLMUINT16)uiBlockSize; + +#ifdef FLM_DEBUG + m_uiChecksum = 0; + m_pUseList = NULL; +#endif + +#ifdef FLM_CACHE_PROTECT + protectCachedItem(); +#endif +} + +/**************************************************************************** +Desc: Allocate a cache block. If we are at the cache limit, unused cache + blocks will be replaced. NOTE: This routine assumes that the block + cache mutex is locked. +****************************************************************************/ +RCODE F_BlockCacheMgr::allocBlock( + F_Db * pDb, + F_CachedBlock ** ppSCacheRV) +{ + RCODE rc = NE_XFLM_OK; + F_Database * pDatabase = pDb->getDatabase(); + FLMUINT uiBlockSize = pDatabase->getBlockSize(); + F_CachedBlock * pSCache; + F_CachedBlock * pTmpSCache; + F_CachedBlock * pPrevSCache; + + // Quick check to see if there is a block in the free list that can be + // re-used. Start at the MRU end of the list so that if items in the + // free list are only being used periodically, the items at the LRU end + // will age out and the size of the list will be reduced. + + pSCache = m_pFirstFree; + while (pSCache) + { + if (!pSCache->m_uiUseCount && + pSCache->getBlkSize() == uiBlockSize) + { + pSCache->unlinkFromFreeList(); + goto Reuse_Block; + } + pSCache = pSCache->m_pNextInDatabase; + } + + // The intent of this little loop is to be optimistic and hope that + // there is a block we can cannibalize or free without having to write + // it. If not, we will still allocate a new block and allow ourselves + // to be temporarily over the cache limit. In this case, the cache size + // will be reduced only AFTER this new block is safely linked into cache. + // This is necessary because we don't want two different threads allocating + // memory for the same block. + + pTmpSCache = m_pLRUReplace; + while( pTmpSCache && gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + // Need to save the pointer to the previous entry in the list because + // we may end up unlinking it below, in which case we would have lost + // the previous entry. + + pPrevSCache = pTmpSCache->m_pPrevInReplaceList; + + // See if the cache block can be replaced or freed. + + flmAssert( !pTmpSCache->m_ui16Flags); + if (pTmpSCache->canBeFreed()) + { + if (pTmpSCache->getBlkSize() == uiBlockSize) + { + pSCache = pTmpSCache; + flmAssert( !pSCache->m_ui16Flags); + pTmpSCache->unlinkCache( FALSE, NE_XFLM_OK); + + // We use a goto instead of a break because then + // we don't have to do the additional test + // down below. We already know that pSCache + // will be non-NULL. + + goto Reuse_Block; + } + else + { + // NOTE: This call will free the memory pointed to by + // pTmpSCache. Hence, pTmpSCache should NOT be used after + // this point. + + pTmpSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + } + pTmpSCache = pPrevSCache; + } + + // If we were not able to cannibalize an F_CachedBlock object, + // allocate one. + + if (pSCache) + { +Reuse_Block: + + flmAssert( !pSCache->m_pPrevInReplaceList); + flmAssert( !pSCache->m_pNextInReplaceList); + flmAssert( !pSCache->m_ui16Flags); + flmAssert( !pSCache->m_uiUseCount); + + // If block is an old version, need to decrement the + // Usage.uiOldVerBytes tally. + + if (pSCache->m_ui64HighTransID != ~((FLMUINT64)0)) + { + FLMUINT uiSize = pSCache->memSize(); + flmAssert( m_Usage.uiOldVerBytes >= uiSize); + m_Usage.uiOldVerBytes -= uiSize; + flmAssert( m_Usage.uiOldVerCount); + m_Usage.uiOldVerCount--; + } + + // If we are cannibalizing, be sure to reset certain fields. + + pSCache->m_ui16Flags = 0; + pSCache->m_uiUseCount = 0; +#ifdef FLM_DEBUG + pSCache->m_uiChecksum = 0; +#endif + + // Need to set high transaction ID to 0xFFFFFFFF. This indicates that + // the block is not currently counted in the Usage.uiOldVerBytes tally - + // seeing as how it was just allocated. + // DO NOT USE setTransID routine here because that routine + // will adjust the tally. The caller of this routine should call + // setTransID to ensure that the tally is set appropriately. + // This is the only place in the code where it is legal to set + // ui64HighTransID without calling setTransID. + + pSCache->m_ui64HighTransID = ~((FLMUINT64)0); + } + else + { + if ((pSCache = new( uiBlockSize) F_CachedBlock( uiBlockSize)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + m_Usage.uiCount++; + m_Usage.uiByteCount += pSCache->memSize(); + + if (shouldRehash( m_Usage.uiCount, m_uiNumBuckets)) + { + if (checkHashFailTime( &m_uiHashFailTime)) + { + if (RC_BAD( rc = rehash())) + { + goto Exit; + } + } + } + } + + *ppSCacheRV = pSCache; + + // Set use count to one so the block cannot be replaced. + + pSCache->useForThread( 0); + +Exit: + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/******************************************************************** +Desc: This converts a block header to native format. +*********************************************************************/ +void convertBlkHdr( + F_BLK_HDR * pBlkHdr + ) +{ + + // This routine should only be called on blocks that are NOT + // currently in native format. + + flmAssert( blkIsNonNativeFormat( pBlkHdr)); + + convert32( &pBlkHdr->ui32BlkAddr); + convert32( &pBlkHdr->ui32PrevBlkInChain); + convert32( &pBlkHdr->ui32NextBlkInChain); + convert32( &pBlkHdr->ui32PriorBlkImgAddr); + convert64( &pBlkHdr->ui64TransID); + convert32( &pBlkHdr->ui32BlkCRC); + convert16( &pBlkHdr->ui16BlkBytesAvail); + if (blkIsBTree( pBlkHdr)) + { + convert16( &(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16LogicalFile)); + convert16( &(((F_BTREE_BLK_HDR *)pBlkHdr)->ui16NumKeys)); + } + blkSetNativeFormat( pBlkHdr); +} + +/******************************************************************** +Desc: This converts a logical file header structure +*********************************************************************/ +void convertLfHdr( + F_LF_HDR * pLfHdr) +{ + convert32( &pLfHdr->ui32LfNumber); + convert32( &pLfHdr->ui32LfType); + convert32( &pLfHdr->ui32RootBlkAddr); + convert32( &pLfHdr->ui32EncId); + convert64( &pLfHdr->ui64NextNodeId); + convert64( &pLfHdr->ui64FirstDocId); + convert64( &pLfHdr->ui64LastDocId); +} + +/******************************************************************** +Desc: This converts a block header to native format. +*********************************************************************/ +void convertBlk( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr) +{ + // This routine should only be called on blocks that are NOT + // currently in native format. + + convertBlkHdr( pBlkHdr); + if (pBlkHdr->ui8BlkType == BT_LFH_BLK) + { + FLMUINT uiPos = SIZEOF_STD_BLK_HDR; + FLMUINT uiEnd = blkGetEnd( uiBlockSize, SIZEOF_STD_BLK_HDR, + pBlkHdr); + F_LF_HDR * pLfHdr = (F_LF_HDR *)((FLMBYTE *)pBlkHdr + + SIZEOF_STD_BLK_HDR); + + // Only one block type requires further conversion. + + while (uiPos + sizeof( F_LF_HDR) <= uiEnd) + { + convertLfHdr( pLfHdr); + pLfHdr++; + uiPos += sizeof( F_LF_HDR); + } + } +} + +/******************************************************************** +Desc: This routine prepares a block for use after reading it in from + disk. It will convert the block to native format if necessary, + and will also verify the CRC on the block. We always want to + convert the block if we can, so even if the CRC is bad or the + block end is bad, we will attempt to do a convert if it is in + non-native format. +*********************************************************************/ +RCODE flmPrepareBlockForUse( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT32 ui32CRC; + FLMUINT16 ui16BlkBytesAvail; + FLMUINT uiBlkEnd; + FLMBOOL bBadBlkEnd; + + // Determine if we should convert the block here. + // Calculation of CRC should be on unconverted block. + + ui16BlkBytesAvail = pBlkHdr->ui16BlkBytesAvail; + if (blkIsNonNativeFormat( pBlkHdr)) + { + convert16( &ui16BlkBytesAvail); + } + + if( (FLMUINT)ui16BlkBytesAvail > uiBlockSize - blkHdrSize( pBlkHdr)) + { + uiBlkEnd = blkHdrSize( pBlkHdr); + bBadBlkEnd = TRUE; + } + else + { + uiBlkEnd = (blkIsNewBTree( pBlkHdr) + ? uiBlockSize + : uiBlockSize - (FLMUINT)ui16BlkBytesAvail); + bBadBlkEnd = FALSE; + } + + // CRC must be calculated BEFORE converting the block. + + ui32CRC = calcBlkCRC( pBlkHdr, uiBlkEnd); + + if( blkIsNonNativeFormat( pBlkHdr)) + { + convertBlk( uiBlockSize, pBlkHdr); + } + + if( ui32CRC != pBlkHdr->ui32BlkCRC || bBadBlkEnd) + { + rc = RC_SET( NE_XFLM_BLOCK_CRC); + goto Exit; + } + +Exit: + + return( rc); +} + +/******************************************************************** +Desc: This routine attempts to read a block from disk. It will + attempt the specified number of times. +*********************************************************************/ +RCODE F_Database::readTheBlock( + F_Db * pDb, + TMP_READ_STATS * pTmpReadStats, // READ statistics. + F_BLK_HDR * pBlkHdr, // Pointer to buffer where block is + // to be read into. + FLMUINT uiFilePos, // File position to be read from. If + // file position != block address, we + // are reading from the log. + FLMUINT uiBlkAddress // Block address that is to be read. + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesRead; + F_TMSTAMP StartTime; + FLMUINT64 ui64ElapMilli; + XFLM_DB_STATS * pDbStats = pDb->m_pDbStats; + + flmAssert( this == pDb->m_pDatabase); + + // We should NEVER be attempting to read a block address that is + // beyond the current logical end of file. + + if (!FSAddrIsBelow( uiBlkAddress, pDb->m_uiLogicalEOF)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Read the block + + if (pDb->m_uiKilledTime) + { + rc = RC_SET( NE_XFLM_OLD_VIEW); + goto Exit; + } + + if (pTmpReadStats) + { + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->OldViewBlockReads.ui64Count++; + pTmpReadStats->OldViewBlockReads.ui64TotalBytes += + m_uiBlockSize; + } + else + { + pTmpReadStats->BlockReads.ui64Count++; + pTmpReadStats->BlockReads.ui64TotalBytes += + m_uiBlockSize; + } + ui64ElapMilli = 0; + f_timeGetTimeStamp( &StartTime); + } + + if (RC_BAD( rc = pDb->m_pSFileHdl->ReadBlock( uiFilePos, + m_uiBlockSize, pBlkHdr, &uiBytesRead))) + { + if (pDbStats) + { + pDbStats->uiReadErrors++; + } + + if (rc == NE_XFLM_IO_END_OF_FILE) + { + + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); + } + goto Exit; + } + + if (pTmpReadStats) + { + flmAddElapTime( &StartTime, &ui64ElapMilli); + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->OldViewBlockReads.ui64ElapMilli += ui64ElapMilli; + } + else + { + pTmpReadStats->BlockReads.ui64ElapMilli += ui64ElapMilli; + } + } + + if (uiBytesRead < m_uiBlockSize) + { + + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); +#ifdef FLM_DBG_LOG + // Must make this call so we can be ensured that the + // transaction ID in the block header has been + // converted if need be. + (void)flmPrepareBlockForUse( m_uiBlockSize, pBlkHdr); +#endif + } + else + { + rc = flmPrepareBlockForUse( m_uiBlockSize, pBlkHdr); + } + + // Decrypt the block if it was encrypted + + if (RC_BAD( rc = decryptBlock( pDb->m_pDict, (FLMBYTE *)pBlkHdr))) + { + goto Exit; + } + +#ifdef FLM_DBG_LOG + if (uiFilePos != uiBlkAddress) + { + flmDbgLogWrite( this, uiBlkAddress, uiFilePos, + (FLMUINT)pBlkHdr->ui64TransID, "LGRD"); + } + else + { + flmDbgLogWrite( this, uiBlkAddress, 0, + pBlkHdr->ui64TransID, "READ"); + } +#endif + + if (RC_BAD( rc)) + { + if (pTmpReadStats && + (rc == NE_XFLM_BLOCK_CRC || rc == NE_XFLM_OLD_VIEW)) + { + if (uiFilePos != uiBlkAddress) + { + pTmpReadStats->uiOldViewBlockChkErrs++; + } + else + { + pTmpReadStats->uiBlockChkErrs++; + } + } + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Read a data block into cache. This routine reads the requested + version of a block into memory. It follows links to previous + versions of the block if necessary in order to do this. +****************************************************************************/ +RCODE F_Database::readBlock( + F_Db * pDb, + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiFilePos, // File position where we are to + // start reading from. + FLMUINT uiBlkAddress, // Address of block that is to + // be read into cache. + FLMUINT64 ui64NewerBlkLowTransID, + // Low transaction ID of the last newer + // version of the block. + // NOTE: This has no meaning + // when uiFilePos == uiBlkAddress. + F_CachedBlock * pSCache, // Cache block to read the data + // into. + FLMBOOL * pbFoundVerRV, // Returns a flag to the caller to + // tell it whether it found any + // versions of the requested block + // starting at the file position + // that was passed in. + FLMBOOL * pbDiscardRV // Returns a flag which, if TRUE, + // tells the caller to discard + // the block that was just read + // in and set the high transaction ID + // on the block that comes just + // after it - because they are + // the same version. + ) +{ + RCODE rc = NE_XFLM_OK; + F_BLK_HDR * pBlkHdr = pSCache->m_pBlkHdr; + F_CachedBlock * pNextSCache; + FLMBOOL bMutexLocked = FALSE; + XFLM_LFILE_STATS * pLFileStats; + XFLM_BLOCKIO_STATS * pBlockIOStats; + FLMBOOL bIncrPriorImageCnt = FALSE; + FLMBOOL bIncrOldViewCnt = FALSE; + TMP_READ_STATS TmpReadStats; + TMP_READ_STATS * pTmpReadStats; + XFLM_DB_STATS * pDbStats = pDb->m_pDbStats; + + flmAssert( this == pDb->m_pDatabase); + + *pbFoundVerRV = FALSE; + *pbDiscardRV = FALSE; + + if (pDbStats) + { + f_memset( &TmpReadStats, 0, sizeof( TmpReadStats)); + pTmpReadStats = &TmpReadStats; + } + else + { + pTmpReadStats = NULL; + } + + // Read in the block from the database + + // Stay in a loop reading until we get an error or get the block + + for (;;) + { + if (pDbStats) + { + if (uiFilePos != uiBlkAddress) + { + bIncrPriorImageCnt = TRUE; + } + bIncrOldViewCnt = FALSE; + } + + // Read and verify the block. + + if (RC_BAD( rc = readTheBlock( pDb, pTmpReadStats, + pBlkHdr, uiFilePos, uiBlkAddress))) + { + goto Exit; + } + pBlkHdr->ui8BlkFlags &= ~(BLK_IS_BEFORE_IMAGE); + + // See if we can use the current version of the block, or if we + // must go get a previous version. + + // See if we even got the block we thought we wanted. + + if ((FLMUINT)pBlkHdr->ui32BlkAddr != uiBlkAddress) + { + if (uiFilePos == uiBlkAddress) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + else + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); + } + + goto Exit; + } + + // This flag is set to true to indicate that we found at least one + // version of the requested block. NOTE: This flag does NOT mean + // that we found the specific version requested, only that we + // found some version starting at the given address. + + *pbFoundVerRV = TRUE; + + // Check to see if the transaction range for the block we just read + // overlaps the transaction range for next older version of the block + // in the version list. If the ranges overlap, the transaction ID on + // each block had better be the same or we have a corruption. If the + // transaction IDs are the same, they are the same version of the block, + // and we can discard the one we just read. + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + +Get_Next_Block: + + if ((pNextSCache = pSCache->m_pNextInVersionList) != NULL) + { + FLMUINT64 ui64TmpTransID1; + FLMUINT64 ui64TmpTransID2; + + // If next block is still being read in, we must wait for + // it to complete before looking at its transaction IDs. + + if (pNextSCache->m_ui16Flags & CA_READ_PENDING) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits++; + if (RC_BAD( rc = flmWaitNotifyReq( + gv_XFlmSysData.hBlockCacheMutex, pDb->m_hWaitSem, + &pNextSCache->m_pNotifyList, (void *)&pNextSCache))) + { + goto Exit; + } + + // The thread doing the notify "uses" the cache block + // on behalf of this thread to prevent the cache block + // from being flushed after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the cache block - because we + // will put a "use" on it below. + + pNextSCache->releaseForThread(); + + // See if we still have the same next block. + + goto Get_Next_Block; + } + + // Check for overlapping trans ID ranges. NOTE: At this + // point, if we have an overlap, we know we have the version + // of the block we need (see comment above). Hence, we will + // either break out of the loop at this point or goto exit + // and return an error. + + ui64TmpTransID1 = pBlkHdr->ui64TransID; + if (ui64TmpTransID1 <= pNextSCache->m_ui64HighTransID) + { + ui64TmpTransID2 = pNextSCache->getLowTransID(); + + // If the low trans IDs on the two blocks are not equal + // we have a corruption. + + if (ui64TmpTransID1 != ui64TmpTransID2) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // The blocks are the same, discard one of them. + + *pbDiscardRV = TRUE; + + // Set the high trans ID on the block we are NOT discarding. + // To find the version of the block we want, we have been + // reading through a chain of blocks, from newer versions to + // progressively older versions. If uiFilePos == uiBlkAddress, + // we are positioned on the most current version of the block. + // In this case, the high trans ID for the block should be + // set to the highest possible value. + + // If uiFilePos != uiBlkAddress, we are positioned on an older + // version of the block. The variable ui64NewerBlkLowTransID + // contains the low transaction ID for a newer version of the + // block we read just prior to reading this block. + + if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb || + uiFilePos == uiBlkAddress) + { + pNextSCache->setTransID( ~((FLMUINT64)0)); + } + else + { + pNextSCache->setTransID( (ui64NewerBlkLowTransID - 1)); + } + + // When discard flag is TRUE, we need to go right to + // exit, because we don't want to decrypt, do sanity + // check, etc. NOTE: mutex is still locked, and + // we want it to remain locked - see code at Exit. + + goto Exit; + } + } + + // See if this version of the block is what we want + + if (pBlkHdr->ui64TransID <= pDb->m_ui64CurrTransID) + { + + // Set the high trans ID on the block. If we are in an + // update transaction, or uiFilePos == uiBlkAddress, we + // are positioned on the most current version of the block. + // In this case, the high trans ID for the block should be + // set to ~((FLMUINT64)0). Otherwise we are positioned on an older + // version of the block, and the block's high transaction ID + // should be set to the newer block's low transaction ID minus one. + + if (pDb->m_eTransType == XFLM_UPDATE_TRANS || m_bTempDb || + uiFilePos == uiBlkAddress) + { + pSCache->setTransID( ~((FLMUINT64)0)); + } + else + { + pSCache->setTransID( (ui64NewerBlkLowTransID - 1)); + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + break; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + // At this point, we know we are going to have to get a prior + // version of the block. In an update transaction, this is + // indicative of a file corruption. + + if (pDb->m_eTransType != XFLM_READ_TRANS) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // At this point, we know we are in a read transaction. Save the + // block's low trans ID. + + ui64NewerBlkLowTransID = pBlkHdr->ui64TransID; + + // See if there is a prior version of the block and determine whether + // it's expected trans ID is in the range we need. + // NOTE: If the prior version address is zero or is the same as our + // current file position, there is no previous version of the block. + + if ((FLMUINT)pBlkHdr->ui32PriorBlkImgAddr == uiFilePos) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); + goto Exit; + } + uiFilePos = (FLMUINT)pBlkHdr->ui32PriorBlkImgAddr; + if (!uiFilePos) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); + goto Exit; + } + } + + // Perform a sanity check on the block header. + + if ((FLMUINT)pBlkHdr->ui16BlkBytesAvail > + m_uiBlockSize - blkHdrSize( pBlkHdr)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + +Exit: + + // NOTE: When we are discarding the block, we CANNOT unlock + // the mutex, because we have to take care of it on + // the outside. Mutex better be locked if we are discarding. + + if (*pbDiscardRV) + { + flmAssert( bMutexLocked); + } + else if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + // If we got an old view error, it has to be a corruption, unless we + // were killed. + + if (rc == NE_XFLM_OLD_VIEW) + { + if (!pDb->m_uiKilledTime || pDb->m_eTransType == XFLM_UPDATE_TRANS || + m_bTempDb) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + } + } + + // Increment cache fault statistics + + if (pDbStats) + { + if ((pLFileStats = pDb->getLFileStatPtr( pLFile)) == NULL) + { + pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, NULL, (FLMBYTE *)pBlkHdr); + } + else if (RC_BAD( rc)) + { + // Didn't really get a valid block, assign all statistics + // gathered to the leaf block statistics. + + pBlockIOStats = &pLFileStats->LeafBlockStats; + } + else + { + pBlockIOStats = flmGetBlockIOStatPtr( pDbStats, + pLFileStats, (FLMBYTE *)pBlkHdr); + } + + if (pBlockIOStats) + { + pDbStats->bHaveStats = TRUE; + if (pLFileStats) + { + pLFileStats->bHaveStats = TRUE; + } + + flmUpdateDiskIOStats( &pBlockIOStats->BlockReads, + &TmpReadStats.BlockReads); + + flmUpdateDiskIOStats( &pBlockIOStats->OldViewBlockReads, + &TmpReadStats.OldViewBlockReads); + + pBlockIOStats->uiBlockChkErrs += + TmpReadStats.uiBlockChkErrs; + + pBlockIOStats->uiOldViewBlockChkErrs += + TmpReadStats.uiOldViewBlockChkErrs; + if (rc == NE_XFLM_OLD_VIEW || bIncrOldViewCnt) + { + pBlockIOStats->uiOldViewErrors++; + } + } + } + + return( rc); +} + +/**************************************************************************** +Desc: Increment the use count on a cache block for a particular + thread. NOTE: This routine assumes that the block cache mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +void F_CachedBlock::dbgUseForThread( + FLMUINT uiThreadId) +{ + SCACHE_USE * pUse; + FLMUINT uiMyThreadId = (FLMUINT)(!uiThreadId + ? (FLMUINT)f_threadId() + : uiThreadId); + + // If the use count is 0, make sure there are not entries + // in the use list. + + if (!m_uiUseCount && m_pUseList != NULL) + { + return; + } + + // First increment the overall use count for the block + + m_uiUseCount++; + if (m_uiUseCount == 1) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed++; + if (m_uiChecksum) + { + flmAssert( m_uiChecksum == computeChecksum()); + } + } + gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses++; + + // Now add the thread's usage record - or increment it if there + // is already one there for the thread. + + // See if we already have this thread in the use list + + pUse = m_pUseList; + while (pUse && pUse->uiThreadId != uiMyThreadId) + { + pUse = pUse->pNext; + } + + if (!pUse) + { + if (RC_BAD( f_calloc( (FLMUINT)sizeof( SCACHE_USE), + &pUse))) + { + return; + } + + f_memset( pUse, 0, sizeof( SCACHE_USE)); + pUse->uiThreadId = uiMyThreadId; + pUse->pNext = m_pUseList; + m_pUseList = pUse; + } + + pUse->uiUseCount++; +} +#endif + +/**************************************************************************** +Desc: Decrement the use count on a cache block for a particular + thread. NOTE: This routine assumes that the block cache mutex + is locked. +****************************************************************************/ +#ifdef FLM_DEBUG +void F_CachedBlock::dbgReleaseForThread( void) +{ + SCACHE_USE * pUse; + SCACHE_USE * pPrevUse; + FLMUINT uiMyThreadId = (FLMUINT)f_threadId(); + + // Find the thread's use + + pUse = m_pUseList; + pPrevUse = NULL; + while (pUse && pUse->uiThreadId != uiMyThreadId) + { + pPrevUse = pUse; + pUse = pUse->pNext; + } + + if (!pUse) + { + return; + } + + m_uiUseCount--; + gv_XFlmSysData.pBlockCacheMgr->m_uiTotalUses--; + if (!m_uiUseCount) + { + m_uiChecksum = computeChecksum(); + gv_XFlmSysData.pBlockCacheMgr->m_uiBlocksUsed--; + flmAssert( pUse->uiUseCount == 1); + } + + // Free the use record if its count goes to zero + + pUse->uiUseCount--; + if (!pUse->uiUseCount) + { + if (!pPrevUse) + { + m_pUseList = pUse->pNext; + } + else + { + pPrevUse->pNext = pUse->pNext; + } + f_free( &pUse); + } +} +#endif + +/**************************************************************************** +Desc: Read a data block into cache. This routine takes care of allocating + a cache block and reading the block from disk into memory. NOTE: + This routine assumes that the block cache mutex is locked. It may + unlock the block cache mutex long enough to do the read, but the + mutex will still be locked when it exits. +****************************************************************************/ +RCODE F_Database::readIntoCache( + F_Db * pDb, + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiBlkAddress, // Address of block that is to + // be read into cache. + F_CachedBlock * pPrevInVerList, // Previous block in version list to + // link the block to. + F_CachedBlock * pNextInVerList, // Next block in version list to link + // the block to. + F_CachedBlock ** ppSCacheRV, // Returns allocated cache block. + FLMBOOL * pbGotFromDisk) // Returns TRUE if block was read + // from disk +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache; + F_CachedBlock * pTmpSCache; + FNOTIFY * pNotify; + FLMUINT uiFilePos; + FLMUINT64 ui64NewerBlkLowTransID = 0; + FLMBOOL bFoundVer; + FLMBOOL bDiscard; + + flmAssert( this == pDb->m_pDatabase); + + *pbGotFromDisk = FALSE; + + // Lock the prev and next in place by incrementing their use + // count. We don't want allocBlock to use them. + + if (pPrevInVerList) + { + pPrevInVerList->useForThread( 0); + } + + if (pNextInVerList) + { + pNextInVerList->useForThread( 0); + } + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( pDb, &pSCache); + + if (pPrevInVerList) + { + pPrevInVerList->releaseForThread(); + } + + if (pNextInVerList) + { + pNextInVerList->releaseForThread(); + } + + if (RC_BAD( rc)) + { + goto Exit; + } + + pSCache->m_uiBlkAddress = uiBlkAddress; + + // Set the "dummy" flag so that we won't incur the overhead of + // linking the block into the replace list. It would be removed + // from the replace list almost immediately anyway, when the + // "read pending" flag is set below. + + pSCache->m_ui16Flags |= CA_DUMMY_FLAG; + + // Link block into various lists + + if( pDb->m_uiFlags & FDB_DONT_POISON_CACHE) + { + if( !(pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) || + (pLFile && pLFile->eLfType != XFLM_LF_INDEX)) + { + pSCache->linkToGlobalListAsLRU(); + } + else + { + pSCache->linkToGlobalListAsMRU(); + } + } + else + { + pSCache->linkToGlobalListAsMRU(); + } + + pSCache->linkToDatabase( this); + if (!pPrevInVerList) + { + F_CachedBlock ** ppSCacheBucket; + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_uiSigBitsInBlkSize, uiBlkAddress); + uiFilePos = uiBlkAddress; + if (pNextInVerList) + { + pNextInVerList->unlinkFromHashBucket( ppSCacheBucket); + } + pSCache->linkToHashBucket( ppSCacheBucket); + } + else + { + uiFilePos = pPrevInVerList->getPriorImageAddress(); + ui64NewerBlkLowTransID = pPrevInVerList->getLowTransID(); + pPrevInVerList->m_pNextInVersionList = pSCache; + pPrevInVerList->verifyCache( 2400); + } + + if (pNextInVerList) + { + pNextInVerList->m_pPrevInVersionList = pSCache; + pNextInVerList->verifyCache( 2500); + } + + pSCache->m_pPrevInVersionList = pPrevInVerList; + pSCache->m_pNextInVersionList = pNextInVerList; + pSCache->verifyCache( 2600); + + // Set the read-pending flag for this block. This will force other + // threads that need to read this block to wait for the I/O to + // complete. + + pSCache->setFlags( CA_READ_PENDING); + pSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; + gv_XFlmSysData.pBlockCacheMgr->m_uiPendingReads++; + + // Unlock the mutex and attempt to read the block into memory + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + rc = readBlock( pDb, pLFile, uiFilePos, uiBlkAddress, + ui64NewerBlkLowTransID, + pSCache, &bFoundVer, &bDiscard); + + // NOTE: If the bDiscard flag is TRUE, the mutex will still be + // locked. If FALSE, we need to relock it. + + if (!bDiscard) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + } + + // Get a pointer to the notify list BEFORE discarding the cache + // block - if we are going to discard - because pSCache can + // change if we discard. + + pNotify = pSCache->m_pNotifyList; + pSCache->m_pNotifyList = NULL; + + // Unset the read pending flag and reset the use count to zero. + // Both of these actions should be done before doing a discard, + // if a discard is going to be done. + + pSCache->clearFlags( CA_READ_PENDING); + gv_XFlmSysData.pBlockCacheMgr->m_uiPendingReads--; + pSCache->releaseForThread(); + + // If we had no errors, take care of some other things + + if (RC_OK( rc)) + { + // The bDiscard flag tells us that we should discard the + // block that we just read and use the next block in the + // version list - because they are the same version. + + if (bDiscard) + { + + // NOTE: We are guaranteed that pSCache->m_pNextInVersionList + // is non-NULL at this point, because when we set the + // bDiscard flag to TRUE, it was non-NULL, and we know that + // the mutex was NOT unlocked in that case. + + pTmpSCache = pSCache->m_pNextInVersionList; + pSCache->unlinkCache( TRUE, NE_XFLM_OK); + pSCache = pTmpSCache; + } + else + { + *pbGotFromDisk = TRUE; + } + } + + // Notify all of the waiters of the read result. + // IMPORTANT NOTE: This should be the LAST thing that is + // done except for unlink the block below in the case of + // an error having occurred. + + ScaNotify( pNotify, pSCache, rc); + + // If we had a BAD rc, unlink the block from the lists it is in and + // free the memory. + + if (RC_BAD( rc)) + { + pSCache->unlinkCache( TRUE, NE_XFLM_OK); + goto Exit; + } + + *ppSCacheRV = pSCache; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine frees all cache blocks that have been modified by + the update transaction. This routine is called whenever a + transaction is to be aborted. +****************************************************************************/ +void F_Database::freeModifiedBlocks( + FLMUINT64 ui64CurrTransId) +{ + F_CachedBlock * pSCache; + F_CachedBlock * pNextSCache; + FLMBOOL bFirstPass = TRUE; + FLMBOOL bFreedAll; + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + // Unlink all log blocks and reset their flags so they + // won't be marked as needing to be written to disk. + + unlinkTransLogBlocks(); + +Do_Free_Pass: + + pSCache = m_pSCacheList; + flmAssert( !m_pPendingWriteList); + bFreedAll = TRUE; + while (pSCache) + { + + // If the high transaction ID on the block is one less than this + // transaction's ID, the block is the most current block. Therefore, + // its high transaction ID should be reset to ~((FLMUINT64)0). + + if (pSCache->m_ui64HighTransID == ui64CurrTransId - 1) + { + pSCache->setTransID( ~((FLMUINT64)0)); + + // Need to link blocks that become the current version again + // into the file log list if they are dirty. linkToLogList + // will check to see if the block has already been logged. If it has, + // it won't be linked into the list. + // NOTE: If the blocks were in the "new" list originally, we don't take + // the time to put them back into that list because they would have to + // be inserted in order. They will still get written out eventually, but + // they won't be written out by the reduceNewBlocks call. + + if (pSCache->m_ui16Flags & CA_DIRTY) + { + pSCache->linkToLogList(); + } + } + else if (pSCache->m_ui64HighTransID == ~((FLMUINT64)0) && + pSCache->getLowTransID() >= ui64CurrTransId && + !(pSCache->m_ui16Flags & CA_READ_PENDING)) + + { + pNextSCache = pSCache->m_pNextInDatabase; + + // Another thread might have a temporary "use" on this + // block. Unlock the mutex long enough to allow the + // other thread(s) to get rid of their "uses". Then start + // from the top of the list again. + + if (pSCache->m_uiUseCount) + { + + // Don't want to unlock the mutex during the first pass + // because it opens the door to the prior version of one of + // these modified blocks being removed from cache before we + // have a chance to reset its ui64HighTransID back to + // ~((FLMUINT64)0). + // During the first pass, we want to get through all of the + // blocks so that the code up above will get exercised for + // each such block. + + if (bFirstPass) + { + bFreedAll = FALSE; + pSCache = pNextSCache; + continue; + } + else + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + f_sleep( 10); + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + pSCache = m_pSCacheList; + continue; + } + } + else + { +#ifdef FLM_DEBUG + F_CachedBlock * pResetDirty = NULL; +#endif + + // Unset dirty flag so we don't get an assert in unlinkCache. + + if (pSCache->m_ui16Flags & CA_DIRTY) + { +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; +#endif + flmAssert( this == pSCache->m_pDatabase); + pSCache->unsetDirtyFlag(); +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'G'); +#endif + } + +#ifdef FLM_DEBUG + // If m_pNextInVersionList is dirty it is because + // ScaUnlinkTransLogBlocks changed the WAS_DIRTY flag to + // DIRTY. If we don't temporarily clear the DIRTY flag, + // unlinkCache will assert. + + if( pSCache->m_pNextInVersionList && + (pSCache->m_pNextInVersionList->m_ui16Flags & CA_DIRTY)) + { + pResetDirty = pSCache->m_pNextInVersionList; + +#ifdef FLM_CACHE_PROTECT + pResetDirty->unprotectCachedItem(); +#endif + pResetDirty->m_ui16Flags &= ~CA_DIRTY; +#ifdef FLM_CACHE_PROTECT + pResetDirty->protectCachedItem(); +#endif + } +#endif + + pSCache->unlinkCache( TRUE, NE_XFLM_OK); + +#ifdef FLM_DEBUG + if( pResetDirty) + { +#ifdef FLM_CACHE_PROTECT + pResetDirty->unprotectCachedItem(); +#endif + pResetDirty->m_ui16Flags |= CA_DIRTY; +#ifdef FLM_CACHE_PROTECT + pResetDirty->protectCachedItem(); +#endif + } +#endif + pSCache = pNextSCache; + continue; + } + } + + pSCache = pSCache->m_pNextInDatabase; + } + + if (!bFreedAll && bFirstPass) + { + bFirstPass = FALSE; + goto Do_Free_Pass; + } + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: Write an IO buffer to disk. +****************************************************************************/ +RCODE F_Database::writeContiguousBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + F_IOBuffer * pIOBuffer, + FLMUINT uiBlkAddress, + FLMBOOL bDoAsync) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE * pucWriteBuffer; + F_IOBuffer * pAsyncBuffer; + FLMUINT uiBytesWritten; + FLMUINT uiWriteLen; + + pucWriteBuffer = pIOBuffer->getBuffer(); + +#if defined( FLM_NLM) || defined( FLM_WIN) + if (!bDoAsync) + { + pAsyncBuffer = NULL; + } + else + { + pAsyncBuffer = pIOBuffer; + } +#else + F_UNREFERENCED_PARM( bDoAsync); + pAsyncBuffer = NULL; +#endif + + // Determine how many bytes to write + + uiWriteLen = pIOBuffer->getBufferSize(); + pSFileHdl->setMaxAutoExtendSize( m_uiMaxFileSize); + pSFileHdl->setExtendSize( m_uiFileExtendSize); + + pIOBuffer->startTimer( pDbStats); + + // NOTE: No guarantee that pIOBuffer will still be around + // after the call to WriteBlock, unless we are doing + // non-asynchronous write. + + rc = pSFileHdl->WriteBlock( uiBlkAddress, uiWriteLen, + pucWriteBuffer, pIOBuffer->getBufferSize(), + pAsyncBuffer, &uiBytesWritten); + if (!pAsyncBuffer) + { + pIOBuffer->notifyComplete( rc); + } + pIOBuffer = NULL; + + if (RC_BAD( rc)) + { + if (pDbStats) + { + pDbStats->bHaveStats = TRUE; + pDbStats->uiWriteErrors++; + } + goto Exit; + } + +Exit: + + // If we allocated a write buffer, but did not do a write with it + // still need to do the notify to clean up cache blocks. + + if (pIOBuffer) + { + flmAssert( RC_BAD( rc)); + pIOBuffer->notifyComplete( rc); + } + return( rc); +} + +/**************************************************************************** +Desc: Prepares a block to be written out. Calculates the checksum and + converts the block to native format if not currently in native + format. +****************************************************************************/ +RCODE flmPrepareBlockToWrite( + FLMUINT uiBlockSize, + F_BLK_HDR * pBlkHdr) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkLen; + + if ((FLMUINT)pBlkHdr->ui16BlkBytesAvail > + uiBlockSize - blkHdrSize( pBlkHdr)) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_BLOCK_CRC); + goto Exit; + } + uiBlkLen = (blkIsNewBTree( pBlkHdr) + ? uiBlockSize + : uiBlockSize - (FLMUINT)pBlkHdr->ui16BlkBytesAvail); + + // Block should already be in native format. + + flmAssert( !blkIsNonNativeFormat( pBlkHdr)); + + // Calculate and set the block CRC. + + pBlkHdr->ui32BlkCRC = calcBlkCRC( pBlkHdr, uiBlkLen); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine writes all blocks in the sorted list, or releases them. + It attempts to write as many as it can that are currently + contiguous. + NOTE: This routine assumes that the block cache mutex is NOT locked. +****************************************************************************/ +RCODE F_Database::writeSortedBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxDirtyCache, + FLMUINT * puiDirtyCacheLeft, + FLMBOOL * pbForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL bDoAsync, + FLMUINT uiNumSortedBlocks, + FLMBOOL * pbWroteAll) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiStartBlkAddr = 0; + FLMUINT uiLastBlkAddr = 0; + FLMUINT uiContiguousBlocks = 0; + FLMUINT uiNumSortedBlocksProcessed; + FLMUINT uiBlockCount; + F_CachedBlock * ppContiguousBlocks [MAX_BUFFER_BLOCKS]; + FLMBOOL bBlockDirty [MAX_BUFFER_BLOCKS]; + FLMUINT uiOffset; + FLMUINT uiTmpOffset; + FLMUINT uiLoop; + FLMUINT uiStartOffset; + FLMUINT uiCopyLen; + FLMBOOL bForceCheckpoint = *pbForceCheckpoint; + F_CachedBlock * pSCache; + F_IOBuffer * pIOBuffer = NULL; + FLMBYTE * pucBuffer; + + uiOffset = 0; + for (;;) + { + + // Mutex must be locked to test dirty flags. + + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + // See how many we have that are contiguous + + uiContiguousBlocks = 0; + uiNumSortedBlocksProcessed = 0; + uiStartOffset = uiTmpOffset = uiOffset; + while (uiTmpOffset < uiNumSortedBlocks) + { + pSCache = m_ppBlocksDone [uiTmpOffset]; + + // See if this block is still eligible for writing out. + // If so, mark it as write pending and add to list. + + flmAssert( pSCache->m_ui16Flags & CA_DIRTY); + + // Is it contiguous with last block or the first block? + + if (!uiContiguousBlocks || + (FSGetFileNumber( uiLastBlkAddr) == + FSGetFileNumber( pSCache->m_uiBlkAddress) && + uiLastBlkAddr + m_uiBlockSize == pSCache->m_uiBlkAddress)) + { + + // Block is either first block or contiguous with + // last block. + +Add_Contiguous_Block: + uiLastBlkAddr = pSCache->m_uiBlkAddress; + + // Set first block address if this is the first one. + + if (!uiContiguousBlocks) + { + uiStartBlkAddr = pSCache->m_uiBlkAddress; + } + ppContiguousBlocks [uiContiguousBlocks] = pSCache; + bBlockDirty [uiContiguousBlocks++] = TRUE; + uiNumSortedBlocksProcessed++; + if (uiContiguousBlocks == MAX_BUFFER_BLOCKS) + { + break; + } + uiTmpOffset++; + } + else + { + FLMUINT uiGap; + FLMUINT uiSaveContiguousBlocks; + FLMUINT uiBlkAddress; + + // Ran into a non-contiguous block. If we are not forcing + // a checkpoint, take what we have and write it out. + // If we are forcing a checkpoint, see if we can fill the + // gap with other blocks in cache. + + if (!bForceCheckpoint) + { + break; + } + + // See if the gap is worth trying to fill. + + // If blocks are in different files, cannot fill gap. + + if (FSGetFileNumber( uiLastBlkAddr) != + FSGetFileNumber( pSCache->m_uiBlkAddress)) + { + break; + } + + // If 32K won't encompass both blocks, not worth it to try + // and fill the gap. + + uiGap = FSGetFileOffset( pSCache->m_uiBlkAddress) - + FSGetFileOffset( uiLastBlkAddr) - m_uiBlockSize; + if (uiGap > 32 * 1024 - (m_uiBlockSize * 2)) + { + break; + } + + // If the gap would run us off the maximum blocks to + // request, don't try to fill it. + + if (uiContiguousBlocks + uiGap / m_uiBlockSize + 1 > + MAX_BUFFER_BLOCKS) + { + break; + } + + uiSaveContiguousBlocks = uiContiguousBlocks; + uiBlkAddress = uiLastBlkAddr + m_uiBlockSize; + while (uiBlkAddress != pSCache->m_uiBlkAddress) + { + F_CachedBlock ** ppSCacheBucket; + F_CachedBlock * pTmpSCache; + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_uiSigBitsInBlkSize, uiBlkAddress); + pTmpSCache = *ppSCacheBucket; + while (pTmpSCache && + (pTmpSCache->m_uiBlkAddress != uiBlkAddress || + pTmpSCache->m_pDatabase != this)) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + if (!pTmpSCache || + (pTmpSCache->m_ui16Flags & + (CA_READ_PENDING | CA_WRITE_PENDING | CA_WRITE_INHIBIT)) || + pTmpSCache->m_ui64HighTransID != ~((FLMUINT64)0)) + { + break; + } + ppContiguousBlocks [uiContiguousBlocks] = pTmpSCache; + + bBlockDirty [uiContiguousBlocks++] = + (pTmpSCache->m_ui16Flags & CA_DIRTY) + ? TRUE + : FALSE; + + pTmpSCache->useForThread( 0); + uiBlkAddress += m_uiBlockSize; + } + + // If we couldn't fill in the entire gap, we are done. + + if (uiBlkAddress != pSCache->m_uiBlkAddress) + { + + // Release the blocks we obtained in the above loop. + + while (uiContiguousBlocks > uiSaveContiguousBlocks) + { + uiContiguousBlocks--; + ppContiguousBlocks [uiContiguousBlocks]->releaseForThread(); + } + break; + } + else + { + goto Add_Contiguous_Block; + } + } + } + + // At this point, we know how many are contiguous. + + if (!uiContiguousBlocks) + { + flmAssert( uiOffset == uiNumSortedBlocks); + break; + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Ask for a buffer of the size needed. + + flmAssert( pIOBuffer == NULL); + if (RC_BAD( rc = m_pBufferMgr->getBuffer( + &pIOBuffer, uiContiguousBlocks * m_uiBlockSize, + m_uiBlockSize))) + { + goto Exit; + } + pIOBuffer->setCompletionCallback( scaWriteComplete); + + // Callback will now take care of everything between + // uiStartOffset and uiStartOffset + uiNumSortedBlocksProcessed - 1 + // inclusive, as well as any non-dirty blocks that were + // put in for filler. + + flmAssert( uiNumSortedBlocksProcessed); + uiOffset = uiStartOffset + uiNumSortedBlocksProcessed; + uiBlockCount = uiContiguousBlocks; + + // Must set to zero so we don't process ppContiguousBlocks + // at exit. + + uiContiguousBlocks = 0; + + // Set write pending on all of the blocks before unlocking + // the mutex. + // Then unlock the mutex and come out and copy + // the blocks. + + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + for (uiLoop = 0; uiLoop < uiBlockCount; uiLoop++) + { + pSCache = ppContiguousBlocks [uiLoop]; + if (bBlockDirty [uiLoop]) + { + flmAssert( pSCache->m_ui16Flags & CA_DIRTY); + flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_INHIBIT)); + pSCache->setFlags( CA_WRITE_PENDING); + flmAssert( *puiDirtyCacheLeft >= m_uiBlockSize); + (*puiDirtyCacheLeft) -= m_uiBlockSize; + pSCache->unlinkFromDatabase(); + pSCache->linkToDatabase( this); + } + else + { + flmAssert( !(pSCache->m_ui16Flags & CA_DIRTY)); + } + + // Set callback data so we will release these and clear + // the pending flag if we don't do the I/O. + + pIOBuffer->setCompletionCallbackData( uiLoop, pSCache); + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + // Copy blocks into the IO buffer. + + pucBuffer = pIOBuffer->getBuffer(); + for (uiLoop = 0; + uiLoop < uiBlockCount; + uiLoop++, pucBuffer += m_uiBlockSize) + { + pSCache = ppContiguousBlocks [uiLoop]; + + // Copy data from block to the write buffer + + uiCopyLen = blkGetEnd( m_uiBlockSize, + blkHdrSize( pSCache->m_pBlkHdr), + pSCache->m_pBlkHdr); + f_memcpy( pucBuffer, pSCache->m_pBlkHdr, uiCopyLen); + + // Encrypt the block if needed + + if (RC_BAD( rc = encryptBlock( m_pDictList, + pucBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, + (F_BLK_HDR *)pucBuffer))) + { + goto Exit; + } + } + + rc = writeContiguousBlocks( pDbStats, pSFileHdl, + pIOBuffer, uiStartBlkAddr, bDoAsync); + pIOBuffer = NULL; + + // See if we should give up our write lock. Will do so if we + // are not forcing a checkpoint and we have not exceeded the + // maximum time since the last checkpoint AND we have gotten + // below the maximum dirty blocks allowed. + + if (!bForceCheckpoint && bIsCPThread) + { + FLMUINT uiCurrTime = (FLMUINT)FLM_GET_TIMER(); + + if (scaSeeIfForceCheckpoint( uiCurrTime, m_uiLastCheckpointTime, + m_pCPInfo)) + { + bForceCheckpoint = TRUE; + } + else + { + if (m_pWriteLockObj->ThreadWaitingLock() && + *puiDirtyCacheLeft <= uiMaxDirtyCache) + { + + // Break out of loop and finish writing whatever + // we have pending. + + *pbWroteAll = FALSE; + goto Exit; + } + } + } + } + +Exit: + + // Unuse any blocks that did not get processed. + + while (uiOffset < uiNumSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + m_ppBlocksDone[ uiOffset]->releaseForThread(); + uiOffset++; + } + + while (uiContiguousBlocks) + { + uiContiguousBlocks--; + + // Only release the non-dirty blocks, because dirty blocks + // will have been taken care of in the loop above. + + if (!bBlockDirty [uiContiguousBlocks]) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + ppContiguousBlocks [uiContiguousBlocks]->releaseForThread(); + } + } + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + // If we allocated a write buffer, but did not do a write with it, + // still need to call notifyComplete so that our callback function + // will be called and the F_CachedBlock objects will be released. + + if (pIOBuffer) + { + flmAssert( RC_BAD( rc)); + pIOBuffer->notifyComplete( rc); + } + + *pbForceCheckpoint = bForceCheckpoint; + return( rc); +} + +/**************************************************************************** +Desc: This routine writes all dirty cache blocks to disk. This routine + is called when a transaction is committed. +****************************************************************************/ +RCODE F_Database::flushDirtyBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT uiMaxDirtyCache, + FLMBOOL bForceCheckpoint, + FLMBOOL bIsCPThread, + FLMBOOL * pbWroteAll) +{ + RCODE rc = NE_XFLM_OK; + RCODE rc2; + F_CachedBlock * pSCache; + FLMBOOL bDoAsync; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiSortedBlocks = 0; + FLMUINT uiBlockCount = 0; + FLMBOOL bWasForcing; + FLMBOOL bWriteInhibited; + FLMUINT uiDirtyCacheLeft; + FLMBOOL bAllocatedAll = FALSE; + + flmAssert( !m_uiLogCacheCount); + + if (m_pCPInfo) + { + lockMutex(); + m_pCPInfo->bWritingDataBlocks = TRUE; + unlockMutex(); + } + + // See if we can do async IO. + + bDoAsync = (gv_XFlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + flmAssert( !m_pPendingWriteList); + + uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; + + // If we are forcing a checkpoint, pre-allocate an array big enough + // to hold all of the dirty blocks. We do this so we won't end up + // continually re-allocating the array in the loop below. + +Force_Checkpoint: + + if (bForceCheckpoint) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + pSCache = m_pSCacheList; + uiBlockCount = 0; + while (pSCache && (pSCache->m_ui16Flags & CA_DIRTY)) + { + uiBlockCount++; + pSCache = pSCache->m_pNextInDatabase; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + bAllocatedAll = TRUE; + if (uiBlockCount > m_uiBlocksDoneArraySize * 2) + { + if (RC_BAD( rc = allocBlocksArray( + (uiBlockCount + 1) / 2, TRUE))) + { + if (rc == NE_XFLM_MEM) + { + bAllocatedAll = FALSE; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + } + + for (;;) + { + + flmAssert( !bMutexLocked); + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + + // Create a list of blocks to write out - MAX_BLOCKS_TO_SORT at most. + + pSCache = m_pSCacheList; + uiSortedBlocks = 0; + for (;;) + { + FLMUINT uiPrevBlkAddress; + + if (bAllocatedAll) + { + if (uiSortedBlocks == uiBlockCount) + { +#ifdef FLM_DEBUG + // Better not be any dirty blocks after the last one. + + if (uiSortedBlocks) + { + pSCache = m_ppBlocksDone [uiSortedBlocks - 1]; + flmAssert( !pSCache->m_pNextInDatabase || + !(pSCache->m_pNextInDatabase->m_ui16Flags & CA_DIRTY)); + } +#endif + break; + } + flmAssert( pSCache && (pSCache->m_ui16Flags & CA_DIRTY)); + } + else + { + if (!pSCache || !(pSCache->m_ui16Flags & CA_DIRTY) || + uiSortedBlocks == MAX_BLOCKS_TO_SORT) + { + break; + } + } + + flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_PENDING)); + uiPrevBlkAddress = pSCache->getPriorImageAddress(); + + bWriteInhibited = FALSE; + if (pSCache->m_ui16Flags & CA_WRITE_INHIBIT) + { + // When the checkpoint thread is running there is no need to + // inhibit writes - because it is not possible for an updater + // to be making any changes at this point. However, + // the inhibit writes bit may still be set because the + // thread that originally did the update transaction never + // got the use count to go to zero (due to a reader that + // simultaneously had a use) and hence, the inhibit bit + // has never been unset. It is only unset when we see + // the use count go to zero. + + if (bIsCPThread) + { + pSCache->clearFlags( CA_WRITE_INHIBIT); + } + else + { + bWriteInhibited = TRUE; + } + } + + // Skip blocks that are write inhibited or that have + // not been properly logged yet. + + if (bWriteInhibited || + (!uiPrevBlkAddress && pSCache->m_pNextInVersionList)) + { + flmAssert( !bForceCheckpoint); + } + else + { + if (uiSortedBlocks == m_uiBlocksDoneArraySize * 2) + { + if (RC_BAD( rc = allocBlocksArray( 0, TRUE))) + { + goto Exit; + } + } + + // Keep list of blocks to process + + m_ppBlocksDone [uiSortedBlocks++] = pSCache; + + // Must use to keep from going away. + + pSCache->useForThread( 0); + } + pSCache = pSCache->m_pNextInDatabase; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + // Sort the list of blocks by block address. + + if (uiSortedBlocks) + { + if (uiSortedBlocks > 1) + { + f_qsort( m_ppBlocksDone, + 0, uiSortedBlocks - 1, scaSortCompare, scaSortSwap); + } + bWasForcing = bForceCheckpoint; + rc = writeSortedBlocks( pDbStats, pSFileHdl, + uiMaxDirtyCache, &uiDirtyCacheLeft, + &bForceCheckpoint, bIsCPThread, + bDoAsync, uiSortedBlocks, pbWroteAll); + } + else + { + goto Exit; + } + + // Set to zero so won't get released at exit. + + uiSortedBlocks = 0; + + if (!bIsCPThread || RC_BAD( rc) || !(*pbWroteAll)) + { + goto Exit; + } + if (bForceCheckpoint) + { + if (!bWasForcing) + { + + // Needs to be the checkpoint thread that does this + // because all of the log blocks have to have been + // written out - which the checkpoint thread does + // before calling this routine. + + flmAssert( bIsCPThread); + goto Force_Checkpoint; + } + else if (bAllocatedAll) + { + // We did all of the blocks in one pass, so + // break out of the loop. + + goto Exit; + } + } + } + +Exit: + + // Release any blocks that are still used. + + while (uiSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + uiSortedBlocks--; + + // Release any blocks we didn't process through. + + pSCache = m_ppBlocksDone [uiSortedBlocks]; + pSCache->releaseForThread(); + } + + // Need to finish up any async writes. + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + if (bDoAsync) + { + + // Wait for writes to complete. + + if (RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + + flmAssert( !m_pPendingWriteList); + + // Better not be any incomplete writes at this point. + + flmAssert( !m_pBufferMgr->havePendingIO()); + + // Don't keep around a large block array if we happened to + // allocate one that is bigger than our normal size. It may + // be huge because we were forcing a checkpoint. + + if (m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) + { + f_free( &m_ppBlocksDone); + m_uiBlocksDoneArraySize = 0; + } + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE F_Database::reduceDirtyCache( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl) +{ + RCODE rc = NE_XFLM_OK; + RCODE rc2; + F_CachedBlock * pSCache; + FLMBOOL bDoAsync; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiDirtyCacheLeft; + FLMUINT uiSortedBlocks = 0; + FLMUINT uiInhibitCount; + FLMBOOL bForceCheckpoint; + FLMBOOL bWroteAll; + + flmAssert( !m_uiLogCacheCount); + + // See if we can do async IO. + + bDoAsync = (gv_XFlmSysData.bOkToDoAsyncWrites && + pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + flmAssert( !m_pPendingWriteList); + + if( m_uiDirtyCacheCount > m_uiBlocksDoneArraySize * 2) + { + if( RC_BAD( rc = allocBlocksArray( + (m_uiDirtyCacheCount + 1) / 2, TRUE))) + { + goto Exit; + } + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + + pSCache = m_pSCacheList; + uiSortedBlocks = 0; + uiInhibitCount = 0; + + while( pSCache && + (pSCache->m_ui16Flags & CA_DIRTY)) + { + if( (pSCache->m_ui16Flags & CA_WRITE_INHIBIT) != 0) + { + uiInhibitCount++; + } + else + { + flmAssert( uiSortedBlocks < m_uiDirtyCacheCount); + m_ppBlocksDone[ uiSortedBlocks++] = pSCache; + pSCache->useForThread( 0); + } + + pSCache = pSCache->m_pNextInDatabase; + } + + flmAssert( uiSortedBlocks + uiInhibitCount == m_uiDirtyCacheCount); + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + if( !uiSortedBlocks) + { + goto Exit; + } + + if( uiSortedBlocks > 1) + { + f_qsort( m_ppBlocksDone, + 0, uiSortedBlocks - 1, scaSortCompare, scaSortSwap); + } + + uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; + bForceCheckpoint = FALSE; + bWroteAll = TRUE; + + rc = writeSortedBlocks( pDbStats, pSFileHdl, 0, &uiDirtyCacheLeft, + &bForceCheckpoint, FALSE, bDoAsync, uiSortedBlocks, &bWroteAll); + + uiSortedBlocks = 0; + + if( RC_BAD( rc)) + { + goto Exit; + } + +Exit: + + while( uiSortedBlocks) + { + if( !bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + + uiSortedBlocks--; + + // Release any blocks we didn't process through. + + pSCache = m_ppBlocksDone[ uiSortedBlocks]; + pSCache->releaseForThread(); + } + + // Need to finish up any async writes. + + if( bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + if( bDoAsync) + { + // Wait for writes to complete. + + if( RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) + { + if( RC_OK( rc)) + { + rc = rc2; + } + } + } + + flmAssert( !m_pPendingWriteList); + + // Better not be any incomplete writes at this point. + + flmAssert( !m_pBufferMgr->havePendingIO()); + + // Don't keep around a large block array if we happened to + // allocate one that is bigger than our normal size. It may + // be huge because we were forcing a checkpoint. + + if( m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) + { + f_free( &m_ppBlocksDone); + m_uiBlocksDoneArraySize = 0; + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine writes new cache blocks to disk. The purpose of this + routine is to allow cache to be reduced as quickly as possible. + This is best accomplished by flushing blocks that are contiguous + before resorting to writing out non-contiguous blocks. The "new" + block list attempts to accomplish this by keeping an ordered list + of most of the blocks that have been created since the last checkpoint. + The list may not contain all of the new blocks if a transaction, which + modified blocks, was aborted since the last checkpoint completed. + In this case, the blocks would have been removed from the new list + when new versions of the blocks were created. Upon aborting, the + blocks that were originally in the new list are not put back into the + list because of the cost associated with finding their correct places + in the list. Even though these blocks aren't in the new list anymore, + they are still marked as being dirty and will written out eventually. +****************************************************************************/ +RCODE F_Database::reduceNewBlocks( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMUINT * puiBlocksFlushed) +{ + RCODE rc = NE_XFLM_OK; + RCODE rc2; + F_CachedBlock * pSCache; + FLMBOOL bDoAsync = FALSE; + FLMBOOL bMutexLocked = FALSE; + FLMUINT uiSortedBlocks = 0; + FLMUINT uiDirtyCacheLeft; + FLMUINT uiBlocksFlushed = 0; + + flmAssert( !m_uiLogCacheCount); + if (m_pCPInfo) + { + lockMutex(); + m_pCPInfo->bWritingDataBlocks = TRUE; + unlockMutex(); + } + + // See if we can do async IO. + + bDoAsync = (gv_XFlmSysData.bOkToDoAsyncWrites && pSFileHdl->CanDoAsync()) + ? TRUE + : FALSE; + + flmAssert( !m_pPendingWriteList); + uiDirtyCacheLeft = m_uiDirtyCacheCount * m_uiBlockSize; + + if (m_uiBlocksDoneArraySize < MAX_BLOCKS_TO_SORT) + { + if (RC_BAD( rc = allocBlocksArray( MAX_BLOCKS_TO_SORT, TRUE))) + { + // If the array size is non-zero, but we were unable to allocate + // the size we wanted, we'll just be content to output as many + // blocks as possible with the existing size of the array + + if( rc == NE_XFLM_MEM && m_uiBlocksDoneArraySize) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + } + + // Create a list of blocks to write out + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + pSCache = m_pFirstInNewList; + uiSortedBlocks = 0; + for (;;) + { + FLMUINT uiPrevBlkAddress; + + if (!pSCache || uiSortedBlocks == m_uiBlocksDoneArraySize) + { + break; + } + + flmAssert( !(pSCache->m_ui16Flags & CA_WRITE_PENDING)); + flmAssert( pSCache->m_ui16Flags & (CA_DIRTY | CA_IN_NEW_LIST)); + + uiPrevBlkAddress = pSCache->getPriorImageAddress(); + + // Skip blocks that are write inhibited + + if( pSCache->m_ui16Flags & CA_WRITE_INHIBIT) + { + pSCache = pSCache->m_pNextInReplaceList; + continue; + } + + // Keep list of blocks to process + + m_ppBlocksDone [uiSortedBlocks++] = pSCache; + + // Must use to keep from going away. + + pSCache->useForThread( 0); + pSCache = pSCache->m_pNextInReplaceList; + } + + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + if (uiSortedBlocks) + { + FLMBOOL bForceCheckpoint = FALSE; + FLMBOOL bDummy; + + rc = writeSortedBlocks( pDbStats, pSFileHdl, + ~((FLMUINT)0), &uiDirtyCacheLeft, + &bForceCheckpoint, FALSE, + bDoAsync, uiSortedBlocks, &bDummy); + + if( RC_OK( rc)) + { + uiBlocksFlushed += uiSortedBlocks; + } + } + else + { + goto Exit; + } + + // Set to zero so won't get released at exit. + + uiSortedBlocks = 0; + +Exit: + + // Release any blocks that are still used. + + while (uiSortedBlocks) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + uiSortedBlocks--; + + // Release any blocks we didn't process through. + + pSCache = m_ppBlocksDone [uiSortedBlocks]; + +#ifdef FLM_DEBUG + if( RC_OK( rc)) + { + flmAssert( !(pSCache->m_ui16Flags & CA_IN_NEW_LIST)); + } +#endif + + pSCache->releaseForThread(); + } + + // Need to finish up any async writes. + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + } + + if (bDoAsync) + { + + // Wait for writes to complete. + + if (RC_BAD( rc2 = m_pBufferMgr->waitForAllPendingIO())) + { + if (RC_OK( rc)) + { + rc = rc2; + } + } + } + + flmAssert( !m_pPendingWriteList); + + // Better not be any incomplete writes at this point. + + flmAssert( !m_pBufferMgr->havePendingIO()); + + // Don't keep around a large block array if we happened to + // allocate one that is bigger than our normal size. It may + // be huge because we were forcing a checkpoint. + + if (m_uiBlocksDoneArraySize > MAX_BLOCKS_TO_SORT) + { + f_free( &m_ppBlocksDone); + m_uiBlocksDoneArraySize = 0; + } + + if (puiBlocksFlushed) + { + *puiBlocksFlushed = uiBlocksFlushed; + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine is called to determine if a cache block or cache record + is still needed. +****************************************************************************/ +FLMBOOL F_Database::neededByReadTrans( + FLMUINT64 ui64LowTransId, + FLMUINT64 ui64HighTransId) +{ + FLMBOOL bNeeded = FALSE; + F_Db * pReadTrans; + + lockMutex(); + + // Quick check - so we don't have to traverse all read transactions. + + if (!m_pFirstReadTrans || + ui64HighTransId < m_pFirstReadTrans->m_ui64CurrTransID || + ui64LowTransId > m_pLastReadTrans->m_ui64CurrTransID) + { + goto Exit; + } + + // Traverse all read transactions - this loop assumes that the + // read transactions are in order of when they started - meaning + // that the ui64CurrTransID on each will be ascending order. The + // loop will quit early once it can detect that the block is + // too old for all remaining transactions. + + pReadTrans = m_pFirstReadTrans; + while (pReadTrans) + { + if (pReadTrans->m_ui64CurrTransID >= ui64LowTransId && + pReadTrans->m_ui64CurrTransID <= ui64HighTransId) + { + bNeeded = TRUE; + goto Exit; + } + else if (pReadTrans->m_ui64CurrTransID > ui64HighTransId) + { + // All remaining transaction's transaction IDs will + // also be greater than the block's high trans ID + // Therefore, we can quit here. + + goto Exit; + } + pReadTrans = pReadTrans->m_pNextReadTrans; + } + +Exit: + + unlockMutex(); + return( bNeeded); +} + +/**************************************************************************** +Desc: This routine is called just after a transaction has successfully + committed. It will unset the flags on log blocks + that would cause them to be written to disk. If the block is no longer + needed by a read transaction, it will also put the block in the + LRU list so it will be selected for replacement first. +****************************************************************************/ +void F_Database::releaseLogBlocks( void) +{ + F_CachedBlock * pSCache; + F_CachedBlock * pNextSCache; + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + pSCache = m_pTransLogList; + while (pSCache) + { + +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags = pSCache->m_ui16Flags; +#endif + + // A block in this list should never be dirty. + + flmAssert( !(pSCache->m_ui16Flags & CA_DIRTY)); + if ((pSCache->m_ui16Flags & CA_WRITE_TO_LOG) && + !(pSCache->m_ui16Flags & CA_LOG_FOR_CP)) + { + flmAssert( m_uiLogCacheCount); + m_uiLogCacheCount--; + } + + pSCache->clearFlags( CA_WRITE_TO_LOG | CA_WAS_DIRTY); + +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'I'); +#endif + pNextSCache = pSCache->m_pNextInHashBucket; + + // Perhaps we don't really need to set these pointers to NULL, + // but it helps keep things clean. + +#ifdef FLM_CACHE_PROTECT + pSCache->unprotectCachedItem(); +#endif + pSCache->m_pNextInHashBucket = NULL; + pSCache->m_pPrevInHashBucket = NULL; +#ifdef FLM_CACHE_PROTECT + pSCache->protectCachedItem(); +#endif + + // If the block is no longer needed by a read transaction, + // and it does not need to be logged for the checkpoint, + // move it to the free list. + + if ((!pSCache->m_uiUseCount) && + (!pSCache->neededByReadTrans()) && + (!(pSCache->m_ui16Flags & CA_LOG_FOR_CP))) + { + F_CachedBlock * pNewerVer = pSCache->m_pPrevInVersionList; + + if( !pSCache->m_pNextInVersionList && pNewerVer && + pNewerVer->m_ui64HighTransID == ~((FLMUINT64)0) && + pNewerVer->m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + pNewerVer->unlinkFromLogList(); + } + + pSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + + pSCache = pNextSCache; + } + m_pTransLogList = NULL; + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: Retrieve a data block. Shared cache is searched first. If the block + is not in shared cache, it will be retrieved from disk and put into + cache. The use count on the block will be incremented. +****************************************************************************/ +RCODE F_Database::getBlock( + F_Db * pDb, + LFILE * pLFile, // Pointer to logical file structure + // We are retrieving the block for. + // NULL if there is no logical file. + FLMUINT uiBlkAddress, // Address of requested block. + FLMUINT * puiNumLooksRV, // Pointer to FLMUINT where number of + // cache lookups is to be returned. + // If pointer is non-NULL it indicates + // that we only want to find the block + // if it is in cache. If it is NOT + // in cache, do NOT read it in from + // disk. -- This capability is needed + // by the FlmDbReduceSize function. + F_CachedBlock ** ppSCacheRV) // Returns pointer to cache block. +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bMutexLocked = FALSE; + FLMUINT64 ui64BlkVersion; + FLMUINT uiNumLooks; + F_CachedBlock ** ppSCacheBucket; + F_CachedBlock * pSBlkVerCache; + F_CachedBlock * pSMoreRecentVerCache; + F_CachedBlock * pSCache; + FLMBOOL bGotFromDisk = FALSE; + + flmAssert( this == pDb->m_pDatabase); + flmAssert( uiBlkAddress != 0); + + *ppSCacheRV = NULL; + + // We should NEVER be attempting to read a block address that is + // beyond the current logical end of file. + + if (!FSAddrIsBelow( uiBlkAddress, pDb->m_uiLogicalEOF)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + goto Exit; + } + + // Release CPU to prevent CPU hog + + f_yieldCPU(); + + // Lock the mutex + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + pDb->m_uiInactiveTime = 0; + + // Search shared cache for the desired version of the block. + // First, determine the hash bucket. + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_uiSigBitsInBlkSize, uiBlkAddress); + + // Search down the linked list of F_CachedBlock objects off of the bucket + // looking for the correct cache block. + + pSCache = *ppSCacheBucket; + uiNumLooks = 1; + while ((pSCache) && + (pSCache->m_uiBlkAddress != uiBlkAddress || + pSCache->m_pDatabase != this)) + { + if ((pSCache = pSCache->m_pNextInHashBucket) != NULL) + { + uiNumLooks++; + } + } + + // If there was no block found with the appropriate file/address we need to + // create a dummy block and attempt to read it in. + + if (!pSCache) + { + if (puiNumLooksRV) + { + *puiNumLooksRV = uiNumLooks; + *ppSCacheRV = NULL; + goto Exit; + } + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults++; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks += uiNumLooks; + if (RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, + NULL, NULL, &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + else + { + // A block with the appropriate file/address was found. We now + // need to follow the chain until we find the version of the + // block that we need. + + pSMoreRecentVerCache = NULL; + + // Save pointer to block that is newest version. + + pSBlkVerCache = pSCache; + ui64BlkVersion = pDb->m_ui64CurrTransID; + + for (;;) + { + + // If the block is being read into memory, wait for the read + // to complete so we can see what it is. + + if (pSCache && (pSCache->m_ui16Flags & CA_READ_PENDING)) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiIoWaits++; + if (RC_BAD( rc = flmWaitNotifyReq( + gv_XFlmSysData.hBlockCacheMutex, pDb->m_hWaitSem, + &pSCache->m_pNotifyList, (void *)&pSCache))) + { + goto Exit; + } + + // The thread doing the notify "uses" the cache block + // on behalf of this thread to prevent the cache block + // from being flushed after it unlocks the mutex. + // At this point, since we have locked the mutex, + // we need to release the cache block. + + pSCache->releaseForThread(); + + // Start over at the top of the list. + + pSBlkVerCache = pSCache; + while (pSBlkVerCache->m_pPrevInVersionList) + { + pSBlkVerCache = pSBlkVerCache->m_pPrevInVersionList; + } + pSCache = pSBlkVerCache; + pSMoreRecentVerCache = NULL; + continue; + } + + if (!pSCache || ui64BlkVersion > pSCache->m_ui64HighTransID) + { + if (puiNumLooksRV) + { + *puiNumLooksRV = uiNumLooks; + *ppSCacheRV = NULL; + goto Exit; + } + + // The version of the block we want is not in the list, + // either because we are at the end of the list (!pSCache), + // or because the block version we want is higher than + // the high trans ID on the cache block we are looking + // at. See if there is anything on disk that comes after + // that block. If not, simply return an OLD_VIEW + // error. + + if (pSMoreRecentVerCache && + pSMoreRecentVerCache->getPriorImageAddress() == 0) + { + // Should only be possible when reading a root block, + // because the root block address in the LFILE may be + // a block that was just created by an update + // transaction. + + flmAssert( pDb->m_uiKilledTime); + rc = RC_SET( NE_XFLM_OLD_VIEW); + goto Exit; + } + + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaults++; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheFaultLooks += uiNumLooks; + + if (pSMoreRecentVerCache) + { + if( RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, + pSMoreRecentVerCache, + pSMoreRecentVerCache->m_pNextInVersionList, + &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = readIntoCache( pDb, pLFile, uiBlkAddress, + NULL, pSBlkVerCache, &pSCache, &bGotFromDisk))) + { + goto Exit; + } + } + + // At this point, if the read was successful, we should + // have the block we want. + + break; + } + else if (ui64BlkVersion >= pSCache->getLowTransID()) + { + + // This is the version of the block that we need. + + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHits++; + gv_XFlmSysData.pBlockCacheMgr->m_Usage.uiCacheHitLooks += uiNumLooks; + break; + } + else + { + // If we are in an update transaction, the version of the + // block we want should ALWAYS be at the top of the list. + // If not, we have a serious problem! + + flmAssert( pDb->m_eTransType != XFLM_UPDATE_TRANS); + + pSMoreRecentVerCache = pSCache; + pSCache = pSCache->m_pNextInVersionList; + + if (pSCache) + { + uiNumLooks++; + } + } + } + } + + // Increment the use count on the block. + + pSCache->useForThread( 0); + + // Block was found, make it the MRU block or bump it up in the MRU list, + // if it is not already at the top. + + if( pDb->m_uiFlags & FDB_DONT_POISON_CACHE) + { + if (!(pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) || + (pLFile && pLFile->eLfType != XFLM_LF_INDEX)) + { + if (!bGotFromDisk) + { + pSCache->stepUpInGlobalList(); + } + + // If the block was read from disk and FDB_DONT_POISION_CACHE is + // set, we don't need to do anything because the block is + // already linked at the LRU position. + } + else if (pSCache->m_pPrevInGlobal) + { + pSCache->unlinkFromGlobalList(); + pSCache->linkToGlobalListAsMRU(); + } + } + else if (pSCache->m_pPrevInGlobal) + { + pSCache->unlinkFromGlobalList(); + pSCache->linkToGlobalListAsMRU(); + } + + *ppSCacheRV = pSCache; + +Exit: + +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + scaVerify( 300); + } +#endif + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + return( rc); +} + +/**************************************************************************** +Desc: Create a data block. +****************************************************************************/ +RCODE F_Database::createBlock( + F_Db * pDb, + F_CachedBlock ** ppSCacheRV) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBlkAddress; + F_BLK_HDR * pBlkHdr; + F_CachedBlock * pSCache; + F_CachedBlock * pOldSCache; + FLMBOOL bMutexLocked = FALSE; + FLMBOOL bLocalCacheAllocation = FALSE; + FLMUINT uiOldLogicalEOF; + F_CachedBlock ** ppSCacheBucket; + FLMUINT uiBlockSize = pDb->m_pDatabase->getBlockSize(); + + pDb->m_bHadUpdOper = TRUE; + + // First see if there is a free block in the avail list. + + if (pDb->m_uiFirstAvailBlkAddr) + { + rc = blockUseNextAvail( pDb, ppSCacheRV); + goto Exit; + } + + // See if we need to free any cache or write dirty cache + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( pDb))) + { + goto Exit; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = FALSE; + + // Create a new block at EOF + + uiBlkAddress = pDb->m_uiLogicalEOF; + + // Time for a new block file? + + if (FSGetFileOffset(uiBlkAddress) >= m_uiMaxFileSize) + { + FLMUINT uiFileNumber = FSGetFileNumber( uiBlkAddress) + 1; + + if (uiFileNumber > MAX_DATA_BLOCK_FILE_NUMBER) + { + rc = RC_SET( NE_XFLM_DB_FULL); + goto Exit; + } + + if (RC_BAD( rc = pDb->m_pSFileHdl->CreateFile( uiFileNumber))) + { + goto Exit; + } + uiBlkAddress = FSBlkAddress( uiFileNumber, 0 ); + } + + // Allocate a cache block for this new block. If we have older + // versions of this block already in cache, we need to link the + // new block above the older version. If reasonable, try to + // allocate the new cache block without locking the mutex. + + if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.lockMutex(); + + if( (pSCache = new( uiBlockSize, TRUE) F_CachedBlock( uiBlockSize)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + pSCache->unprotectCachedItem(); +#endif + pSCache->m_uiUseCount++; +#ifdef FLM_CACHE_PROTECT + pSCache->protectCachedItem(); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.unlockMutex(); + bLocalCacheAllocation = TRUE; + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + + // Determine the hash bucket the new block should be put into. + + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_uiSigBitsInBlkSize, uiBlkAddress); + + // Search down the linked list of F_CachedBlock objects off of the bucket + // looking for an older version of the block. If there are older + // versions, we should get rid of them. + + pOldSCache = *ppSCacheBucket; + while (pOldSCache && + (pOldSCache->m_uiBlkAddress != uiBlkAddress || + pOldSCache->m_pDatabase != this)) + { + pOldSCache = pOldSCache->m_pNextInHashBucket; + } + + while (pOldSCache) + { + F_CachedBlock * pNextSCache = pOldSCache->m_pNextInVersionList; + + // Older versions of blocks should not be in use or needed + // by anyone because the only we we would have an older + // version of a block beyond the logical EOF is if + // FlmDbReduceSize had been called. But it forces a + // checkpoint that requires any read transactions to be + // non-active, or killed. + + flmAssert( !pOldSCache->m_ui16Flags); + flmAssert( !pOldSCache->m_uiUseCount); + flmAssert( pOldSCache->m_ui64HighTransID == ~((FLMUINT64)0) || + !pOldSCache->neededByReadTrans()); + pOldSCache->unlinkCache( TRUE, NE_XFLM_OK); + pOldSCache = pNextSCache; + } + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + if( bLocalCacheAllocation) + { + F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; + + // Now that the mutex is locked, update stats and do other work + // that couldn't be done when the block was allocated. + + pBlockCacheMgr->m_Usage.uiCount++; + pBlockCacheMgr->m_Usage.uiByteCount += pSCache->memSize(); + + // Set use count to one so the block cannot be replaced. + +#ifdef FLM_CACHE_PROTECT + pSCache->unprotectCachedItem(); +#endif + pSCache->m_uiUseCount--; +#ifdef FLM_CACHE_PROTECT + pSCache->protectCachedItem(); +#endif + pSCache->useForThread( 0); + } + else + { + if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( pDb, &pSCache))) + { + goto Exit; + } + } + + pSCache->m_uiBlkAddress = uiBlkAddress; + pSCache->setTransID( ~((FLMUINT64)0)); + + // Initialize the block data, dirty flag is set so that it will be + // flushed as needed. + + pBlkHdr = pSCache->m_pBlkHdr; + f_memset( pBlkHdr, 0, m_uiBlockSize); + pBlkHdr->ui32BlkAddr = (FLMUINT32)uiBlkAddress; + pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; + pBlkHdr->ui16BlkBytesAvail = + (FLMUINT16)(m_uiBlockSize - SIZEOF_STD_BLK_HDR); + blkSetNativeFormat( pBlkHdr); + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( this, pSCache->m_uiBlkAddress, 0, + pSCache->getLowTransID(), + "CREATE"); +#endif + + // Link block into the global list + + pSCache->m_ui16Flags |= CA_DUMMY_FLAG; + pSCache->linkToGlobalListAsMRU(); + + // Set the dirty flag + + pSCache->setDirtyFlag( this); + pSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + pSCache->setFlags( CA_WRITE_INHIBIT); + +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( 0, 'J'); +#endif + + // Now that the dirty flag and write inhibit flag + // have been set, link the block to the file + + pSCache->linkToDatabase( this); + pSCache->linkToHashBucket( ppSCacheBucket); + + uiOldLogicalEOF = pDb->m_uiLogicalEOF; + pDb->m_uiLogicalEOF = uiBlkAddress + m_uiBlockSize; + + // Link the block into the "new" list + + pSCache->linkToNewList(); + + // Return a pointer to the block + + *ppSCacheRV = pSCache; + +Exit: + +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bMutexLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bMutexLocked = TRUE; + } + scaVerify( 400); + } +#endif + + if (bMutexLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + if (RC_BAD( rc)) + { + *ppSCacheRV = NULL; + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: This routine releases a cache block by decrementing its use count. + If the use count goes to zero, the block will be moved to the MRU + position in the global cache list. +****************************************************************************/ +void ScaReleaseCache( + F_CachedBlock * pSCache, + FLMBOOL bMutexAlreadyLocked) +{ + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + } + + // Can turn off write inhibit when the use count is about to go to zero, + // because we are guaranteed at that point that nobody is going to still + // update it. + + if (pSCache->getUseCount() == 1) + { + pSCache->clearFlags( CA_WRITE_INHIBIT); + } + + pSCache->releaseForThread(); + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } +} + +/**************************************************************************** +Desc: This routine increments the use count on a cache block +****************************************************************************/ +void ScaUseCache( + F_CachedBlock * pSCache, + FLMBOOL bMutexAlreadyLocked) +{ + if (!bMutexAlreadyLocked) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + } + + pSCache->useForThread( 0); + + if (!bMutexAlreadyLocked) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } +} + +/**************************************************************************** +Desc: Test and set the dirty flag on a block if not already set. This is + called on the case where a block didn't need to be logged because + it had already been logged, but it still needs to have its dirty + bit set. +****************************************************************************/ +void F_Database::setBlkDirty( + F_CachedBlock * pSCache) +{ +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + // If the dirty flag is already set, we will NOT attempt to set it. + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->m_ui16Flags; +#endif + + if (!(pSCache->m_ui16Flags & CA_DIRTY)) + { + flmAssert( this == pSCache->m_pDatabase); + pSCache->setDirtyFlag( this); + } + + // Move the block into the dirty blocks. Even if block was + // already dirty, put at the end of the list of dirty blocks. + // This will make it so that when reduceCache hits a dirty + // block, it is likely to also be one that will be written + // out by a call to ScaFlushDirtyBlocks. + + pSCache->unlinkFromDatabase(); + pSCache->linkToDatabase( this); + + // Move the block to the MRU slot in the global list + + if (pSCache->m_pPrevInGlobal) + { + pSCache->unlinkFromGlobalList(); + pSCache->linkToGlobalListAsMRU(); + } + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + pSCache->setFlags( CA_WRITE_INHIBIT); +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'O'); +#endif + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + +/**************************************************************************** +Desc: This routine logs a block before it is modified. In the shared cache + system, the block is cloned. The old version of the block is marked + so that it will be written to the rollback log before the new version + of the block can be written to disk. + NOTE: It is assumed that the caller has "used" the block that is passed + in. This routine will release it once we have made a copy of the block. +****************************************************************************/ +RCODE F_Database::logPhysBlk( + F_Db * pDb, + F_CachedBlock ** ppSCacheRV, // This is a pointer to the pointer of the + // cache block that is to be logged. + // If the block has not been logged before + // during the transaction, a new version + // of the block will be created and a + // pointer to that block will be returned. + // Otherwise, the pointer is unchanged. + F_CachedBlock ** ppOldSCache) +{ + RCODE rc = NE_XFLM_OK; + F_CachedBlock * pSCache = *ppSCacheRV; + F_BLK_HDR * pBlkHdr = pSCache->m_pBlkHdr; + F_CachedBlock * pNewSCache; + FLMBOOL bLockedMutex = FALSE; + FLMBOOL bLocalCacheAllocation = FALSE; + FLMUINT uiBlockSize = getBlockSize(); + F_CachedBlock ** ppSCacheBucket; +#ifdef FLM_DBG_LOG + FLMUINT16 ui16OldFlags; +#endif + + flmAssert( this == pDb->m_pDatabase); + flmAssert( pSCache->m_pPrevInVersionList == NULL); + + if( ppOldSCache) + { + *ppOldSCache = NULL; + } + + // Increment the block change count -- this is not an accurate + // indication of the number of blocks that have actually changed. The + // count is used by the cursor code to determine when to re-position in + // the B-Tree. The value is only used by cursors operating within + // an update transaction. + + pDb->m_uiBlkChangeCnt++; + + // See if the block has already been logged since the last transaction. + // If so, there is no need to log it again. + + if( pBlkHdr->ui64TransID == pDb->m_ui64CurrTransID) + { + flmAssert( pDb->m_bHadUpdOper); + setBlkDirty( pSCache); + goto Exit; + } + pDb->m_bHadUpdOper = TRUE; + + // See if we need to free any cache or write dirty cache + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bLockedMutex = TRUE; + if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->reduceCache( pDb))) + { + goto Exit; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + bLockedMutex = FALSE; + + // See if the transaction ID is greater than the last backup + // transaction ID. If so, we need to update our block change + // count. + + if (pBlkHdr->ui64TransID < m_uncommittedDbHdr.ui64LastBackupTransID) + { + m_uncommittedDbHdr.ui32BlksChangedSinceBackup++; + } + + // pDb->m_uiTransEOF contains what the EOF address was at + // the beginning of the transaction. There is no need to log the + // block if it's address is beyond that point because it is a + // NEW block. + + if (!FSAddrIsBelow( (FLMUINT)pSCache->m_pBlkHdr->ui32BlkAddr, + pDb->m_uiTransEOF)) + { + pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; + setBlkDirty( pSCache); + goto Exit; + } + + // Allocate a cache block for this new block. If we have older + // versions of this block already in cache, we need to link the + // new block above the older version. Try to allocate the new + // block outside of the block cache mutex. If not possible, lock + // the mutex and allocate. + + if( !gv_XFlmSysData.pGlobalCacheMgr->cacheOverLimit()) + { + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.lockMutex(); + + if( (pNewSCache = new( uiBlockSize, TRUE) F_CachedBlock( uiBlockSize)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + +#ifdef FLM_CACHE_PROTECT + pNewSCache->unprotectCachedItem(); +#endif + pNewSCache->m_uiUseCount++; +#ifdef FLM_CACHE_PROTECT + pNewSCache->protectCachedItem(); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.unlockMutex(); + bLocalCacheAllocation = TRUE; + + // Copy the old block's data into this one. + + pBlkHdr = pNewSCache->m_pBlkHdr; +#ifdef FLM_CACHE_PROTECT + pNewSCache->unprotectCachedItem(); +#endif + f_memcpy( pBlkHdr, pSCache->m_pBlkHdr, m_uiBlockSize); +#ifdef FLM_CACHE_PROTECT + pNewSCache->protectCachedItem(); +#endif + } + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bLockedMutex = TRUE; + + // Allocate a cache block - either a new one or by replacing + // an existing one. + + if( bLocalCacheAllocation) + { + F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; + + // Now that the mutex is locked, update stats and do other work + // that couldn't be done when the block was allocated. + + pBlockCacheMgr->m_Usage.uiCount++; + pBlockCacheMgr->m_Usage.uiByteCount += pNewSCache->memSize(); + + // Set use count to one so the block cannot be replaced. + +#ifdef FLM_CACHE_PROTECT + pNewSCache->unprotectCachedItem(); +#endif + pNewSCache->m_uiUseCount--; +#ifdef FLM_CACHE_PROTECT + pNewSCache->protectCachedItem(); +#endif + pNewSCache->useForThread( 0); + } + else + { + if (RC_BAD( rc = gv_XFlmSysData.pBlockCacheMgr->allocBlock( + pDb, &pNewSCache))) + { + goto Exit; + } + + // Copy the old block's data into this one. + + pBlkHdr = pNewSCache->m_pBlkHdr; +#ifdef FLM_CACHE_PROTECT + pNewSCache->unprotectCachedItem(); +#endif + f_memcpy( pBlkHdr, pSCache->m_pBlkHdr, m_uiBlockSize); +#ifdef FLM_CACHE_PROTECT + pNewSCache->protectCachedItem(); +#endif + } + +#ifdef FLM_DEBUG + + // Make sure the caller isn't logging one that has already been + // logged. + + if (gv_XFlmSysData.pBlockCacheMgr->m_bDebug) + { + F_CachedBlock * pTmpSCache = m_pTransLogList; + + while (pTmpSCache) + { + flmAssert( pTmpSCache != pSCache); + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + } +#endif + + pNewSCache->m_uiBlkAddress = pSCache->m_uiBlkAddress; + +#ifdef FLM_DBG_LOG + flmDbgLogWrite( this, + (FLMUINT)pNewSCache->m_pBlkHdr->ui32BlkAddr, 0, + pDb->m_ui64CurrTransID, + "NEW-VER"); +#endif + + // Link the block to the global list + + pNewSCache->m_ui16Flags |= CA_DUMMY_FLAG; + pNewSCache->linkToGlobalListAsMRU(); + + // Set flags so that appropriate flushing to log and DB will be done. + + pNewSCache->setDirtyFlag( this); + pNewSCache->m_ui16Flags &= ~CA_DUMMY_FLAG; + + // Set write inhibit bit so we will not unset the dirty bit + // until the use count goes to zero. + + pNewSCache->setFlags( CA_WRITE_INHIBIT); + + // Previous block address should be zero until we actually log the + // prior version of the block. + + pBlkHdr->ui32PriorBlkImgAddr = 0; + + // Set the low and high trans IDs on the newly created block. + + pNewSCache->setTransID( ~((FLMUINT64)0)); + pBlkHdr->ui64TransID = pDb->m_ui64CurrTransID; +#ifdef FLM_DBG_LOG + pNewSCache->logFlgChange( 0, 'L'); +#endif + + // Determine the hash bucket the new block should be put into. + + flmAssert( pSCache->m_uiBlkAddress); + ppSCacheBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + m_uiSigBitsInBlkSize, pSCache->m_uiBlkAddress); + + // Link new block into various lists. + + pSCache->unlinkFromHashBucket( ppSCacheBucket); + pSCache->m_pPrevInVersionList = pNewSCache; + pSCache->verifyCache( 2900); + pNewSCache->m_pNextInVersionList = pSCache; + pNewSCache->linkToDatabase( this); + pNewSCache->linkToHashBucket( ppSCacheBucket); + pNewSCache->verifyCache( 3000); + + // Set the high trans ID on the old block to be one less than + // the current trans ID. Also set the flag indicating that + // the block needs to be written to the rollback log. + + pSCache->setTransID( (pDb->m_ui64CurrTransID - 1)); +#ifdef FLM_DBG_LOG + ui16OldFlags = pSCache->m_ui16Flags; +#endif + + if (!(pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) + { + m_uiLogCacheCount++; + } + + pSCache->setFlags( CA_WRITE_TO_LOG); + + if (pSCache->getLowTransID() <= + m_uncommittedDbHdr.ui64RflLastCPTransID) + { + pSCache->setFlags( CA_LOG_FOR_CP); + } + + if (pSCache->m_ui16Flags & CA_DIRTY) + { + pSCache->setFlags( CA_WAS_DIRTY); + flmAssert( this == pSCache->m_pDatabase); + pSCache->unsetDirtyFlag(); + + // No more need to write inhibit - because the old version of the + // block cannot possibly be changed. + + pSCache->clearFlags( CA_WRITE_INHIBIT); + + // Move the block out of the dirty blocks. + + pSCache->unlinkFromDatabase(); + pSCache->linkToDatabase( this); + } + +#ifdef FLM_DBG_LOG + pSCache->logFlgChange( ui16OldFlags, 'N'); +#endif + + // Put the old block into the list of the transaction's + // log blocks + + pSCache->m_pPrevInHashBucket = NULL; + if ((pSCache->m_pNextInHashBucket = m_pTransLogList) != NULL) + { +#ifdef FLM_CACHE_PROTECT + pSCache->m_pNextInHashBucket->unprotectCachedItem(); +#endif + pSCache->m_pNextInHashBucket->m_pPrevInHashBucket = pSCache; +#ifdef FLM_CACHE_PROTECT + pSCache->m_pNextInHashBucket->protectCachedItem(); +#endif + } + m_pTransLogList = pSCache; + + // Link the new block to the file log list + + pNewSCache->linkToLogList(); + + // If this is an indexing thread, the old version of the + // block will probably not be needed again so put it at the LRU end + // of the cache. The assumption is that a background indexing thread + // has also set the FDB_DONT_POISON_CACHE flag. + + if (pDb->m_uiFlags & FDB_BACKGROUND_INDEXING) + { + pSCache->unlinkFromGlobalList(); + pSCache->linkToGlobalListAsLRU(); + } + + // Release the old block and return a pointer to the new block. + + if( !ppOldSCache) + { + ScaReleaseCache( pSCache, bLockedMutex); + } + else + { + *ppOldSCache = pSCache; + } + + *ppSCacheRV = pNewSCache; + +Exit: +#ifdef SCACHE_LINK_CHECKING + if (RC_BAD( rc)) + { + if (!bLockedMutex) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + bLockedMutex = TRUE; + } + scaVerify( 500); + } +#endif + + if (bLockedMutex) + { + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + if( RC_BAD( rc)) + { + pDb->setMustAbortTrans( rc); + } + + return( rc); +} + +/**************************************************************************** +Desc: Constructor for block cache manager. +****************************************************************************/ +F_BlockCacheMgr::F_BlockCacheMgr() +{ + m_pMRUReplace = NULL; + m_pLRUReplace = NULL; + m_pFirstFree = NULL; + m_pLastFree = NULL; + m_ppHashBuckets = NULL; + m_uiNumBuckets = 0; + m_uiHashFailTime = 0; + f_memset( &m_Usage, 0, sizeof( m_Usage)); + m_uiFreeBytes = 0; + m_uiFreeCount = 0; + m_uiReplaceableCount = 0; + m_uiReplaceableBytes = 0; + m_bAutoCalcMaxDirty = FALSE; + m_uiMaxDirtyCache = 0; + m_uiLowDirtyCache = 0; + m_uiTotalUses = 0; + m_uiBlocksUsed = 0; + m_uiPendingReads = 0; + m_uiIoWaits = 0; + m_uiHashMask = 0; + m_bReduceInProgress = FALSE; +#ifdef FLM_DEBUG + m_bDebug = FALSE; +#endif +} + +/**************************************************************************** +Desc: This routine initializes the hash table for block cache. +****************************************************************************/ +RCODE F_BlockCacheMgr::initHashTbl( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiAllocSize; + + // Calculate the number of bits needed to represent values in the + // hash table. + + m_uiNumBuckets = MIN_HASH_BUCKETS; + m_uiHashMask = (m_uiNumBuckets - 1); + uiAllocSize = (FLMUINT)sizeof( F_CachedBlock *) * m_uiNumBuckets; + if (RC_BAD( rc = f_calloc( uiAllocSize, &m_ppHashBuckets))) + { + goto Exit; + } + gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine initializes the block cache manager. +****************************************************************************/ +RCODE F_BlockCacheMgr::initCache( void) +{ + RCODE rc = NE_XFLM_OK; +#define MAX_NUM_BLOCK_SIZES 16 + FLMUINT uiBlockSizes[ MAX_NUM_BLOCK_SIZES]; + FLMUINT uiBlockSize; + FLMUINT uiLoop; + + // Allocate memory for the hash table. + + if (RC_BAD( rc = initHashTbl())) + { + goto Exit; + } + + // Initialize the cache block allocator + + for( uiLoop = 0, uiBlockSize = XFLM_MIN_BLOCK_SIZE; + uiBlockSize <= XFLM_MAX_BLOCK_SIZE; + uiLoop++, uiBlockSize *= 2) + { + if( uiLoop >= MAX_NUM_BLOCK_SIZES) + { + rc = RC_SET_AND_ASSERT( NE_XFLM_MEM); + goto Exit; + } + + uiBlockSizes[ uiLoop] = uiBlockSize + sizeof( F_CachedBlock); + } + + uiBlockSizes[ uiLoop] = 0; + + if (RC_BAD( rc = m_blockAllocator.setup( + gv_XFlmSysData.pGlobalCacheMgr->m_pSlabManager, TRUE, uiBlockSizes, + &m_Usage.slabUsage))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine resizes the hash table for the block cache manager. + NOTE: This routine assumes that the cache block mutex has been locked. +****************************************************************************/ +RCODE F_BlockCacheMgr::rehash( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiNewHashTblSize; + F_CachedBlock ** ppOldHashTbl; + FLMUINT uiOldHashTblSize; + F_CachedBlock ** ppBucket; + F_CachedBlock ** ppSCacheBucket; + FLMUINT uiLoop; + F_CachedBlock * pTmpSCache; + F_CachedBlock * pTmpNextSCache; + FLMUINT uiOldMemSize; + + uiNewHashTblSize = caGetBestHashTblSize( m_Usage.uiCount); + + // At this point we better have a different hash table size + // or something is mucked up! + + flmAssert( uiNewHashTblSize != m_uiNumBuckets); + + // Save the old hash table and its size. + + if ((ppOldHashTbl = m_ppHashBuckets) != NULL) + { + uiOldMemSize = f_msize( ppOldHashTbl); + } + else + { + uiOldMemSize = 0; + } + uiOldHashTblSize = m_uiNumBuckets; + + // Allocate a new hash table. + + if (RC_BAD( rc = f_calloc( (FLMUINT)sizeof( F_CachedBlock *) * + (FLMUINT)uiNewHashTblSize, &m_ppHashBuckets))) + { + m_uiHashFailTime = FLM_GET_TIMER(); + m_ppHashBuckets = ppOldHashTbl; + goto Exit; + } + + // Subtract off old size and add in new size. + + gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( uiOldMemSize); + gv_XFlmSysData.pGlobalCacheMgr->incrTotalBytes( f_msize( m_ppHashBuckets)); + + m_uiNumBuckets = uiNewHashTblSize; + m_uiHashMask = uiNewHashTblSize - 1; + + // Relink all of the cache blocks into the new + // hash table. + + for (uiLoop = 0, ppBucket = ppOldHashTbl; + uiLoop < uiOldHashTblSize; + uiLoop++, ppBucket++) + { + pTmpSCache = *ppBucket; + while (pTmpSCache) + { + pTmpNextSCache = pTmpSCache->m_pNextInHashBucket; + + // Should not be anything in a hash bucket that is not + // associated with a database. + + flmAssert( pTmpSCache->m_pDatabase); + flmAssert( pTmpSCache->m_uiBlkAddress); + ppSCacheBucket = blockHash( + pTmpSCache->m_pDatabase->getSigBitsInBlkSize(), + pTmpSCache->m_uiBlkAddress); + + pTmpSCache->linkToHashBucket( ppSCacheBucket); + pTmpSCache = pTmpNextSCache; + } + } + + // Throw away the old hash table. + + f_free( &ppOldHashTbl); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine performs various checks on an individual cache block + to verify that it is linked into the proper lists, etc. This routine + assumes that the cache block mutex has been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +void F_CachedBlock::verifyCache( + int iPlace) +{ + F_CachedBLock * pTmpSCache; + FLMUINT uiTmp = getBlkSize(); + FLMUINT uiSigBitsInBlkSize; + F_CachedBlock ** ppBucket; + + uiSigBitsInBlkSize = calcSigBits( uiTmp); + ppBucket = gv_XFlmSysData.pBlockCacheMgr->blockHash( + uiSigBitsInBlkSize, m_uiBlkAddress); + pTmpSCache = *ppBucket; + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if( pTmpSCache) + { + if (!m_pDatabase) + { + f_breakpoint( iPlace+3); + } + + if (m_pPrevInVersionList) + { + f_breakpoint( iPlace+4); + } + + // Verify that it is not in the log list. + + if (m_ui16Flags & CA_WRITE_TO_LOG) + { + f_breakpoint( iPlace+5); + } + pTmpSCache = m_pDatabase->getTransLogList(); + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + if (pTmpSCache) + { + f_breakpoint( iPlace+6); + } + } + else + { + if (m_pDatabase && !m_pPrevInVersionList) + { + f_breakpoint( iPlace+7); + } + + // If the block is marked as needing to be logged, verify that + // it is in the log list. + + if (m_ui16Flags & CA_WRITE_TO_LOG) + { + pTmpSCache = m_pDatabase->getTransLogList(); + while (pTmpSCache && pTmpSCache != this) + { + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + + if (!pTmpSCache) + { + // Not in the log list + + f_breakpoint( iPlace+8); + } + + // Better also have a newer version. + + if (!m_pPrevInVersionList) + { + // Not linked to a prior version. + + f_breakpoint( iPlace+9); + } + } + } + + // Verify that the prev and next pointers do not point to itself. + + if (m_pPrevInVersionList == this) + { + f_breakpoint( iPlace+10); + } + + if (m_pNextInVersionList == this) + { + f_breakpoint( iPlace+11); + } +} +#endif + +/**************************************************************************** +Desc: This routine performs various checks on the cache to verify that + things are linked into the proper lists, etc. This routine assumes + that the cache block mutex has been locked. +****************************************************************************/ +#ifdef SCACHE_LINK_CHECKING +FSTATIC void scaVerify( + int iPlace) +{ + FLMUINT uiLoop; + F_CachedBlock ** ppBucket; + F_CachedBlock * pTmpSCache; + + // Verify that everything in buckets has a pFile and does NOT + // have a m_pPrevInVersionList + + for (uiLoop = 0, ppBucket = gv_XFlmSysData.pBlockCacheMgr->m_ppHashBuckets; + uiLoop < gv_XFlmSysData.pBlockCacheMgr->m_uiNumBuckets; + uiLoop++, ppBucket++) + { + pTmpSCache = *ppBucket; + while (pTmpSCache) + { + if (!pTmpSCache->m_pDatabase) + { + f_breakpoint(iPlace+1); + } + if (pTmpSCache->m_pPrevInVersionList) + { + f_breakpoint(iPlace+2); + } + pTmpSCache = pTmpSCache->m_pNextInHashBucket; + } + } + + // Traverse the entire list - make sure that everything + // with a file is hashed and linked properly + // and everything without a file is NOT hashed. + + pTmpSCache = (F_CachedBlock *)gv_XFlmSysData.pBlockCacheMgr->m_MRUList.m_pMRUItem; + while (pTmpSCache) + { + pTmpSCache->verifyCache( 1000 + iPlace); + pTmpSCache = pTmpSCache->m_pNextInGlobal; + } +} +#endif + +/**************************************************************************** +Desc: This routine determines what hash table size best fits the current + item count. It finds the hash bucket size whose midpoint between + the minimum and maximum range is closest to the node count. +****************************************************************************/ +FLMUINT caGetBestHashTblSize( + FLMUINT uiCurrItemCount + ) +{ + FLMUINT uiNumHashBuckets; + FLMUINT uiMaxItemsForNumHashBuckets; + FLMUINT uiMinItemsForNumHashBuckets; + FLMUINT uiClosestNumHashBuckets = 0; + FLMUINT uiDistanceFromMidpoint; + FLMUINT uiLowestDistanceFromMidpoint; + FLMUINT uiHashTblItemsMidpoint; + + uiLowestDistanceFromMidpoint = 0xFFFFFFFF; + for (uiNumHashBuckets = MIN_HASH_BUCKETS; + uiNumHashBuckets <= MAX_HASH_BUCKETS; + uiNumHashBuckets *= 2) + { + + // Maximum desirable record count for a specific hash table size + // we have arbitrarily chosen to be four times the number of buckets. + // Minimum desirable record count we have arbitrarily chosen to be + // the hash table size divided by fourn. + + uiMaxItemsForNumHashBuckets = maxItemCount( uiNumHashBuckets); + uiMinItemsForNumHashBuckets = minItemCount( uiNumHashBuckets); + + // Ignore any hash bucket sizes where the current record count + // is not between the desired minimum and maximum. + + if (uiCurrItemCount >= uiMinItemsForNumHashBuckets && + uiCurrItemCount <= uiMaxItemsForNumHashBuckets) + { + + // Calculate the midpoint between the minimum and maximum + // for this particular hash table size. + + uiHashTblItemsMidpoint = (uiMaxItemsForNumHashBuckets - + uiMinItemsForNumHashBuckets) / 2; + + // See how far our current record count is from this midpoint. + + uiDistanceFromMidpoint = (FLMUINT)((uiHashTblItemsMidpoint > uiCurrItemCount) + ? (uiHashTblItemsMidpoint - uiCurrItemCount) + : (uiCurrItemCount - uiHashTblItemsMidpoint)); + + // If the distance from the midpoint is closer than our previous + // lowest distance, save it. + + if (uiDistanceFromMidpoint < uiLowestDistanceFromMidpoint) + { + uiClosestNumHashBuckets = uiNumHashBuckets; + uiLowestDistanceFromMidpoint = uiDistanceFromMidpoint; + } + } + } + + // Take the number of buckets whose middle was closest to the + // current record count; + + if (uiLowestDistanceFromMidpoint == 0xFFFFFFFF) + { + // If we did not fall between any of the minimums or maximums, + // we are either below the lowest minimum, or higher than the + // highest maximum. + + uiNumHashBuckets = (FLMUINT)((uiCurrItemCount < minItemCount( MIN_HASH_BUCKETS)) + ? (FLMUINT)MIN_HASH_BUCKETS + : (FLMUINT)MAX_HASH_BUCKETS); + + } + else + { + uiNumHashBuckets = uiClosestNumHashBuckets; + } + return( uiNumHashBuckets); +} + +/**************************************************************************** +Desc: This routine shuts down the shared cache manager and frees all + resources allocated by it. +****************************************************************************/ +F_BlockCacheMgr::~F_BlockCacheMgr() +{ + + // Free the hash table + + if (m_ppHashBuckets) + { + gv_XFlmSysData.pGlobalCacheMgr->decrTotalBytes( f_msize( m_ppHashBuckets)); + f_free( &m_ppHashBuckets); + } + flmAssert( !m_MRUList.m_pMRUItem && !m_MRUList.m_pLRUItem); +} + +/**************************************************************************** +Desc: This routine frees all of the cache associated with an F_Database object. +****************************************************************************/ +void F_Database::freeBlockCache( void) +{ + F_CachedBlock * pSCache; + F_CachedBlock * pNextSCache; + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + // First, unlink as many as can be unlinked. + + pSCache = m_pSCacheList; + flmAssert( !m_pPendingWriteList); + while (pSCache) + { + f_yieldCPU(); + pNextSCache = pSCache->m_pNextInDatabase; + + if (!pSCache->m_uiUseCount) + { + // Turn off all bits that would cause an assert - we don't + // care at this point, because we are forcing the file to + // be closed. + + if (pSCache->m_ui16Flags & (CA_WRITE_TO_LOG | CA_LOG_FOR_CP)) + { + flmAssert( m_uiLogCacheCount); + m_uiLogCacheCount--; + } + if (pSCache->m_pNextInVersionList && + (pSCache->m_pNextInVersionList->m_ui16Flags & + (CA_WRITE_TO_LOG | CA_LOG_FOR_CP))) + { + flmAssert( m_uiLogCacheCount); + m_uiLogCacheCount--; + } + +#ifdef FLM_DEBUG + pSCache->clearFlags( + CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); + + if (pSCache->m_pNextInVersionList) + { + pSCache->m_pNextInVersionList->clearFlags( + CA_DIRTY | CA_WRITE_TO_LOG | CA_LOG_FOR_CP | CA_WAS_DIRTY); + } +#endif + + if (pSCache->m_ui16Flags & CA_IN_FILE_LOG_LIST) + { + pSCache->unlinkFromLogList(); + } + else if( pSCache->m_ui16Flags & CA_IN_NEW_LIST) + { + pSCache->unlinkFromNewList(); + } + + pSCache->unlinkCache( TRUE, NE_XFLM_OK); + } + else + { + // Another thread must have a temporary use on this block + // because it is traversing cache for some reason. We + // don't want to free this block until the use count + // is zero, so just put it into the free list so that + // when its use count goes to zero we will either + // re-use or free it. + + pSCache->unlinkCache( FALSE, NE_XFLM_OK); + pSCache->linkToFreeList( FLM_GET_TIMER()); + } + pSCache = pNextSCache; + } + + // Set the F_Database cache list pointer to NULL. Even if we didn't free + // all of the cache blocks right now, we at least unlinked them from + // the F_Database object. + + m_pSCacheList = NULL; + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +} + + +/**************************************************************************** +Desc: This routine computes an in-memory checksum on a cache block. This + is to guard against corrupt cache blocks being written back to disk. + NOTE: This routine assumes that the cache block mutex is already locked. +****************************************************************************/ +#ifdef FLM_DEBUG +FLMUINT F_CachedBlock::computeChecksum( void) +{ + FLMUINT uiChecksum = 0; + + if( gv_XFlmSysData.pBlockCacheMgr->m_bDebug) + { + FLMUINT uiBlkSize = getBlkSize(); + FLMBYTE * pucBlk = (FLMBYTE *)m_pBlkHdr; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiBlkSize; uiLoop += 4, pucBlk += 4) + { + uiChecksum = (uiChecksum ^ (FLMUINT)(*((FLMUINT32 *)(pucBlk)))); + } + + if (!uiChecksum) + { + uiChecksum = 1; + } + } + + return( uiChecksum); +} +#endif + +/**************************************************************************** +Desc: This routine finishes a checkpoint. At this point we are guaranteed + to have both the file lock and the write lock, and all dirty blocks + have been written to the database. This is the code that writes out + the log header and truncates the rollback log, roll-forward log, and + database files as needed. +****************************************************************************/ +RCODE F_Database::finishCheckpoint( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoTruncate, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset, + FLMUINT uiCPStartTime, + FLMUINT uiTotalToWrite) +{ + RCODE rc = NE_XFLM_OK; + XFLM_DB_HDR * pCommittedDbHdr = &m_lastCommittedDbHdr; + XFLM_DB_HDR saveDbHdr; + FLMUINT uiNewCPFileNum; + FLMUINT64 ui64CurrTransID; + FLMUINT uiSaveTransOffset; + FLMUINT uiSaveCPFileNum; + FLMBOOL bTruncateLog = FALSE; + FLMBOOL bTruncateRflFile = FALSE; + FLMUINT uiTruncateRflSize = 0; + FLMUINT uiLogEof; + FLMUINT uiHighLogFileNumber; +#ifdef FLM_DBG_LOG + FLMBOOL bResetRBL = FALSE; +#endif + + // Update the DB header to indicate that we now + // have a new checkpoint. + + f_memcpy( &saveDbHdr, pCommittedDbHdr, sizeof( XFLM_DB_HDR)); + + // Save some of the values we are going to change. These values + // will be needed below. + + ui64CurrTransID = pCommittedDbHdr->ui64CurrTransID; + uiSaveTransOffset = (FLMUINT)pCommittedDbHdr->ui32RflLastTransOffset; + uiSaveCPFileNum = (FLMUINT)pCommittedDbHdr->ui32RflLastCPFileNum; + +#ifdef FLM_DEBUG + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + // If we get to this point, there should be no dirty blocks for + // the file. + + flmAssert( !m_uiDirtyCacheCount && !m_pPendingWriteList && + (!m_pSCacheList || + !(m_pSCacheList->m_ui16Flags & CA_DIRTY))); + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); +#endif + + // Determine if we can reset the physical log. The log can be reset if + // there are no blocks in the log that are needed to preserve a read + // consistent view for a read transaction. By definition, this will + // be the case if there are no read transactions that started before + // the last transaction committed. Thus, that is what we check. + + // If we exceed a VERY large size, we need to wait for the read + // transactions to empty out so we can force a truncation of the + // log. This is also true if we are truncating the database and + // changing the logical EOF. + + uiLogEof = (FLMUINT)pCommittedDbHdr->ui32RblEOF; + uiHighLogFileNumber = FSGetFileNumber( uiLogEof); + + // Lock the database mutex while modifying the last committed header. + + lockMutex(); + if (uiHighLogFileNumber > 0 || bDoTruncate || + FSGetFileOffset( uiLogEof) > LOW_VERY_LARGE_LOG_THRESHOLD_SIZE) + { + FLMINT iWaitCnt = 0; + F_Db * pFirstDb; + FLMUINT ui5MinutesTime; + FLMUINT ui30SecTime; + char szMsgBuf[ 128]; + IF_LogMessageClient * pLogMsg = NULL; + FLMUINT uiFirstDbInactiveSecs; + FLMUINT uiElapTime; + FLMUINT uiLastMsgTime = FLM_GET_TIMER(); + FLMBOOL bMustTruncate = (bDoTruncate || + uiHighLogFileNumber || + FSGetFileOffset( uiLogEof) >= + HIGH_VERY_LARGE_LOG_THRESHOLD_SIZE) + ? TRUE + : FALSE; + + FLM_SECS_TO_TIMER_UNITS( 300, ui5MinutesTime); + FLM_SECS_TO_TIMER_UNITS( 30, ui30SecTime); + + if (m_pCPInfo && bMustTruncate) + { + m_pCPInfo->uiStartWaitTruncateTime = FLM_GET_TIMER(); + } + + pFirstDb = m_pFirstReadTrans; + while ((!m_pCPInfo || !m_pCPInfo->bShuttingDown) && pFirstDb && + pFirstDb->m_ui64CurrTransID < ui64CurrTransID) + { + FLMUINT uiTime; + FLMUINT uiFirstDbInactiveTime = 0; + FLMUINT64 ui64FirstDbCurrTransID = pFirstDb->m_ui64CurrTransID; + FLMUINT uiFirstDbThreadId = pFirstDb->m_uiThreadId; + F_Db * pTmpDb; + + uiTime = (FLMUINT)FLM_GET_TIMER(); + + if( !bMustTruncate) + { + pTmpDb = pFirstDb; + while( pTmpDb && pTmpDb->m_ui64CurrTransID < ui64CurrTransID) + { + if (!pTmpDb->m_uiInactiveTime) + { + pTmpDb->m_uiInactiveTime = uiTime; + } + pTmpDb = pTmpDb->m_pNextReadTrans; + } + uiFirstDbInactiveTime = pFirstDb->m_uiInactiveTime; + } + + // If the read transaction has been inactive for 5 minutes, + // forcibly kill it unless it has been marked as a "don't kill" + // transaction. + + if( !(pFirstDb->m_uiFlags & FDB_DONT_KILL_TRANS) && + (bMustTruncate || (uiFirstDbInactiveTime && + FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime) >= ui5MinutesTime))) + { + pFirstDb->m_uiKilledTime = uiTime; + if ((m_pFirstReadTrans = pFirstDb->m_pNextReadTrans) != NULL) + { + m_pFirstReadTrans->m_pPrevReadTrans = NULL; + } + else + { + m_pLastReadTrans = NULL; + } + pFirstDb->m_pPrevReadTrans = NULL; + + if ((pFirstDb->m_pNextReadTrans = m_pFirstKilledTrans) != NULL) + { + pFirstDb->m_pNextReadTrans->m_pPrevReadTrans = pFirstDb; + } + m_pFirstKilledTrans = pFirstDb; + + unlockMutex(); + + // Log a message indicating that we have killed the transaction + + if ((pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) + { + uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiFirstDbInactiveSecs); + + f_sprintf( szMsgBuf, + "Killed transaction %I64u." + " Thread: %X." + " Inactive time: %u seconds.", + ui64FirstDbCurrTransID, + (unsigned)uiFirstDbThreadId, + (unsigned)uiFirstDbInactiveSecs); + + pLogMsg->changeColor( XFLM_YELLOW, XFLM_BLACK); + pLogMsg->appendString( szMsgBuf); + flmEndLogMessage( &pLogMsg); + } + + lockMutex(); + pFirstDb = m_pFirstReadTrans; + continue; + } + else if (!bMustTruncate) + { + if (iWaitCnt >= 200) + { + break; + } + } + + unlockMutex(); + + if (!bMustTruncate) + { + iWaitCnt++; + f_sleep( 6); + } + else + { + // Log a message indicating that we are waiting for the + // transaction to complete + + if (FLM_ELAPSED_TIME( uiTime, uiLastMsgTime) >= ui30SecTime) + { + if ((pLogMsg = flmBeginLogMessage( XFLM_GENERAL_MESSAGE)) != NULL) + { + uiElapTime = FLM_ELAPSED_TIME( uiTime, uiFirstDbInactiveTime); + FLM_TIMER_UNITS_TO_SECS( uiElapTime, uiFirstDbInactiveSecs); + + f_sprintf( szMsgBuf, + "Waiting for transaction %I64u to complete." + " Thread: %X." + " Inactive time: %u seconds.", + ui64FirstDbCurrTransID, + (unsigned)uiFirstDbThreadId, + (unsigned)uiFirstDbInactiveSecs); + + pLogMsg->changeColor( XFLM_YELLOW, XFLM_BLACK); + pLogMsg->appendString( szMsgBuf); + flmEndLogMessage( &pLogMsg); + } + + uiLastMsgTime = FLM_GET_TIMER(); + } + + f_sleep( 100); + } + lockMutex(); + pFirstDb = m_pFirstReadTrans; + } + + if (bMustTruncate && m_pCPInfo) + { + m_pCPInfo->uiStartWaitTruncateTime = 0; + } + } + + if (!m_pFirstReadTrans || + m_pFirstReadTrans->m_ui64CurrTransID >= ui64CurrTransID) + { + + // We may want to truncate the log file if it has grown real big. + + if (uiHighLogFileNumber > 0 || + FSGetFileOffset( uiLogEof) > LOG_THRESHOLD_SIZE) + { + bTruncateLog = TRUE; + } + pCommittedDbHdr->ui32RblEOF = (FLMUINT32)pCommittedDbHdr->ui16BlockSize; +#ifdef FLM_DBG_LOG + bResetRBL = TRUE; +#endif + } + pCommittedDbHdr->ui32RblFirstCPBlkAddr = 0; + + // Set the checkpoint RFL file number and offset to be the same as + // the last transaction's RFL file number and offset if nothing + // is passed in. If a non-zero uiCPFileNum is passed in, it is because + // we are checkpointing the last transaction that has been recovered + // by the recovery process. + // In this case, instead of moving the pointers all the way forward, + // to the last committed transaction, we simply move them forward to + // the last recovered transaction. + + if (uiCPFileNum) + { + pCommittedDbHdr->ui32RflLastCPFileNum = (FLMUINT32)uiCPFileNum; + pCommittedDbHdr->ui32RflLastCPOffset = (FLMUINT32)uiCPOffset; + } + else + { + FLMBOOL bResetRflFile = FALSE; + + // If the RFL volume is full, and the LOG_AUTO_TURN_OFF_KEEP_RFL + // flag is TRUE, change the LOG_KEEP_RFL_FILES to FALSE. + + if (m_pRfl->isRflVolumeFull() && + pCommittedDbHdr->ui8RflKeepFiles && + pCommittedDbHdr->ui8RflAutoTurnOffKeep) + { + pCommittedDbHdr->ui8RflKeepFiles = 0; + bResetRflFile = TRUE; + } + + pCommittedDbHdr->ui32RflLastCPFileNum = + pCommittedDbHdr->ui32RflCurrFileNum; + + if (!pCommittedDbHdr->ui8RflKeepFiles) + { + pCommittedDbHdr->ui32RflLastCPOffset = 512; + if (bResetRflFile) + { + + // This will cause the RFL file to be recreated on the + // next transaction - causing the keep signature to be + // changed. Also need to set up to use new serial + // numbers so restore can't wade into this RFL file and + // attempt to start restoring from it. + + pCommittedDbHdr->ui32RflLastTransOffset = 0; + f_createSerialNumber( pCommittedDbHdr->ucLastTransRflSerialNum); + f_createSerialNumber( pCommittedDbHdr->ucNextRflSerialNum); + } + + // If LOG_RFL_LAST_TRANS_OFFSET is zero, someone has set this up + // intentionally to cause the RFL file to be created at the + // beginning of the next transaction. We don't want to lose + // that, so if it is zero, we don't change it. + + else if (pCommittedDbHdr->ui32RflLastTransOffset != 0) + { + pCommittedDbHdr->ui32RflLastTransOffset = 512; + } + uiTruncateRflSize = (FLMUINT)pCommittedDbHdr->ui32RflMinFileSize; + if ((uiSaveTransOffset >= RFL_TRUNCATE_SIZE) || + (uiSaveTransOffset >= uiTruncateRflSize)) + { + bTruncateRflFile = TRUE; + if (uiTruncateRflSize > RFL_TRUNCATE_SIZE) + { + uiTruncateRflSize = RFL_TRUNCATE_SIZE; + } + else if (uiTruncateRflSize < 512) + { + uiTruncateRflSize = 512; + } + + // Set to nearest 512 byte boundary + + uiTruncateRflSize &= 0xFFFFFE00; + } + } + else + { + FLMUINT uiLastTransOffset = + (FLMUINT)pCommittedDbHdr->ui32RflLastTransOffset; + + // If the RFL volume is not OK, and we are not currently positioned + // at the beginning of an RFL file, we should set things up to roll to + // the next RFL file. That way, if they need to change RFL volumes + // it will be OK, and we can create the new RFL file. + + if (!m_pRfl->seeIfRflVolumeOk() && uiLastTransOffset > 512) + { + pCommittedDbHdr->ui32RflLastTransOffset = 0; + pCommittedDbHdr->ui32RflCurrFileNum++; + pCommittedDbHdr->ui32RflLastCPOffset = 512; + pCommittedDbHdr->ui32RflLastCPFileNum = + pCommittedDbHdr->ui32RflCurrFileNum; + } + else + { + // If the transaction offset is zero, we want the last CP offset + // to be 512 - it should never be set to zero. It is possible + // for the transaction offset to still be zero at this point if + // we haven't done a non-empty transaction yet. + + if (!uiLastTransOffset) + { + uiLastTransOffset = 512; + } + pCommittedDbHdr->ui32RflLastCPOffset = + (FLMUINT32)uiLastTransOffset; + } + } + } + + // Set the checkpoint Trans ID to be the trans ID of the + // last committed transaction. + + pCommittedDbHdr->ui64RflLastCPTransID = pCommittedDbHdr->ui64CurrTransID; + + unlockMutex(); + + // Write the log header - this will complete the checkpoint. + + if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, + pCommittedDbHdr, + &m_checkpointDbHdr, TRUE))) + { + + // Restore log header. + + lockMutex(); + f_memcpy( pCommittedDbHdr, &saveDbHdr, sizeof( XFLM_DB_HDR)); + unlockMutex(); + goto Exit; + } + else if (bTruncateLog) + { + IF_FileHdl * pCFileHdl; + + if (uiHighLogFileNumber) + { + (void)pSFileHdl->TruncateFiles( + FIRST_LOG_BLOCK_FILE_NUMBER, + uiHighLogFileNumber); + } + + if (RC_OK( pSFileHdl->GetFileHdl( 0, TRUE, &pCFileHdl))) + { + (void)pCFileHdl->Truncate( LOG_THRESHOLD_SIZE); + } + } + +#ifdef FLM_DBG_LOG + if (bResetRBL) + { + char szMsg [80]; + + if (bTruncateLog) + { + f_sprintf( szMsg, "f%u, Reset&TruncRBL, CPTID:%I64u", + (unsigned)((FLMUINT)this), + pCommittedDbHdr->ui64RflLastCPTransID); + } + else + { + f_sprintf( szMsg, "f%u, ResetRBL, CPTID:%I64u", + (unsigned)((FLMUINT)this), + pCommittedDbHdr->ui64RflLastCPTransID); + } + flmDbgLogMsg( szMsg); + } +#endif + + // The checkpoint is now complete. Reset the first checkpoint + // block address to zero. + + m_uiFirstLogCPBlkAddress = 0; + m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + + // Save the state of the log header into the checkpointDbHdr buffer. + + f_memcpy( &m_checkpointDbHdr, pCommittedDbHdr, sizeof( XFLM_DB_HDR)); + + // See if we need to delete RFL files that are no longer in use. + + uiNewCPFileNum = (FLMUINT)pCommittedDbHdr->ui32RflLastCPFileNum; + + if (!pCommittedDbHdr->ui8RflKeepFiles && + uiSaveCPFileNum != uiNewCPFileNum && + uiNewCPFileNum > 1) + { + FLMUINT uiLastRflFileDeleted = + (FLMUINT)pCommittedDbHdr->ui32RflLastFileNumDeleted; + + uiLastRflFileDeleted++; + while (uiLastRflFileDeleted < uiNewCPFileNum) + { + char szLogFilePath [F_PATH_MAX_SIZE]; + RCODE TempRc; + FLMUINT uiFileNameSize = sizeof( szLogFilePath); + FLMBOOL bNameTruncated; + + m_pRfl->getFullRflFileName( uiLastRflFileDeleted, + szLogFilePath, &uiFileNameSize, + &bNameTruncated); + if (bNameTruncated) + { + break; + } + if (RC_BAD( TempRc = gv_pFileSystem->Delete( szLogFilePath))) + { + if (TempRc != NE_XFLM_IO_PATH_NOT_FOUND && + TempRc != NE_XFLM_IO_INVALID_FILENAME) + { + break; + } + } + uiLastRflFileDeleted++; + } + uiLastRflFileDeleted--; + + // If we actually deleted a file, update the log header. + + if (uiLastRflFileDeleted != + (FLMUINT)pCommittedDbHdr->ui32RflLastFileNumDeleted) + { + pCommittedDbHdr->ui32RflLastFileNumDeleted = + (FLMUINT32)uiLastRflFileDeleted; + if (RC_BAD( rc = writeDbHdr( pDbStats, pSFileHdl, + pCommittedDbHdr, + &m_checkpointDbHdr, TRUE))) + { + goto Exit; + } + + // Save the state of the log header into the checkpointDbHdr buffer + // and update the last checkpoint time again. + + f_memcpy( &m_checkpointDbHdr, pCommittedDbHdr, + sizeof( XFLM_DB_HDR)); + m_uiLastCheckpointTime = (FLMUINT)FLM_GET_TIMER(); + } + } + + // Truncate the RFL file, if the truncate flag was set above. + + if (bTruncateRflFile) + { + (void)m_pRfl->truncate( hWaitSem, uiTruncateRflSize); + } + + // Truncate the files, if requested to do so - this would be a request of + // FlmDbReduceSize. + + if (bDoTruncate) + { + if (RC_BAD( rc = pSFileHdl->TruncateFile( + (FLMUINT)pCommittedDbHdr->ui32LogicalEOF))) + { + goto Exit; + } + } + + // Re-enable the RFL volume OK flag - in case it was turned off somewhere. + + m_pRfl->setRflVolumeOk(); + + // If we complete a checkpoint successfully, we want to set the + // pFile->CheckpointRc so that new transactions can come in. + // NOTE: CheckpointRc should only be set while we still have the + // lock on the database - which should always be the case at this + // point. This routine can only be called if we have obtained both + // the write lock and the file lock. + + m_CheckpointRc = NE_XFLM_OK; + + // If we were calculating our maximum dirty cache, finish the + // calculation. + + if (uiCPStartTime) + { + FLMUINT uiCPEndTime = FLM_GET_TIMER(); + FLMUINT uiCPElapsedTime = FLM_ELAPSED_TIME( uiCPEndTime, uiCPStartTime); + FLMUINT uiElapsedMilli; + FLMUINT ui15Seconds; + FLMUINT uiMaximum; + FLMUINT uiLow; + + // Get elapsed time in milliseconds - only calculate a new maximum if + // we did at least a half second worth of writing. + + FLM_TIMER_UNITS_TO_MILLI( uiCPElapsedTime, uiElapsedMilli); + + if (uiElapsedMilli >= 500) + { + + // Calculate what could be written in 15 seconds - set maximum + // to that. If calculated maximum is zero, we will not change + // the current maximum. + + FLM_SECS_TO_TIMER_UNITS( 15, ui15Seconds); + + uiMaximum = (FLMUINT)(((FLMUINT64)uiTotalToWrite * + (FLMUINT64)ui15Seconds) / (FLMUINT64)uiCPElapsedTime); + if (uiMaximum) + { + // Low is maximum minus what could be written in roughly + // two seconds. + + uiLow = uiMaximum - (uiMaximum / 7); + + // Only set the maximum if we are still in auto-calculate mode. + + if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) + { + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + + // Test flag again after locking the mutex + + if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) + { + gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache = uiMaximum; + gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache = uiLow; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: This routine performs a checkpoint. It will stay in here until + it either finishes, gets interrupted, or gets an error. If we are not + forcing a checkpoint, we periodically check to see if we should switch + to a forced mode. We also periodically check to see if another thread + needs is waiting to obtain the write lock. +****************************************************************************/ +RCODE F_Database::doCheckpoint( + F_SEM hWaitSem, + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoTruncate, + FLMBOOL bForceCheckpoint, + FLMINT iForceReason, + FLMUINT uiCPFileNum, + FLMUINT uiCPOffset) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bWroteAll; + FLMUINT uiCPStartTime = 0; + FLMUINT uiTotalToWrite; + FLMUINT uiMaxDirtyCache; + F_CachedBlock * pSCache; + FLMUINT uiTimestamp; + + pSFileHdl->enableFlushMinimize(); + + if (m_pCPInfo) + { + lockMutex(); + m_pCPInfo->bDoingCheckpoint = TRUE; + m_pCPInfo->uiStartTime = (FLMUINT)FLM_GET_TIMER(); + m_pCPInfo->bForcingCheckpoint = bForceCheckpoint; + if (bForceCheckpoint) + { + m_pCPInfo->uiForceCheckpointStartTime = m_pCPInfo->uiStartTime; + } + m_pCPInfo->iForceCheckpointReason = iForceReason; + m_pCPInfo->uiDataBlocksWritten = + m_pCPInfo->uiLogBlocksWritten = 0; + unlockMutex(); + } + + uiTotalToWrite = (m_uiDirtyCacheCount + m_uiLogCacheCount) * + m_uiBlockSize; + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + if (bForceCheckpoint) + { + if (gv_XFlmSysData.pBlockCacheMgr->m_bAutoCalcMaxDirty) + { + uiCPStartTime = FLM_GET_TIMER(); + } + } + + // If the amount of dirty cache is over our maximum, we must at least bring + // it down below the low threshhold. Otherwise, we set uiMaxDirtyCache + // to the highest possible value - which will not require us to get + // it below anything - because it is already within limits. + + if (gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache && + uiTotalToWrite > gv_XFlmSysData.pBlockCacheMgr->m_uiMaxDirtyCache) + { + uiMaxDirtyCache = gv_XFlmSysData.pBlockCacheMgr->m_uiLowDirtyCache; + } + else + { + uiMaxDirtyCache = ~((FLMUINT)0); + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + + // Write out log blocks first. + + bWroteAll = TRUE; + if (RC_BAD( rc = flushLogBlocks( hWaitSem, pDbStats, pSFileHdl, + TRUE, uiMaxDirtyCache, + &bForceCheckpoint, &bWroteAll))) + { + goto Exit; + } + + // If we didn't write out all log blocks, we got interrupted. + + if (!bWroteAll) + { + flmAssert( !bForceCheckpoint); + goto Exit; + } + + // Now write out dirty blocks + + if (RC_BAD( rc = flushDirtyBlocks( pDbStats, pSFileHdl, + uiMaxDirtyCache, + bForceCheckpoint, TRUE, &bWroteAll))) + { + goto Exit; + } + + // If we didn't write out all dirty blocks, we got interrupted + + if (!bWroteAll) + { + flmAssert( !bForceCheckpoint); + goto Exit; + } + + // All dirty blocks and log blocks have been written, so we just + // need to finish the checkpoint. + + if (RC_BAD( rc = finishCheckpoint( hWaitSem, pDbStats, pSFileHdl, + bDoTruncate, uiCPFileNum, uiCPOffset, + uiCPStartTime, uiTotalToWrite))) + { + goto Exit; + } + +Exit: + + // If we were attempting to force a checkpoint and it failed, + // we want to set m_CheckpointRc, because we want to + // prevent new transactions from starting until this situation + // is cleared up (see fltrbeg.cpp). Note that setting + // m_CheckpointRc to something besides NE_XFLM_OK will cause + // the checkpoint thread to force checkpoints whenever it is woke + // up until it succeeds (see flopen.cpp). + + if (RC_BAD( rc) && bForceCheckpoint) + { + m_CheckpointRc = rc; + } + + // Timestamp all of the items in the free list + + if (bForceCheckpoint) + { + uiTimestamp = FLM_GET_TIMER(); + + f_mutexLock( gv_XFlmSysData.hBlockCacheMutex); + pSCache = gv_XFlmSysData.pBlockCacheMgr->m_pFirstFree; + while (pSCache) + { + pSCache->m_uiBlkAddress = uiTimestamp; + pSCache = pSCache->m_pNextInDatabase; + } + f_mutexUnlock( gv_XFlmSysData.hBlockCacheMutex); + } + + if (m_pCPInfo) + { + lockMutex(); + m_pCPInfo->bDoingCheckpoint = FALSE; + unlockMutex(); + } + + pSFileHdl->disableFlushMinimize(); + + return( rc); +} + +/**************************************************************************** +Notes: This routine assumes the cache block cache mutex is locked + This is a static method, so there is no "this" pointer to the + F_BlockCacheMgr object. +****************************************************************************/ +FLMBOOL F_BlockRelocator::canRelocate( + void * pvAlloc) +{ + return( ((F_CachedBlock *)pvAlloc)->m_uiUseCount ? FALSE : TRUE); +} + +/**************************************************************************** +Desc: Fixes up all pointers needed to allow an F_CachedBlock object to be + moved to a different location in memory +Notes: This routine assumes the cache block mutex is locked + This is a static method, so there is no "this" pointer to the + F_BlockCacheMgr object. +****************************************************************************/ +void F_BlockRelocator::relocate( + void * pvOldAlloc, + void * pvNewAlloc) +{ + F_CachedBlock * pOldSCache = (F_CachedBlock *)pvOldAlloc; + F_CachedBlock * pNewSCache = (F_CachedBlock *)pvNewAlloc; + F_CachedBlock ** ppBucket; + F_BlockCacheMgr * pBlockCacheMgr = gv_XFlmSysData.pBlockCacheMgr; + F_Database * pDatabase = pOldSCache->m_pDatabase; + + flmAssert( !pOldSCache->m_uiUseCount); + + if( pNewSCache->m_pPrevInDatabase) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInDatabase->unprotectCachedItem(); +#endif + pNewSCache->m_pPrevInDatabase->m_pNextInDatabase = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInDatabase->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pNextInDatabase) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInDatabase->unprotectCachedItem(); +#endif + pNewSCache->m_pNextInDatabase->m_pPrevInDatabase = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInDatabase->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pPrevInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInGlobal->unprotectCachedItem(); +#endif + pNewSCache->m_pPrevInGlobal->m_pNextInGlobal = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInGlobal->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pNextInGlobal) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInGlobal->unprotectCachedItem(); +#endif + pNewSCache->m_pNextInGlobal->m_pPrevInGlobal = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInGlobal->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pPrevInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInReplaceList->unprotectCachedItem(); +#endif + pNewSCache->m_pPrevInReplaceList->m_pNextInReplaceList = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInReplaceList->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pNextInReplaceList) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInReplaceList->unprotectCachedItem(); +#endif + pNewSCache->m_pNextInReplaceList->m_pPrevInReplaceList = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInReplaceList->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pPrevInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInHashBucket->unprotectCachedItem(); +#endif + pNewSCache->m_pPrevInHashBucket->m_pNextInHashBucket = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInHashBucket->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pNextInHashBucket) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInHashBucket->unprotectCachedItem(); +#endif + pNewSCache->m_pNextInHashBucket->m_pPrevInHashBucket = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInHashBucket->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pPrevInVersionList) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInVersionList->unprotectCachedItem(); +#endif + pNewSCache->m_pPrevInVersionList->m_pNextInVersionList = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pPrevInVersionList->protectCachedItem(); +#endif + } + + if( pNewSCache->m_pNextInVersionList) + { +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInVersionList->unprotectCachedItem(); +#endif + pNewSCache->m_pNextInVersionList->m_pPrevInVersionList = pNewSCache; +#ifdef FLM_CACHE_PROTECT + pNewSCache->m_pNextInVersionList->protectCachedItem(); +#endif + } + + if( pDatabase) + { + if( pDatabase->m_pSCacheList == pOldSCache) + { + pDatabase->m_pSCacheList = pNewSCache; + } + + if( pDatabase->m_pLastDirtyBlk == pOldSCache) + { + pDatabase->m_pLastDirtyBlk = pNewSCache; + } + + if( pDatabase->m_pFirstInLogList == pOldSCache) + { + pDatabase->m_pFirstInLogList = pNewSCache; + } + + if( pDatabase->m_pLastInLogList == pOldSCache) + { + pDatabase->m_pLastInLogList = pNewSCache; + } + + if( pDatabase->m_pFirstInNewList == pOldSCache) + { + pDatabase->m_pFirstInNewList = pNewSCache; + } + + if( pDatabase->m_pLastInNewList == pOldSCache) + { + pDatabase->m_pLastInNewList = pNewSCache; + } + + if( pDatabase->m_pTransLogList == pOldSCache) + { + pDatabase->m_pTransLogList = pNewSCache; + } + + ppBucket = pBlockCacheMgr->blockHash( pDatabase->getSigBitsInBlkSize(), + pOldSCache->m_uiBlkAddress); + + if( *ppBucket == pOldSCache) + { + *ppBucket = pNewSCache; + } + + flmAssert( pDatabase->m_pPendingWriteList != pOldSCache); + } + + if (pBlockCacheMgr->m_MRUList.m_pMRUItem == (F_CachedItem *)pOldSCache) + { + pBlockCacheMgr->m_MRUList.m_pMRUItem = pNewSCache; + } + + if (pBlockCacheMgr->m_MRUList.m_pLRUItem == (F_CachedItem *)pOldSCache) + { + pBlockCacheMgr->m_MRUList.m_pLRUItem = pNewSCache; + } + + if (pBlockCacheMgr->m_MRUList.m_pLastMRUItem == (F_CachedItem *)pOldSCache) + { + pBlockCacheMgr->m_MRUList.m_pLastMRUItem = pNewSCache; + } + + if (pBlockCacheMgr->m_pMRUReplace == pOldSCache) + { + pBlockCacheMgr->m_pMRUReplace = pNewSCache; + } + + if (pBlockCacheMgr->m_pLRUReplace == pOldSCache) + { + pBlockCacheMgr->m_pLRUReplace = pNewSCache; + } + + if (pBlockCacheMgr->m_pFirstFree == pOldSCache) + { + pBlockCacheMgr->m_pFirstFree = pNewSCache; + } + + if (pBlockCacheMgr->m_pLastFree == pOldSCache) + { + pBlockCacheMgr->m_pLastFree = pNewSCache; + } + + pNewSCache->m_pBlkHdr = (F_BLK_HDR *)&pNewSCache[ 1]; +} + +/**************************************************************************** +Desc: This function will encrypt the block of data passed in. This function + assumes that the buffer passed in includes the block header. +****************************************************************************/ +RCODE F_Database::encryptBlock( + F_Dict * pDict, + FLMBYTE * pucBuffer + ) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + F_COLLECTION * pCollection; + FLMUINT uiLfNum; + F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pucBuffer; + F_ENCDEF * pEncDef = NULL; + FLMUINT uiEncId; + FLMUINT uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); +#ifdef FLM_USE_NICI + F_CCS * pCcs = NULL; +#endif + + if (!blkIsBTree( (F_BLK_HDR *)pucBuffer)) + { + // Nothing to do. We are only interested in btree blocks. + goto Exit; + } + + if (!isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) + { + goto Exit; + } + + if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) + { + uiEncLen = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); + + if (!m_bTempDb) + { + uiEncId = ((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId; + + // Need to get the encryption object. + + if (RC_BAD( rc = pDict->getEncDef( uiEncId, + &pEncDef))) + { + goto Exit; + } + } + } + else if (isContainerBlk( pBlkHdr)) + { + if (!m_bTempDb) + { + uiLfNum = pBlkHdr->ui16LogicalFile; + + // Get the index. + if (RC_BAD( rc = pDict->getCollection( uiLfNum, + &pCollection, + TRUE))) + { + // Not a collection. + if (rc == NE_XFLM_BAD_COLLECTION) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // The collection may not be encrypted. + // We can just exit here. + + if (!pCollection || !pCollection->lfInfo.uiEncId) + { + goto Exit; // NE_XFLM_OK; + } + + // Need to get the encryption object. + if (RC_BAD( rc = pDict->getEncDef( pCollection->lfInfo.uiEncId, + &pEncDef))) + { + goto Exit; + } + } + } + else if (isIndexBlk( pBlkHdr)) + { + if (!m_bTempDb) + { + uiLfNum = pBlkHdr->ui16LogicalFile; + + // Get the index. + if (RC_BAD( rc = pDict->getIndex( uiLfNum, + NULL, + &pIxd, + TRUE))) + { + // Not an index. + if (rc == NE_XFLM_BAD_IX) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // The index may not be encrypted. + // We can just exit here. + if (!pIxd || !pIxd->lfInfo.uiEncId) + { + goto Exit; // NE_XFLM_OK; + } + + // Need to get the encryption object. + if (RC_BAD( rc = pDict->getEncDef( pIxd->lfInfo.uiEncId, + &pEncDef))) + { + goto Exit; + } + } + } + else + { + goto Exit; // NE_XFLM_OK + } + +#ifndef FLM_USE_NICI + rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); + goto Exit; +#else + + if (m_bInLimitedMode) + { + rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + if (!m_bTempDb) + { + flmAssert( pEncDef); + pCcs = pEncDef->pCcs; + } + else + { + flmAssert( !pEncDef); + pCcs = m_pWrappingKey; + } + + flmAssert( pCcs); + flmAssert( !(uiEncLen % 16)); + + // Encrypt the buffer in place. + + if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) + { + if (RC_BAD( rc = pCcs->encryptToStore( &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], + uiEncLen, + &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], + &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr))); + + } + else + { + if (RC_BAD( rc = pCcs->encryptToStore( &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], + uiEncLen, + &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], + &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == (m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr))); + + } +#endif + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: This function will decrypt the block of data passed in. +****************************************************************************/ +RCODE F_Database::decryptBlock( + F_Dict * pDict, + FLMBYTE * pucBuffer) +{ + RCODE rc = NE_XFLM_OK; + IXD * pIxd; + F_COLLECTION * pCollection; + FLMUINT uiLfNum; + F_BTREE_BLK_HDR * pBlkHdr = (F_BTREE_BLK_HDR *)pucBuffer; + FLMUINT uiEncLen; + F_ENCDEF * pEncDef = NULL; +#ifdef FLM_USE_NICI + F_CCS * pCcs = NULL; +#endif + + if (!blkIsBTree( (F_BLK_HDR *)pucBuffer)) + { + // Nothing to do. We are only interested in btree blocks. + goto Exit; + } + + if (!isEncryptedBlk( (F_BLK_HDR *)pBlkHdr)) + { + goto Exit; + } + + if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) + { + + uiEncLen = m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr); + + if (!m_bTempDb) + { + + // Need to get the encryption object. + + if (RC_BAD( rc = pDict->getEncDef( + (FLMUINT)(((F_ENC_DO_BLK_HDR *)pBlkHdr)->ui32EncId), + &pEncDef))) + { + goto Exit; + } + } + } + else if (isContainerBlk( pBlkHdr)) + { + uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); + + if (!m_bTempDb) + { + uiLfNum = pBlkHdr->ui16LogicalFile; + + // Get the index. + if (RC_BAD( rc = pDict->getCollection( uiLfNum, + &pCollection, + TRUE))) + { + // Not a collection. + if (rc == NE_XFLM_BAD_COLLECTION) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // The collection may not be encrypted. + // We can just exit here. + + if (!pCollection || !pCollection->lfInfo.uiEncId) + { + goto Exit; // NE_XFLM_OK; + } + + // Need to get the encryption object. + + if (RC_BAD( rc = pDict->getEncDef( pCollection->lfInfo.uiEncId, + &pEncDef))) + { + goto Exit; + } + } + } + else if (isIndexBlk( pBlkHdr)) + { + uiEncLen = m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr); + + if (!m_bTempDb) + { + uiLfNum = pBlkHdr->ui16LogicalFile; + + // Get the index. + + if (RC_BAD( rc = pDict->getIndex( uiLfNum, + NULL, + &pIxd, + TRUE))) + { + // Not an index. + if (rc == NE_XFLM_BAD_IX) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + // The index may not be encrypted. + // We can just exit here. + + if (!pIxd || !pIxd->lfInfo.uiEncId) + { + goto Exit; // NE_XFLM_OK; + } + + // Need to get the encryption object. + + if (RC_BAD( rc = pDict->getEncDef( pIxd->lfInfo.uiEncId, + &pEncDef))) + { + goto Exit; + } + } + } + else + { + goto Exit; // NE_XFLM_OK + } + + +#ifndef FLM_USE_NICI + rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); + goto Exit; +#else + + if (m_bInLimitedMode) + { + rc = RC_SET( NE_XFLM_ENCRYPTION_UNAVAILABLE); + goto Exit; + } + + if (!m_bTempDb) + { + flmAssert( pEncDef); + pCcs = pEncDef->pCcs; + } + else + { + flmAssert( !pEncDef); + pCcs = m_pWrappingKey; + } + + flmAssert( pCcs); + flmAssert( !(uiEncLen % 16)); + + if (pBlkHdr->stdBlkHdr.ui8BlkType == BT_DATA_ONLY) + { + if (RC_BAD( rc = pCcs->decryptFromStore( + &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], uiEncLen, + &pucBuffer[ sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr)], &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == + (m_uiBlockSize - sizeofDOBlkHdr( (F_BLK_HDR *)pBlkHdr))); + } + else + { + if (RC_BAD( rc = pCcs->decryptFromStore( + &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], uiEncLen, + &pucBuffer[ sizeofBTreeBlkHdr( pBlkHdr)], &uiEncLen))) + { + goto Exit; + } + + flmAssert( uiEncLen == (m_uiBlockSize - sizeofBTreeBlkHdr( pBlkHdr))); + } + +#endif + +Exit: + + return rc; +} + +#undef new +#undef delete + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_CachedBlock::operator new( + FLMSIZET uiSize, + FLMUINT uiBlockSize, + FLMBOOL bAllocMutexLocked) +#ifndef FLM_NLM + throw() +#endif +{ + void * pvPtr; + + flmAssert( uiSize == sizeof( F_CachedBlock)); + if( RC_BAD( gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.allocBuf( + &gv_XFlmSysData.pBlockCacheMgr->m_blockRelocator, + uiSize + uiBlockSize, (FLMBYTE **)&pvPtr, bAllocMutexLocked))) + { + pvPtr = NULL; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.unprotectBuffer( + pvPtr, bAllocMutexLocked); +#endif + return( pvPtr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_CachedBlock::operator new( + FLMSIZET) //uiSize) +#ifndef FLM_NLM + throw() +#endif +{ + // This new should never be called + flmAssert( 0); + return( NULL); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void * F_CachedBlock::operator new[]( FLMSIZET) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_CachedBlock::operator new( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLineNum) +#ifndef FLM_NLM + throw() +#endif +{ + // This new should never be called + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#ifdef FLM_DEBUG +void * F_CachedBlock::operator new[]( + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLine) +#ifndef FLM_NLM + throw() +#endif +{ + flmAssert( 0); + return( NULL); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedBlock::operator delete( + void * ptr) +{ + if( !ptr) + { + return; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.protectBuffer( ptr); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.freeBuf( (FLMBYTE **)&ptr); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void F_CachedBlock::operator delete[]( + void *) // ptr +{ + flmAssert( 0); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedBlock::operator delete( + void *, // ptr + FLMSIZET, // uiSize, + const char *, // pszFileName, + int) // iLine +{ + flmAssert( 0); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedBlock::operator delete( + void * ptr, + const char *, // pszFileName + int) // iLineNum +{ + if( !ptr) + { + return; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.protectBuffer( ptr); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.freeBuf( (FLMBYTE **)&ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedBlock::operator delete( + void * ptr, + FLMUINT, // uiBlockSize, + FLMBOOL) // bAllocMutexLocked) +{ + if( !ptr) + { + return; + } + +#ifdef FLM_CACHE_PROTECT + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.protectBuffer( ptr); +#endif + gv_XFlmSysData.pBlockCacheMgr->m_blockAllocator.freeBuf( (FLMBYTE **)&ptr); +} +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if !defined( __WATCOMC__) && !defined( FLM_SOLARIS) +void F_CachedBlock::operator delete[]( + void *, // ptr, + const char *, // pszFileName + int) // iLineNum +{ + flmAssert( 0); +} +#endif diff --git a/version5/src/translog.cpp b/version5/src/translog.cpp new file mode 100644 index 0000000..438a6df --- /dev/null +++ b/version5/src/translog.cpp @@ -0,0 +1,329 @@ +//------------------------------------------------------------------------------ +// Desc: Routines to handle transaction logging +// +// 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: translog.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" + +FSTATIC void lgWriteComplete( + F_IOBuffer * pIOBuffer); + +#ifdef FLM_DBG_LOG +/**************************************************************************** +Desc: This routine is used to write out information about logged blocks to + the log file. +****************************************************************************/ +void scaLogWrite( + F_Database * pDatabase, + FLMUINT uiWriteAddress, + FLMBYTE * pucBlkBuf, + FLMUINT uiBufferLen, + FLMUINT uiBlockSize, + char * pszEvent + ) +{ + FLMUINT uiOffset = 0; + FLMUINT32 ui32BlkAddr; + FLMUINT64 ui64TransID; + + while (uiOffset < uiBufferLen) + { + ui32BlkAddr = ((F_BLK_HDR *)pucBlkBuf)->ui32BlkAddr; + ui64TransID = ((F_BLK_HDR *)pucBlkBuf)->ui64TransID; + + // A uiWriteAddress of zero means we are writing exactly at the + // block address - i.e., it is the data block, not the log block. + + flmDbgLogWrite( pDatabase, (FLMUINT)ui32BlkAddr, + (FLMUINT)((uiWriteAddress) + ? uiWriteAddress + uiOffset + : (FLMUINT)ui32BlkAddr), + ui64TransID, pszEvent); + uiOffset += uiBlockSize; + pucBlkBuf += uiBlockSize; + } +} +#endif + +/**************************************************************************** +Desc: This is the callback routine that is called when a disk write is + completed. +****************************************************************************/ +FSTATIC void lgWriteComplete( + F_IOBuffer * pIOBuffer + ) +{ + F_Database * pDatabase = + (F_Database *)pIOBuffer->getCompletionCallbackData( 0); +#ifdef FLM_DBG_LOG + FLMUINT uiBlockSize = pDatabase->getBlockSize(); + FLMUINT uiLength = pIOBuffer->getBufferSize(); + char * pszEvent; +#endif + XFLM_DB_STATS * pDbStats = pIOBuffer->getDbStats(); + +#ifdef FLM_DBG_LOG + pszEvent = (char *)(RC_OK( pIOBuffer->getCompletionCode()) + ? (char *)"LGWRT" + : (char *)"LGWRT-FAIL"); + scaLogWrite( pDatabase, 0, pIOBuffer->getBuffer(), uiLength, + uiBlockSize, pszEvent); +#endif + + if (pDbStats) + { + + // Must lock mutex, because this may be called from async write + // completion at any time. + + pDatabase->lockMutex(); + pDbStats->LogBlockWrites.ui64ElapMilli += pIOBuffer->getElapTime(); + pDatabase->unlockMutex(); + } +} + +/**************************************************************************** +Desc: This routine flushes a log buffer to the log file. +****************************************************************************/ +RCODE F_Database::lgFlushLogBuffer( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + FLMBOOL bDoAsync) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBytesWritten; + F_IOBuffer * pAsyncBuffer; + +#if defined( FLM_NLM) || defined( FLM_WIN) + if (!bDoAsync) + { + pAsyncBuffer = NULL; + } + else + { + pAsyncBuffer = m_pCurrLogBuffer; + } +#else + F_UNREFERENCED_PARM( bDoAsync); + pAsyncBuffer = NULL; +#endif + + if (pDbStats) + { + pDbStats->bHaveStats = TRUE; + pDbStats->LogBlockWrites.ui64Count++; + pDbStats->LogBlockWrites.ui64TotalBytes += m_uiCurrLogWriteOffset; + } + + m_pCurrLogBuffer->setCompletionCallback( lgWriteComplete); + m_pCurrLogBuffer->setCompletionCallbackData( 0, (void *)this); + pSFileHdl->setMaxAutoExtendSize( m_uiMaxFileSize); + pSFileHdl->setExtendSize( m_uiFileExtendSize); + m_pCurrLogBuffer->startTimer( pDbStats); + + // NOTE: No guarantee that m_pCurrLogBuffer will still be around + // after the call to WriteBlock, unless we are doing + // non-asynchronous write. + + rc = pSFileHdl->WriteBlock( m_uiCurrLogBlkAddr, + m_uiCurrLogWriteOffset, + m_pCurrLogBuffer->getBuffer(), + m_pCurrLogBuffer->getBufferSize(), + pAsyncBuffer, &uiBytesWritten); + if (!pAsyncBuffer) + { + m_pCurrLogBuffer->notifyComplete( rc); + } + m_pCurrLogBuffer = NULL; + + if (RC_BAD( rc)) + { + if (pDbStats) + { + pDbStats->uiWriteErrors++; + } + goto Exit; + } + +Exit: + + m_uiCurrLogWriteOffset = 0; + m_pCurrLogBuffer = NULL; + return( rc); +} + +/**************************************************************************** +Desc: This routine writes a block to the log file. +****************************************************************************/ +RCODE F_Database::lgOutputBlock( + XFLM_DB_STATS * pDbStats, + F_SuperFileHdl * pSFileHdl, + F_CachedBlock * pLogBlock, // Cached log block. + F_BLK_HDR * pBlkHdr, // Pointer to the corresponding modified + // block in cache. This block will be + // modified to the logged version of + // the block + FLMBOOL bDoAsync, // Do asynchronous writes? + FLMUINT * puiLogEofRV) // Returns log EOF +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFilePos = *puiLogEofRV; + FLMBYTE * pucLogBlk; + F_BLK_HDR * pLogBlkHdr; + FLMUINT uiBlkAddress; + FLMUINT uiLogBufferSize; + + // Time for a new block file? + + if (FSGetFileOffset( uiFilePos) >= m_uiMaxFileSize) + { + FLMUINT uiFileNumber; + + // Write out the current buffer, if it has anything in it. + + if (m_uiCurrLogWriteOffset) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + bDoAsync))) + { + goto Exit; + } + } + + uiFileNumber = FSGetFileNumber( uiFilePos); + + if (!uiFileNumber) + { + uiFileNumber = FIRST_LOG_BLOCK_FILE_NUMBER; + } + else + { + uiFileNumber++; + } + + if (uiFileNumber > MAX_LOG_BLOCK_FILE_NUMBER) + { + rc = RC_SET( NE_XFLM_DB_FULL); + goto Exit; + } + + if (RC_BAD( rc = pSFileHdl->CreateFile( uiFileNumber ))) + { + goto Exit; + } + uiFilePos = FSBlkAddress( uiFileNumber, 0 ); + } + + // Copy the log block to the log buffer. + + if (!m_uiCurrLogWriteOffset) + { + m_uiCurrLogBlkAddr = uiFilePos; + + // Get a buffer for logging. + // NOTE: Buffers are not kept by the F_Database's buffer manager, + // so once we are done with this buffer, it will be freed. + + uiLogBufferSize = MAX_LOG_BUFFER_SIZE; + + for( ;;) + { + if (RC_BAD( rc = m_pBufferMgr->getBuffer( + &m_pCurrLogBuffer, uiLogBufferSize, uiLogBufferSize))) + { + // If we failed to get a buffer of the requested size, + // reduce the buffer size by half and try again. + + if( rc == NE_XFLM_MEM) + { + uiLogBufferSize /= 2; + if( uiLogBufferSize < m_uiBlockSize) + { + goto Exit; + } + rc = NE_XFLM_OK; + continue; + } + goto Exit; + } + break; + } + } + + // Copy data from log block to the log buffer + + pucLogBlk = m_pCurrLogBuffer->getBuffer() + m_uiCurrLogWriteOffset; + pLogBlkHdr = (F_BLK_HDR *)pucLogBlk; + f_memcpy( pLogBlkHdr, pLogBlock->m_pBlkHdr, m_uiBlockSize); + + // If we are logging this block for the current update + // transaction, set the BEFORE IMAGE (BI) flag in the block header + // so we will know that this block is a before image block that + // needs to be restored when aborting the current update + // transaction. + + if (pLogBlock->m_ui16Flags & CA_WRITE_TO_LOG) + { + pLogBlkHdr->ui8BlkFlags |= BLK_IS_BEFORE_IMAGE; + } + uiBlkAddress = (FLMUINT)pLogBlkHdr->ui32BlkAddr; + + // Encrypt the block if needed + + if (RC_BAD( rc = encryptBlock( m_pDictList, + (FLMBYTE *)pLogBlkHdr))) + { + goto Exit; + } + + if (RC_BAD( rc = flmPrepareBlockToWrite( m_uiBlockSize, pLogBlkHdr))) + { + goto Exit; + } + + // Set up for next log block write + + m_uiCurrLogWriteOffset += m_uiBlockSize; + + // If this log buffer is full, write it out. + + if (m_uiCurrLogWriteOffset == m_pCurrLogBuffer->getBufferSize()) + { + if (RC_BAD( rc = lgFlushLogBuffer( pDbStats, pSFileHdl, + bDoAsync))) + { + goto Exit; + } + } + + // Save the previous block address into the modified block's + // block header area. Also save the transaction id. + + pBlkHdr->ui32PriorBlkImgAddr = (FLMUINT32)uiFilePos; + + *puiLogEofRV = uiFilePos + m_uiBlockSize; + +Exit: + + return( rc); +} diff --git a/version5/src/unitable.h b/version5/src/unitable.h new file mode 100644 index 0000000..78fd746 --- /dev/null +++ b/version5/src/unitable.h @@ -0,0 +1,1061 @@ +//------------------------------------------------------------------------------ +// Desc: Contains a table of unicode character properties +// +// Tabs: 3 +// +// Copyright (c) 1991-1992, 1994-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: unitable.h 3113 2006-01-19 13:20:35 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef _UNITABLE_H +#define _UNITABLE_H + +#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}; + +#endif + diff --git a/version5/src/xflaim.h b/version5/src/xflaim.h new file mode 100644 index 0000000..f1d6dc4 --- /dev/null +++ b/version5/src/xflaim.h @@ -0,0 +1,6137 @@ +//------------------------------------------------------------------------------ +// 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 XFLAIM_H +#define XFLAIM_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_POWER_PC + #undef FLM_STRICT_ALIGNMENT + + #if defined( __NETWARE__) || defined( NLM) || defined( N_PLAT_NLM) + #define FLM_NLM + #if defined( __WATCOMC__) + #define FLM_WATCOM_NLM + #elif defined( __MWERKS__) + #define FLM_MWERKS_NLM + #endif + #elif defined( _WIN64) + #define FLM_WIN + #define FLM_64BIT + #define FLM_STRICT_ALIGNMENT + #elif defined( _WIN32) + #define FLM_WIN + #elif defined( _AIX) + #define FLM_UNIX + #define FLM_AIX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif defined( linux) + #define FLM_UNIX + #define FLM_LINUX + #if defined( __PPC__) + #define FLM_POWER_PC + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #endif + #elif defined( sun) + #define FLM_UNIX + #define FLM_SOLARIS + #define FLM_STRICT_ALIGNMENT + #if defined( sparc) || defined( __sparc) + #define FLM_SPARC + #define FLM_BIG_ENDIAN + #endif + #elif defined( __hpux) || defined( hpux) + #define FLM_UNIX + #define FLM_HPUX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #elif (defined( __ppc__) || defined( __ppc64__)) && defined( __APPLE__) + #define FLM_UNIX + #define FLM_OSX + #define FLM_BIG_ENDIAN + #define FLM_STRICT_ALIGNMENT + #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__) + #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; + + #if defined( FLM_64BIT) || defined( FLM_OSX) + 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 long FLMSIZET; + typedef unsigned int FLMUINT32; + #elif _MSC_VER >= 1300 + typedef unsigned long __w64 FLMUINT; + typedef long __w64 FLMINT; + typedef unsigned int FLMUINT32; + #else + typedef unsigned long FLMUINT; + typedef long FLMINT; + typedef unsigned int FLMUINT32; + #endif + #elif defined( FLM_NLM) + typedef unsigned long int FLMUINT; + typedef long int FLMINT; + typedef unsigned long int FLMUINT32; + #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; + typedef unsigned FLMSIZET; + + #if defined( __MWERKS__) + typedef unsigned long long FLMUINT64; + typedef long long FLMINT64; + #else + typedef unsigned __int64 FLMUINT64; + typedef __int64 FLMINT64; + #endif + + #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]; + } XFLM_GUID; + + #define RXFLMIID const XFLM_GUID & + #define RXFLMCLSID const XFLM_GUID & + #define XFLMGUID XFLM_GUID + #define XFLMCLSID XFLM_GUID + + // XFLM_DEFINE_GUID may be used to define or declare a GUID + // #define XFLM_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 XFLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const XFLMGUID name + #else + #define XFLM_DEFINE_GUID( name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + extern const xpcselany XFLMGUID name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + #endif + + // Make sure we define XFLMAPI based on the way + // it is defined in PSA (see psaapi.h) + + #if defined( FLM_WIN) + #define XFLMAPI __stdcall + #ifdef FLM_DEBUG + #define FINLINE inline + #else + #define FINLINE __forceinline + #endif + #elif defined( FLM_NLM) + #define XFLMAPI __stdcall + #define FINLINE inline + #elif defined( FLM_UNIX) + #define XFLMAPI + #define FINLINE inline + #else + #error Platform not supported + #endif + + #define XFLMEXTC extern "C" + + // xflmnovtbl keeps MS compilers from generating vtables for interfaces + + #ifdef _MSC_VER + #define xflmnovtbl __declspec( novtable) + #else + #define xflmnovtbl + #endif + + #define xflminterface struct xflmnovtbl + + XFLM_DEFINE_GUID( Internal_IID_XFLMIUnknown, 0x00000000, 0x0000, 0x0000, + 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46); + + xflminterface XFLMIUnknown + { + virtual RCODE XFLMAPI QueryInterface( + RXFLMIID riid, + void ** ppv) = 0; + + virtual FLMUINT32 XFLMAPI AddRef( void) = 0; + + virtual FLMUINT32 XFLMAPI Release( void) = 0; + }; + + // XFLMIClassFactory + // uuid: 00000001-0000-0000-C000-000000000046 (same as MSCOM IClassFactory) + + XFLM_DEFINE_GUID( Internal_IID_XFLMIClassFactory, 0x00000001, 0x0000, 0x0000, + 0xC0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x46); + + xflminterface XFLMIClassFactory : public XFLMIUnknown + { + virtual RCODE XFLMAPI CreateInstance( + XFLMIUnknown * piuouter, + RXFLMIID riid, + void ** ppv) = 0; + + virtual RCODE XFLMAPI LockServer( + bool lockf) = 0; + }; + + /**************************************************************************** + Forward References + ****************************************************************************/ + xflminterface IF_Backup; + xflminterface IF_DataVector; + xflminterface IF_Db; + xflminterface IF_DbInfo; + xflminterface IF_DirHdl; + xflminterface IF_DOMNode; + xflminterface IF_FileHdl; + xflminterface IF_FileSystem; + xflminterface IF_IStream; + xflminterface IF_PosIStream; + xflminterface IF_ResultSet; + xflminterface IF_Query; + xflminterface IF_ThreadInfo; + xflminterface IF_Pool; + xflminterface IF_DynaBuf; + xflminterface IF_NodeInfo; + xflminterface IF_BTreeInfo; + + // These are interfaces that need to be implemented by + // applications. XFlaim uses them to report status or to do + // callbacks of various kinds. + + xflminterface IF_OStream; + xflminterface IF_BackupClient; + xflminterface IF_BackupStatus; + xflminterface IF_CommitClient; + xflminterface IF_DbCheckStatus; + xflminterface IF_DbCopyStatus; + xflminterface IF_DbRebuildStatus; + xflminterface IF_DbRenameStatus; + xflminterface IF_DeleteStatus; + xflminterface IF_EventClient; + xflminterface IF_IxClient; + xflminterface IF_IxStatus; + xflminterface IF_LockInfoClient; + xflminterface IF_LoggerClient; + xflminterface IF_LogMessageClient; + xflminterface IF_OperandComparer; + xflminterface IF_RestoreClient; + xflminterface IF_RestoreStatus; + xflminterface IF_ResultSetSortStatus; + xflminterface IF_ResultSetCompare; + xflminterface IF_QueryStatus; + xflminterface IF_QueryValidator; + xflminterface IF_QueryValFunc; + xflminterface IF_QueryNodeSource; + xflminterface IF_UpgradeClient; + xflminterface IF_BTreeInfoStatus; + + /**************************************************************************** + 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 XFLM_US_LANG 0 // English, United States + #define XFLM_AF_LANG 1 // Afrikaans + #define XFLM_AR_LANG 2 // Arabic + #define XFLM_CA_LANG 3 // Catalan + #define XFLM_HR_LANG 4 // Croatian + #define XFLM_CZ_LANG 5 // Czech + #define XFLM_DK_LANG 6 // Danish + #define XFLM_NL_LANG 7 // Dutch + #define XFLM_OZ_LANG 8 // English, Australia + #define XFLM_CE_LANG 9 // English, Canada + #define XFLM_UK_LANG 10 // English, United Kingdom + #define XFLM_FA_LANG 11 // Farsi + #define XFLM_SU_LANG 12 // Finnish + #define XFLM_CF_LANG 13 // French, Canada + #define XFLM_FR_LANG 14 // French, France + #define XFLM_GA_LANG 15 // Galician + #define XFLM_DE_LANG 16 // German, Germany + #define XFLM_SD_LANG 17 // German, Switzerland + #define XFLM_GR_LANG 18 // Greek + #define XFLM_HE_LANG 19 // Hebrew + #define XFLM_HU_LANG 20 // Hungarian + #define XFLM_IS_LANG 21 // Icelandic + #define XFLM_IT_LANG 22 // Italian + #define XFLM_NO_LANG 23 // Norwegian + #define XFLM_PL_LANG 24 // Polish + #define XFLM_BR_LANG 25 // Portuguese, Brazil + #define XFLM_PO_LANG 26 // Portuguese, Portugal + #define XFLM_RU_LANG 27 // Russian + #define XFLM_SL_LANG 28 // Slovak + #define XFLM_ES_LANG 29 // Spanish + #define XFLM_SV_LANG 30 // Swedish + #define XFLM_YK_LANG 31 // Ukrainian + #define XFLM_UR_LANG 32 // Urdu + #define XFLM_TK_LANG 33 // Turkey + #define XFLM_JP_LANG 34 // Japanese + #define XFLM_KO_LANG 35 // Korean + #define XFLM_CT_LANG 36 // Chinese-Traditional + #define XFLM_CS_LANG 37 // Chinese-Simplified + #define XFLM_LA_LANG 38 // another Asian language + + /**************************************************************************** + Desc: This structure is used as a parameter to dbCreate to specify + the create options for a database. It is also optionally returned + when calling dbOpen. + ****************************************************************************/ + typedef struct + { + FLMUINT uiBlockSize; + #define XFLM_DEFAULT_BLKSIZ 4096 + + FLMUINT uiVersionNum; // Database version number + #define XFLM_VER_5_12 512 + #define XFLM_CURRENT_VERSION_NUM XFLM_VER_5_12 + #define XFLM_CURRENT_VER_STR "5.12" + + FLMUINT uiMinRflFileSize; // Minimum bytes per RFL file + #define XFLM_DEFAULT_MIN_RFL_FILE_SIZE ((FLMUINT)100 * (FLMUINT)1024 * (FLMUINT)1024) + FLMUINT uiMaxRflFileSize; // Maximum bytes per RFL file + #define XFLM_DEFAULT_MAX_RFL_FILE_SIZE XFLM_MAXIMUM_FILE_SIZE + FLMBOOL bKeepRflFiles; // Keep RFL files? + #define XFLM_DEFAULT_KEEP_RFL_FILES_FLAG FALSE + FLMBOOL bLogAbortedTransToRfl; // Log aborted transactions to RFL? + #define XFLM_DEFAULT_LOG_ABORTED_TRANS_FLAG FALSE + + FLMUINT uiDefaultLanguage; + #define XFLM_DEFAULT_LANG (XFLM_US_LANG) + + } XFLM_CREATE_OPTS, F_CREATE_OPTS; + + 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 " FDB.ui64CurrTransID + // ui32LogicalEOF --> FDB.uiLogicalEOF + // ui32FirstAvailBlkAddr --> FDB.uiFirstAvailBlkAddr; + // IMPORTANT NOTE: The following items cannot currently be changed + // during an update transaction: + // ui32DbVersion + // ui16BlockSize + // ui8DefaultLanguage + // ui32MaxFileSize + // ui32FirstLFBlkAddr + // This is because they are always accessed from pFile->lastCommittedDbHdr + // regardless of transaction type. If we ever want to change them in an + // update transaction, we will need to modify flmGetDbHdrInfo to copy them + // into the FDB, and then only access them from within the FDB. + + FLMUINT32 ui32DbVersion; // LOG_FLAIM_VERSION + FLMUINT8 ui8BlkChkSummingEnabled; // Is block checksumming enabled? + // Not currently used. + FLMUINT8 ui8RflKeepFiles; // LOG_KEEP_RFL_FILES + FLMUINT8 ui8RflAutoTurnOffKeep; // LOG_AUTO_TURN_OFF_KEEP_RFL + FLMUINT8 ui8RflKeepAbortedTrans; // LOG_KEEP_ABORTED_TRANS_IN_RFL + FLMUINT32 ui32RflCurrFileNum; // LOG_RFL_FILE_NUM + FLMUINT64 ui64LastRflCommitID; // LOG_LAST_RFL_COMMIT_ID + FLMUINT32 ui32RflLastFileNumDeleted; // LOG_LAST_RFL_FILE_DELETED + FLMUINT32 ui32RflLastTransOffset; // LOG_RFL_LAST_TRANS_OFFSET + FLMUINT32 ui32RflLastCPFileNum; // LOG_RFL_LAST_CP_FILE_NUM + FLMUINT32 ui32RflLastCPOffset; // LOG_RFL_LAST_CP_OFFSET + FLMUINT64 ui64RflLastCPTransID; // LOG_LAST_CP_TRANS_ID + FLMUINT32 ui32RflMinFileSize; // LOG_RFL_MIN_FILE_SIZE + FLMUINT32 ui32RflMaxFileSize; // LOG_RFL_MAX_FILE_SIZE + FLMUINT64 ui64CurrTransID; // LOG_CURR_TRANS_ID + FLMUINT64 ui64TransCommitCnt; // LOG_COMMIT_COUNT + FLMUINT32 ui32RblEOF; // LOG_ROLLBACK_EOF + FLMUINT32 ui32RblFirstCPBlkAddr; // LOG_PL_FIRST_CP_BLOCK_ADDR + FLMUINT32 ui32FirstAvailBlkAddr; // LOG_PF_AVAIL_BLKS + FLMUINT32 ui32FirstLFBlkAddr; // First logical file block. + FLMUINT32 ui32LogicalEOF; // LOG_LOGICAL_EOF + FLMUINT32 ui32MaxFileSize; // LOG_MAX_FILE_SIZE + FLMUINT64 ui64LastBackupTransID; // LOG_LAST_BACKUP_TRANS_ID + FLMUINT32 ui32IncBackupSeqNum; // LOG_INC_BACKUP_SEQ_NUM + FLMUINT32 ui32BlksChangedSinceBackup;// LOG_BLK_CHG_SINCE_BACKUP + + #define XFLM_SERIAL_NUM_SIZE 16 + + FLMBYTE ucDbSerialNum[ XFLM_SERIAL_NUM_SIZE]; + // LOG_DB_SERIAL_NUM + FLMBYTE ucLastTransRflSerialNum[ XFLM_SERIAL_NUM_SIZE]; + // LOG_LAST_TRANS_RFL_SERIAL_NUM + FLMBYTE ucNextRflSerialNum[ XFLM_SERIAL_NUM_SIZE]; + // LOG_RFL_NEXT_SERIAL_NUM + FLMBYTE ucIncBackupSerialNum[ XFLM_SERIAL_NUM_SIZE]; + // LOG_INC_BACKUP_SERIAL_NUM + FLMUINT32 ui32DbKeyLen; // LOG_DATABASE_KEY_LEN + + // IMPORTANT NOTE: If anything is changed in here, need to make + // corresponding changes to convertDbHdr routine and + // flmVerifyDiskStructOffsets routine. + + FLMBYTE ucReserved[ 64]; // Reserved for future + // Always initialized to zero + + // Checksum should ALWAYS be last + + FLMUINT32 ui32HdrCRC; // LOG_HDR_CHECKSUM + + // Encryption Key stuff + + #define XFLM_MAX_ENC_KEY_SIZE 256 + + FLMBYTE DbKey[ XFLM_MAX_ENC_KEY_SIZE]; + // LOG_DATABASE_KEY + + // Offsets of variables in the structure + + #define XFLM_DB_HDR_szSignature_OFFSET 0 + #define XFLM_DB_HDR_ui8IsLittleEndian_OFFSET 8 + #define XFLM_DB_HDR_ui8DefaultLanguage_OFFSET 9 + #define XFLM_DB_HDR_ui16BlockSize_OFFSET 10 + #define XFLM_DB_HDR_ui32DbVersion_OFFSET 12 + #define XFLM_DB_HDR_ui8BlkChkSummingEnabled_OFFSET 16 + #define XFLM_DB_HDR_ui8RflKeepFiles_OFFSET 17 + #define XFLM_DB_HDR_ui8RflAutoTurnOffKeep_OFFSET 18 + #define XFLM_DB_HDR_ui8RflKeepAbortedTrans_OFFSET 19 + #define XFLM_DB_HDR_ui32RflCurrFileNum_OFFSET 20 + #define XFLM_DB_HDR_ui64LastRflCommitID_OFFSET 24 + #define XFLM_DB_HDR_ui32RflLastFileNumDeleted_OFFSET 32 + #define XFLM_DB_HDR_ui32RflLastTransOffset_OFFSET 36 + #define XFLM_DB_HDR_ui32RflLastCPFileNum_OFFSET 40 + #define XFLM_DB_HDR_ui32RflLastCPOffset_OFFSET 44 + #define XFLM_DB_HDR_ui64RflLastCPTransID_OFFSET 48 + #define XFLM_DB_HDR_ui32RflMinFileSize_OFFSET 56 + #define XFLM_DB_HDR_ui32RflMaxFileSize_OFFSET 60 + #define XFLM_DB_HDR_ui64CurrTransID_OFFSET 64 + #define XFLM_DB_HDR_ui64TransCommitCnt_OFFSET 72 + #define XFLM_DB_HDR_ui32RblEOF_OFFSET 80 + #define XFLM_DB_HDR_ui32RblFirstCPBlkAddr_OFFSET 84 + #define XFLM_DB_HDR_ui32FirstAvailBlkAddr_OFFSET 88 + #define XFLM_DB_HDR_ui32FirstLFBlkAddr_OFFSET 92 + #define XFLM_DB_HDR_ui32LogicalEOF_OFFSET 96 + #define XFLM_DB_HDR_ui32MaxFileSize_OFFSET 100 + #define XFLM_DB_HDR_ui64LastBackupTransID_OFFSET 104 + #define XFLM_DB_HDR_ui32IncBackupSeqNum_OFFSET 112 + #define XFLM_DB_HDR_ui32BlksChangedSinceBackup_OFFSET 116 + #define XFLM_DB_HDR_ucDbSerialNum_OFFSET 120 + #define XFLM_DB_HDR_ucLastTransRflSerialNum_OFFSET 136 + #define XFLM_DB_HDR_ucNextRflSerialNum_OFFSET 152 + #define XFLM_DB_HDR_ucIncBackupSerialNum_OFFSET 168 + #define XFLM_DB_HDR_ui32DbKeyLen 184 + #define XFLM_DB_HDR_ucReserved_OFFSET 188 + #define XFLM_DB_HDR_ui32HdrCRC_OFFSET 252 + #define XFLM_DB_HDR_DbKey 256 + } XFLM_DB_HDR; + + /**************************************************************************** + Desc: IO Flags + ****************************************************************************/ + #define XFLM_IO_CURRENT_POS FLM_MAX_UINT64 + + #define XFLM_IO_RDONLY 0x0001 + #define XFLM_IO_RDWR 0x0002 + #define XFLM_IO_EXCL 0x0004 + #define XFLM_IO_CREATE_DIR 0x0008 + #define XFLM_IO_SH_DENYRW 0x0010 + #define XFLM_IO_SH_DENYWR 0x0020 + #define XFLM_IO_SH_DENYNONE 0x0040 + #define XFLM_IO_DIRECT 0x0080 + + // File Positioning Definitions + + #define XFLM_IO_SEEK_SET 0 // Beginning of File + #define XFLM_IO_SEEK_CUR 1 // Current File Pointer Position + #define XFLM_IO_SEEK_END 2 // End of File + + // uiFlags values for keyRetrieve() method + + #define XFLM_INCL 0x0010 + #define XFLM_EXCL 0x0020 + #define XFLM_EXACT 0x0040 + #define XFLM_KEY_EXACT 0x0080 + #define XFLM_FIRST 0x0100 + #define XFLM_LAST 0x0200 + #define XFLM_MATCH_IDS 0x0400 + #define XFLM_MATCH_DOC_ID 0x0800 + + // Flags used by openDb method + + #define XFLM_ONLINE 0x0020 + #define XFLM_DONT_REDO_LOG 0x0040 + #define XFLM_DONT_RESUME_THREADS 0x0080 + #define XFLM_DO_LOGICAL_CHECK 0x0100 + #define XFLM_SKIP_DOM_LINK_CHECK 0x0200 // Used only in dbCheck. + #define XFLM_ALLOW_LIMITED_MODE 0x0400 + + // Maximum file size + + #define XFLM_MAXIMUM_FILE_SIZE 0xFFFC0000 + + // Maximum key size + + #define XFLM_MAX_KEY_SIZE 1024 + + // Node types. The order of these enums + // must be preserved as the code sometimes + // depends on the order to test ranges of + // node types. + + 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; + + // NOTE: The eNodeInsertLoc enum values cannot + // be changed. The RFL uses these values + // when logging packets. + + typedef enum + { + XFLM_ROOT = 0, + XFLM_FIRST_CHILD, + XFLM_LAST_CHILD, + XFLM_PREV_SIB, + XFLM_NEXT_SIB, + XFLM_ATTRIBUTE + } eNodeInsertLoc; + + // NOTE: The order of the eDbTransType enum values + // cannot be changed. XFLAIM tests ranges of these + // values. + + typedef enum + { + XFLM_NO_TRANS = 0, + XFLM_READ_TRANS, + XFLM_UPDATE_TRANS + } eDbTransType; + + typedef enum + { + XFLM_LOCK_NONE = 0, + XFLM_LOCK_EXCLUSIVE, + XFLM_LOCK_SHARED + } eDbLockType; + + // Transaction flags + + #define XFLM_DONT_KILL_TRANS 0x1 + #define XFLM_DONT_POISON_CACHE 0x2 + + // Defines used for uiMaxLockWait parameter + + #define XFLM_NO_TIMEOUT 0xFF + + // Backup types + + typedef enum + { + // These values are stored in the header of the + // backup, so do not change their values. + XFLM_FULL_BACKUP = 0, + XFLM_INCREMENTAL_BACKUP + } eDbBackupType; + + // FLAIM Data types. + + #define XFLM_NODATA_TYPE 0 + #define XFLM_TEXT_TYPE 1 + #define XFLM_NUMBER_TYPE 2 + #define XFLM_BINARY_TYPE 3 + #define XFLM_NUM_OF_TYPES 4 + #define XFLM_UNKNOWN_TYPE 0xF + + #define XFLM_STRING_OPTION_STR "string" + #define XFLM_INTEGER_OPTION_STR "integer" + #define XFLM_BINARY_OPTION_STR "binary" + #define XFLM_NODATA_OPTION_STR "nodata" + #define XFLM_CHECKING_OPTION_STR "checking" + #define XFLM_PURGE_OPTION_STR "purge" + #define XFLM_ACTIVE_OPTION_STR "active" + #define XFLM_INDEX_SUSPENDED_STR "suspended" + #define XFLM_INDEX_OFFLINE_STR "offline" + #define XFLM_INDEX_ONLINE_STR "online" + #define XFLM_CASE_INSENSITIVE_OPTION_STR "caseinsensitive" + #define XFLM_DESCENDING_OPTION_STR "sortdescending" + #define XFLM_MISSING_HIGH_OPTION_STR "sortmissinghigh" + #define XFLM_MINSPACES_OPTION_STR "minspaces" + #define XFLM_WHITESPACE_AS_SPACE_STR "whitespaceasspace" + #define XFLM_IGNORE_LEADINGSPACES_OPTION_STR "ignoreleadingspaces" + #define XFLM_IGNORE_TRAILINGSPACES_OPTION_STR "ignoretrailingspaces" + #define XFLM_NOUNDERSCORE_OPTION_STR "nounderscores" + #define XFLM_NOSPACE_OPTION_STR "nospaces" + #define XFLM_NODASH_OPTION_STR "nodashes" + #define XFLM_VALUE_OPTION_STR "value" + #define XFLM_PRESENCE_OPTION_STR "presence" + #define XFLM_SUBSTRING_OPTION_STR "substring" + #define XFLM_EACHWORD_OPTION_STR "eachword" + #define XFLM_ABS_POS_OPTION_STR "abspos" + #define XFLM_METAPHONE_OPTION_STR "metaphone" + + // Encryption Schemes + + #define XFLM_ENC_AES_OPTION_STR "aes" + #define XFLM_ENC_DES3_OPTION_STR "des3" + + // Recovery actions + + typedef enum + { + XFLM_RESTORE_ACTION_CONTINUE = 0, // Continue recovery + XFLM_RESTORE_ACTION_STOP, // Stop recovery + XFLM_RESTORE_ACTION_SKIP, // Skip operation (future) + XFLM_RESTORE_ACTION_RETRY // Retry the operation + } eRestoreAction; + + // Events + + typedef enum + { + XFLM_EVENT_LOCKS, + XFLM_EVENT_UPDATES, + XFLM_MAX_EVENT_CATEGORIES // Should always be last. + } eEventCategory; + + typedef enum + { + XFLM_EVENT_LOCK_WAITING, + XFLM_EVENT_LOCK_GRANTED, + XFLM_EVENT_LOCK_SUSPENDED, + XFLM_EVENT_LOCK_RESUMED, + XFLM_EVENT_LOCK_RELEASED, + XFLM_EVENT_LOCK_TIMEOUT, + XFLM_EVENT_BEGIN_TRANS, + XFLM_EVENT_COMMIT_TRANS, + XFLM_EVENT_ABORT_TRANS, + XFLM_EVENT_CREATE_NODE, + XFLM_EVENT_MODIFY_NODE, + XFLM_EVENT_DELETE_NODE, + XFLM_EVENT_LINK_NODE, + XFLM_EVENT_UNLINK_NODE, + XFLM_EVENT_INDEXING_PROGRESS, + XFLM_MAX_EVENT_TYPES // Should always be last. + } eEventType; + + // Logical files + + typedef enum + { + XFLM_LF_INVALID = 0, + XFLM_LF_COLLECTION, + XFLM_LF_INDEX + } eLFileType; + + // Message logging + + typedef enum + { + XFLM_QUERY_MESSAGE, + XFLM_TRANSACTION_MESSAGE, + XFLM_GENERAL_MESSAGE, + XFLM_NUM_MESSAGE_TYPES + } eLogMessageType; + + typedef enum + { + XFLM_CURRENT_COLOR, + XFLM_BLACK, + XFLM_BLUE, + XFLM_GREEN, + XFLM_CYAN, + XFLM_RED, + XFLM_PURPLE, + XFLM_BROWN, + XFLM_LIGHTGRAY, + XFLM_DARKGRAY, + XFLM_LIGHTBLUE, + XFLM_LIGHTGREEN, + XFLM_LIGHTCYAN, + XFLM_LIGHTRED, + XFLM_LIGHTPURPLE, + XFLM_YELLOW, + XFLM_WHITE, + XFLM_NUM_COLORS + } eColorType; + + typedef struct + { + FLMBOOL bRunning; + FLMUINT uiRunningTime; + FLMBOOL bForcingCheckpoint; + FLMUINT uiForceCheckpointRunningTime; + FLMINT iForceCheckpointReason; + #define XFLM_CP_TIME_INTERVAL_REASON 1 + #define XFLM_CP_SHUTTING_DOWN_REASON 2 + #define XFLM_CP_RFL_VOLUME_PROBLEM 3 + FLMBOOL bWritingDataBlocks; + FLMUINT uiLogBlocksWritten; + FLMUINT uiDataBlocksWritten; + FLMUINT uiDirtyCacheBytes; + FLMUINT uiBlockSize; + FLMUINT uiWaitTruncateTime; + } XFLM_CHECKPOINT_INFO; + + typedef struct + { + FLMUINT64 ui64Slabs; + FLMUINT64 ui64SlabBytes; + FLMUINT64 ui64AllocatedCells; + FLMUINT64 ui64FreeCells; + } XFLM_SLAB_USAGE; + + typedef struct + { + FLMUINT uiByteCount; + FLMUINT uiCount; + FLMUINT uiOldVerCount; + FLMUINT uiOldVerBytes; + FLMUINT uiCacheHits; + FLMUINT uiCacheHitLooks; + FLMUINT uiCacheFaults; + FLMUINT uiCacheFaultLooks; + XFLM_SLAB_USAGE slabUsage; + } XFLM_CACHE_USAGE; + + typedef struct + { + FLMUINT64 ui64TotalExtendedMemory; + FLMUINT64 ui64RemainingExtendedMemory; + FLMUINT64 ui64TotalBytesAllocated; + FLMUINT64 ui64CacheHits; + FLMUINT64 ui64CacheFaults; + } XFLM_ECACHE_USAGE; + + typedef struct + { + FLMUINT uiMaxBytes; + FLMUINT uiTotalBytesAllocated; + FLMBOOL bDynamicCacheAdjust; + FLMUINT uiCacheAdjustPercent; + FLMUINT uiCacheAdjustMin; + FLMUINT uiCacheAdjustMax; + FLMUINT uiCacheAdjustMinToLeave; + FLMUINT uiDirtyCount; + FLMUINT uiDirtyBytes; + FLMUINT uiNewCount; + FLMUINT uiNewBytes; + FLMUINT uiLogCount; + FLMUINT uiLogBytes; + FLMUINT uiFreeCount; + FLMUINT uiFreeBytes; + FLMUINT uiReplaceableCount; + FLMUINT uiReplaceableBytes; + FLMBOOL bPreallocatedCache; + XFLM_CACHE_USAGE BlockCache; + XFLM_CACHE_USAGE NodeCache; + XFLM_ECACHE_USAGE ECache; + } XFLM_CACHE_INFO; + + typedef struct + { + FLMUINT64 ui64Count; // Number of times operation + // was performed. + FLMUINT64 ui64ElapMilli; // Total elapsed time for the + // operations. + } XFLM_COUNT_TIME_STAT; + + typedef struct + { + FLMUINT64 ui64Count; // Number of times read or + // write operation was + // performed. + FLMUINT64 ui64TotalBytes; // Total number of bytes + // involved in the read or + // write operations. + FLMUINT64 ui64ElapMilli; // Total elapsed time for the + // read or write operations. + } XFLM_DISKIO_STAT; + + typedef struct + { + XFLM_COUNT_TIME_STAT CommittedTrans; // Transactions committed + XFLM_COUNT_TIME_STAT AbortedTrans; // Transactions aborted + } XFLM_RTRANS_STATS; + + typedef struct + { + XFLM_COUNT_TIME_STAT CommittedTrans; // Transactions committed + XFLM_COUNT_TIME_STAT GroupCompletes; // Group completes. + FLMUINT64 ui64GroupFinished; // Transactions finished in group + XFLM_COUNT_TIME_STAT AbortedTrans; // Transactions aborted + } XFLM_UTRANS_STATS; + + typedef struct + { + XFLM_DISKIO_STAT BlockReads; // Statistics on block reads. + XFLM_DISKIO_STAT OldViewBlockReads; // Statistics on old view + // block reads. + FLMUINT uiBlockChkErrs; // Number of times we had + // check errors reading + // blocks. + FLMUINT uiOldViewBlockChkErrs; // Number of times we had + // check errors reading an + // old view of a block. + FLMUINT uiOldViewErrors; // Number of times we had an + // old view error when + // reading. + XFLM_DISKIO_STAT BlockWrites; // Statistics on Block writes. + } XFLM_BLOCKIO_STATS; + + typedef struct + { + FLMBOOL bHaveStats; // Flag indicating whether or + // not there are statistics + // for this LFILE. + XFLM_BLOCKIO_STATS RootBlockStats; // Block I/O statistics for root + // blocks. + XFLM_BLOCKIO_STATS MiddleBlockStats; // Block I/O statistics for + // blocks that are not root + // blocks or leaf blocks. + XFLM_BLOCKIO_STATS LeafBlockStats; // Block I/O statistics for leaf + // blocks. + FLMUINT64 ui64BlockSplits; // Number of block splits that + // have occurred in this logical + // file. + FLMUINT64 ui64BlockCombines; // Number of block combines that + // have occurred in this LFile + FLMUINT uiLFileNum; // Logical file number. + eLFileType eLfType; // Logical file type. + } XFLM_LFILE_STATS; + + typedef struct + { + char * pszDbName; // Database name - from pFile. + FLMBOOL bHaveStats; // Flag indicating whether or + // not there are statistics + // for this database. + XFLM_RTRANS_STATS ReadTransStats; // Read Transaction + // Statistics. + XFLM_UTRANS_STATS UpdateTransStats; // Update Transaction + // Statistics. + FLMUINT uiLFileAllocSeq; // Allocation sequence number + // for LFILE array. + XFLM_LFILE_STATS * pLFileStats; // Pointer to LFILE statistics + // array. + FLMUINT uiLFileStatArraySize; // Size of LFILE statistics + // array. + FLMUINT uiNumLFileStats; // Number of elements in LFILE + // array currently in use. + XFLM_BLOCKIO_STATS LFHBlockStats; // Block I/O statistics for + // LFH blocks. + XFLM_BLOCKIO_STATS AvailBlockStats; // Block I/O statistics for + // AVAIL blocks. + + // Write statistics + + XFLM_DISKIO_STAT DbHdrWrites; // Statistics on DB header + // writes. + XFLM_DISKIO_STAT LogBlockWrites; // Statistics on log block + // writes + XFLM_DISKIO_STAT LogBlockRestores; // Statistics on log block + // restores + + // Read statistics. + + XFLM_DISKIO_STAT LogBlockReads; // Statistics on log block + // reads. + FLMUINT uiLogBlockChkErrs; // Number of times we had + // checksum errors reading + // blocks from the rollback + // log. + FLMUINT uiReadErrors; // Number of times we got read + // errors. + FLMUINT uiWriteErrors; // Number of times we got write + // errors. + // Lock statistics + + XFLM_COUNT_TIME_STAT NoLocks; // Times when no lock was held + XFLM_COUNT_TIME_STAT WaitingForLock; // Time waiting for lock + XFLM_COUNT_TIME_STAT HeldLock; // Time holding lock + + } XFLM_DB_STATS; + + typedef struct + { + XFLM_DB_STATS * pDbStats; // Pointer to array of database + // statistics. + FLMUINT uiDBAllocSeq; // Allocation sequence number + // for database statistics. + FLMUINT uiDbStatArraySize; // Size of the database statistics + // array. + FLMUINT uiNumDbStats; // Number of elements in the + // database statistics array that + // are currently in use. + FLMBOOL bCollectingStats; // Flag indicating whether or + // not we are currently + // collecting statistics. + FLMUINT uiStartTime; // Time we started collecting + // statistics. + FLMUINT uiStopTime; // Time we stopped collecting + // statistics. + } XFLM_STATS; + + typedef enum + { + XFLM_INDEX_ONLINE = 0, + XFLM_INDEX_BRINGING_ONLINE, + XFLM_INDEX_SUSPENDED + } eXFlmIndexState; + + typedef struct + { + FLMUINT uiIndexNum; // Index number + eXFlmIndexState eState; + + // Statistics when eState is INDEX_BRINGING_ONLINE + + FLMUINT uiStartTime; // Start time of the offline process or zero. + FLMUINT64 ui64LastDocumentIndexed;// If ~0 then index is online, + // otherwise this is the value of the last + // document ID that was indexed. + FLMUINT64 ui64KeysProcessed; // Keys processed for offline thread. + FLMUINT64 ui64DocumentsProcessed; // Documents processed for offline thread. + FLMUINT64 ui64Transactions; // Number of transactions started by the + // indexing thread + } XFLM_INDEX_STATUS; + + /**************************************************************************** + Desc: The following structures are used to pass data to the client via the + IF_DbRebuildStatus interface + ****************************************************************************/ + typedef struct + { + FLMINT iDoingFlag; + #define REBUILD_GET_BLK_SIZ 1 + #define REBUILD_RECOVER_DICT 2 + #define REBUILD_RECOVER_DATA 3 + FLMBOOL bStartFlag; + FLMUINT64 ui64FileSize; + FLMUINT64 ui64BytesExamined; + FLMUINT64 ui64TotNodes; + FLMUINT64 ui64NodesRecov; + FLMUINT64 ui64DiscardedDocs; + } XFLM_REBUILD_INFO; + + typedef struct + { + FLMINT iErrCode; // Zero means no error is being reported + FLMUINT uiErrLocale; + #define XFLM_LOCALE_NONE 0 + #define XFLM_LOCALE_LFH_LIST 1 + #define XFLM_LOCALE_AVAIL_LIST 2 + #define XFLM_LOCALE_B_TREE 3 + #define XFLM_LOCALE_INDEX 4 + FLMUINT uiErrLfNumber; + FLMUINT uiErrLfType; + FLMUINT uiErrBTreeLevel; + FLMUINT uiErrBlkAddress; + FLMUINT uiErrParentBlkAddress; + FLMUINT uiErrElmOffset; + FLMUINT64 ui64ErrNodeId; + + IF_DataVector * ifpErrIxKey; + } XFLM_CORRUPT_INFO; + + typedef struct + { + FLMINT iCheckPhase; + #define XFLM_CHECK_LFH_BLOCKS 1 + #define XFLM_CHECK_B_TREE 2 + #define XFLM_CHECK_AVAIL_BLOCKS 3 + #define XFLM_CHECK_RS_SORT 4 + #define XFLM_CHECK_DOM_LINKS 5 + FLMBOOL bStartFlag; + FLMUINT64 ui64FileSize; + FLMUINT uiNumLFs; + FLMUINT uiCurrLF; + FLMUINT uiLfNumber; /* Logical File Pass */ + FLMUINT uiLfType; + FLMUINT64 ui64BytesExamined; + FLMUINT uiNumProblemsFixed; /* Number of corruptions repaired */ + FLMUINT64 ui64NumDomNodes; /* in the current Lf */ + FLMUINT64 ui64NumDomLinksVerified; /* in the current Lf */ + FLMUINT64 ui64NumBrokenDomLinks; /* in the current Lf */ + + /* Index check progress */ + + FLMUINT64 ui64NumKeys; /* Number of keys in the result set */ + FLMUINT64 ui64NumDuplicateKeys; /* Number of duplicate keys generated */ + FLMUINT64 ui64NumKeysExamined; /* Number of keys checked */ + FLMUINT64 ui64NumKeysNotFound; /* Keys in index but missing from document */ + FLMUINT64 ui64NumDocKeysNotFound; /* Keys in documents but missing from indexes */ + FLMUINT64 ui64NumConflicts; /* # of non-corruption conflicts */ + } XFLM_PROGRESS_CHECK_INFO; + + /************************************************************************** + Desc: STUFF FOR IF_Query interface + **************************************************************************/ + typedef enum + { + XFLM_UNKNOWN_OP = 0, + + // NOTE: These operators MUST stay in this order - this order is assumed + // by the precedence table - see fquery.cpp + + XFLM_AND_OP = 1, + XFLM_OR_OP = 2, + XFLM_NOT_OP = 3, + XFLM_EQ_OP = 4, + XFLM_NE_OP = 5, + XFLM_APPROX_EQ_OP = 6, + XFLM_LT_OP = 7, + XFLM_LE_OP = 8, + XFLM_GT_OP = 9, + XFLM_GE_OP = 10, + XFLM_BITAND_OP = 11, + XFLM_BITOR_OP = 12, + XFLM_BITXOR_OP = 13, + XFLM_MULT_OP = 14, + XFLM_DIV_OP = 15, + XFLM_MOD_OP = 16, + XFLM_PLUS_OP = 17, + XFLM_MINUS_OP = 18, + XFLM_NEG_OP = 19, + XFLM_LPAREN_OP = 20, + XFLM_RPAREN_OP = 21, + XFLM_COMMA_OP = 22, + XFLM_LBRACKET_OP = 23, + XFLM_RBRACKET_OP = 24, + + // IMPORTANT NOTE: If operators are added after this point, + // modify the isLegalOperator method below. + + // The following operators are only used internally. They + // may NOT be passed into the addOperator method. + + XFLM_EXISTS_OP = 25, + XFLM_RANGE_OP = 26, + XFLM_MATCH_OP = 27 + } eQueryOperators; + + #define XFLM_FIRST_ARITH_OP XFLM_BITAND_OP + #define XFLM_LAST_ARITH_OP XFLM_NEG_OP + + // Comparison rules for strings + + #define XFLM_COMP_CASE_INSENSITIVE 0x0001 + #define XFLM_COMP_COMPRESS_WHITESPACE 0x0002 + // Compress consecutive spaces to single space + #define XFLM_COMP_NO_WHITESPACE 0x0004 + // Ignore all whitespace. This and + // COMP_COMPRESS_WHITESPACE cannot be used + // together. + #define XFLM_COMP_NO_UNDERSCORES 0x0008 + // Convert underscores to whitespace. NOTE: This + // should be applied before COMP_COMPRESS_WHITESPACE + // or COMP_NO_WHITESPACE + #define XFLM_COMP_NO_DASHES 0x0010 + // Remove all dashes + #define XFLM_COMP_WHITESPACE_AS_SPACE 0x0020 + // Convert tab, NL, and CR characters + // to space + #define XFLM_COMP_IGNORE_LEADING_SPACE 0x0040 + // Ignore leading space characters + #define XFLM_COMP_IGNORE_TRAILING_SPACE 0x0080 + // Ignore trailing space characters + + typedef enum + { + XFLM_QUERY_NOT_POSITIONED, + XFLM_QUERY_AT_BOF, + XFLM_QUERY_AT_FIRST, + XFLM_QUERY_AT_FIRST_AND_LAST, + XFLM_QUERY_POSITIONED, + XFLM_QUERY_AT_LAST, + XFLM_QUERY_AT_EOF + } eQueryStates; + + typedef enum + { + XFLM_FUNC_xxx = 0 + } eQueryFunctions; + + typedef enum + { + ROOT_AXIS = 0, + CHILD_AXIS, + PARENT_AXIS, + ANCESTOR_AXIS, + DESCENDANT_AXIS, + FOLLOWING_SIBLING_AXIS, + PRECEDING_SIBLING_AXIS, + FOLLOWING_AXIS, + PRECEDING_AXIS, + ATTRIBUTE_AXIS, + NAMESPACE_AXIS, + SELF_AXIS, + DESCENDANT_OR_SELF_AXIS, + ANCESTOR_OR_SELF_AXIS, + META_AXIS + } eXPathAxisTypes; + + typedef enum + { + XFLM_FALSE = 0, + XFLM_TRUE, + XFLM_UNKNOWN + } XFlmBoolType; + + typedef enum QueryValueTypes + { + XFLM_MISSING_VAL = 0, + + // WARNING: Don't renumber below _VAL enums without + // re-doing gv_DoValAndDictTypesMatch table + + XFLM_BOOL_VAL = 1, // 1 // XFlmBoolType + XFLM_UINT_VAL, // 2 // FLMUINT + XFLM_UINT64_VAL, // 3 // FLMUINT64 + XFLM_INT_VAL, // 4 // FLMINT + XFLM_INT64_VAL, // 5 // FLMINT64 + XFLM_BINARY_VAL, // 6 // FLMBYTE * + XFLM_UTF8_VAL, // 7 // FLMBYTE * + + // XFLM_PASSING_VAL passes all criteria. + + XFLM_PASSING_VAL = 0xFFFF + } eValTypes; + + typedef enum + { + GET_FIRST_VAL = 0, + GET_LAST_VAL, + GET_NEXT_VAL, + GET_PREV_VAL + } ValIterator; + + typedef enum + { + XFLM_QOPT_NONE = 0, + XFLM_QOPT_USING_INDEX, + XFLM_QOPT_FULL_COLLECTION_SCAN, + XFLM_QOPT_SINGLE_NODE_ID, + XFLM_QOPT_NODE_ID_RANGE + } eQOptTypes; + + typedef struct + { + eQOptTypes eOptType; // Type of optimization done + FLMUINT uiCost; // Cost calculated for predicate + FLMUINT64 ui64NodeId; // Only valid if eOptType is + // XFLM_QOPT_SINGLE_NODE_ID or + // XFLM_QOPT_NODE_ID_RANGE + FLMUINT64 ui64EndNodeId; // Only valid if eOptType is + // XFLM_QOPT_NODE_ID_RANGE + FLMUINT uiIxNum; // Index used to execute query if + // eOptType == QOPT_USING_INDEX + FLMBYTE szIxName[ 80]; + FLMBOOL bMustVerifyPath; // Must verify node path. + FLMBOOL bDoNodeMatch; // Node must be retrieved to exe + // query. Only valid if eOptType + // is XFLM_QOPT_USING_INDEX. + FLMUINT bCanCompareOnKey; // Can we compare on index keys? Only + // valid if eOptType == XFLM_QOPT_USING_INDEX. + FLMUINT64 ui64KeysRead; + FLMUINT64 ui64KeyHadDupDoc; + FLMUINT64 ui64KeysPassed; + FLMUINT64 ui64NodesRead; + FLMUINT64 ui64NodesTested; + FLMUINT64 ui64NodesPassed; + FLMUINT64 ui64DocsRead; + FLMUINT64 ui64DupDocsEliminated; + FLMUINT64 ui64NodesFailedValidation; + FLMUINT64 ui64DocsFailedValidation; + FLMUINT64 ui64DocsPassed; + } XFLM_OPT_INFO; + + /************************************************************************** + * XFLAIM Dictionary Tag Numbers + * + * NOTES: + * 1) These numbers cannot be changed for backward compatibility reasons. + * 2) IF ANY NEW TAGS ARE INSERTED - Then you MUST change the database + * version number, because old databases will become invalid..... + * + ***************************************************************************/ + + // Special purpose collections + // NOTE: Do not change the order of these definitions. The + // getNextCollection routine assumes they are in this order. + + // We have reserved from 65501 to 65535 for internal collections + // These should be allocated starting from 65535 and going down. + + #define XFLM_MAX_COLLECTION_NUM 65500 + + #define XFLM_MAINT_COLLECTION 65533 + #define XFLM_DATA_COLLECTION 65534 + #define XFLM_DICT_COLLECTION 65535 + + FINLINE FLMBOOL isDictCollection( + FLMUINT uiCollectionNum) + { + return( (uiCollectionNum == XFLM_DICT_COLLECTION) ? TRUE : FALSE); + } + + // Special purpose indexes + // NOTE: Do not change the order of these definitions. The + // getNextIndex routine assumes they are in this order. + + // We have reserved from 65501 to 65535 for internal indexes + // These should be allocated starting from 65535 and going down. + + #define XFLM_MAX_INDEX_NUM 65500 + #define XFLM_DICT_NUMBER_INDEX 65534 + #define XFLM_DICT_NAME_INDEX 65535 + + // This is the reserved dictionary document that maintains + // information about dictionary IDs + + #define XFLM_DICTINFO_DOC_ID ((FLMUINT64)1) + + // Prefixes + + #define XFLM_MAX_PREFIX_NUM 65500 + + // Encryption Defs + + #define XFLM_MAX_ENCDEF_NUM 65500 + + /**************************************************************************** + Dictionary Identifiers + ****************************************************************************/ + #define XFLM_FIRST_RESERVED_ELEMENT_TAG 0xFFFFFE00 + // Special definitions - cannot actually be used for an element name ID, but + // in indexing specifies the root tag + #define ELM_ROOT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG - 1) + + #define ELM_ELEMENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 0) + #define ELM_ELEMENT_TAG_NAME "element" + #define ELM_ATTRIBUTE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 1) + #define ELM_ATTRIBUTE_TAG_NAME "attribute" + #define ELM_INDEX_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 2) + #define ELM_INDEX_TAG_NAME "Index" + #define ELM_ELEMENT_COMPONENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 4) + #define ELM_ELEMENT_COMPONENT_TAG_NAME "ElementComponent" + #define ELM_ATTRIBUTE_COMPONENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 5) + #define ELM_ATTRIBUTE_COMPONENT_TAG_NAME "AttributeComponent" + #define ELM_COLLECTION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 6) + #define ELM_COLLECTION_TAG_NAME "Collection" + #define ELM_PREFIX_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 7) + #define ELM_PREFIX_TAG_NAME "Prefix" + #define ELM_NEXT_DICT_NUMS_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 8) + #define ELM_NEXT_DICT_NUMS_TAG_NAME "NextDictNums" + #define ELM_DOCUMENT_TITLE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 9) + #define ELM_DOCUMENT_TITLE_TAG_NAME "DocumentTitle" + #define ELM_INVALID_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 10) + #define ELM_INVALID_TAG_NAME "Invalid" + #define ELM_QUARANTINED_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 11) + #define ELM_QUARANTINED_TAG_NAME "Quarantined" + #define ELM_ALL_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 12) + #define ELM_ALL_TAG_NAME "All" + #define ELM_ANNOTATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 13) + #define ELM_ANNOTATION_TAG_NAME "Annotation" + #define ELM_ANY_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 14) + #define ELM_ANY_TAG_NAME "Any" + #define ELM_ATTRIBUTE_GROUP_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 15) + #define ELM_ATTRIBUTE_GROUP_TAG_NAME "AttributeGroup" + #define ELM_CHOICE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 16) + #define ELM_CHOICE_TAG_NAME "Choice" + #define ELM_COMPLEX_CONTENT_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 17) + #define ELM_COMPLEX_CONTENT_TAG_NAME "ComplexContent" + #define ELM_COMPLEX_TYPE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 18) + #define ELM_COMPLEX_TYPE_TAG_NAME "ComplexType" + #define ELM_DOCUMENTATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 19) + #define ELM_DOCUMENTATION_TAG_NAME "Documentation" + #define ELM_ENUMERATION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 20) + #define ELM_ENUMERATION_TAG_NAME "enumeration" + #define ELM_EXTENSION_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 21) + #define ELM_EXTENSION_TAG_NAME "extension" + #define ELM_DELETE_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 22) + #define ELM_DELETE_TAG_NAME "Delete" + #define ELM_BLOCK_CHAIN_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 23) + #define ELM_BLOCK_CHAIN_TAG_NAME "BlockChain" + #define ELM_ENCDEF_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 24) + #define ELM_ENCDEF_TAG_NAME "EncDef" + #define ELM_SWEEP_TAG (XFLM_FIRST_RESERVED_ELEMENT_TAG + 25) + #define ELM_SWEEP_TAG_NAME "Sweep" + + // IMPORTANT NOTE: Change this value whenever adding new reserved element tags! + #define XFLM_LAST_RESERVED_ELEMENT_TAG ELM_SWEEP_TAG + + + #define XFLM_FIRST_RESERVED_ATTRIBUTE_TAG 0xFFFFFE00 + + #define ATTR_DICT_NUMBER_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 0) + #define ATTR_DICT_NUMBER_TAG_NAME "DictNumber" + #define ATTR_COLLECTION_NUMBER_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1) + #define ATTR_COLLECTION_NUMBER_TAG_NAME "CollectionNumber" + #define ATTR_COLLECTION_NAME_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 2) + #define ATTR_COLLECTION_NAME_TAG_NAME "CollectionName" + #define ATTR_NAME_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 3) + #define ATTR_NAME_TAG_NAME "name" + #define ATTR_TARGET_NAMESPACE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 4) + #define ATTR_TARGET_NAMESPACE_TAG_NAME "targetNameSpace" + #define ATTR_TYPE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 5) + #define ATTR_TYPE_TAG_NAME "type" + #define ATTR_STATE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 6) + #define ATTR_STATE_TAG_NAME "State" + #define ATTR_LANGUAGE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 7) + #define ATTR_LANGUAGE_TAG_NAME "Language" + #define ATTR_INDEX_OPTIONS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 8) + #define ATTR_INDEX_OPTIONS_TAG_NAME "IndexOptions" + #define ATTR_INDEX_ON_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 9) + #define ATTR_INDEX_ON_TAG_NAME "IndexOn" + #define ATTR_REQUIRED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 10) + #define ATTR_REQUIRED_TAG_NAME "Required" + #define ATTR_LIMIT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 11) + #define ATTR_LIMIT_TAG_NAME "Limit" + #define ATTR_COMPARE_RULES_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 12) + #define ATTR_COMPARE_RULES_TAG_NAME "CompareRules" + #define ATTR_KEY_COMPONENT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 13) + #define ATTR_KEY_COMPONENT_TAG_NAME "KeyComponent" + #define ATTR_DATA_COMPONENT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 14) + #define ATTR_DATA_COMPONENT_TAG_NAME "DataComponent" + #define ATTR_LAST_DOC_INDEXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 15) + #define ATTR_LAST_DOC_INDEXED_TAG_NAME "LastDocumentIndexed" + #define ATTR_NEXT_ELEMENT_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 16) + #define ATTR_NEXT_ELEMENT_NUM_TAG_NAME "NextElementNum" + #define ATTR_NEXT_ATTRIBUTE_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 17) + #define ATTR_NEXT_ATTRIBUTE_NUM_TAG_NAME "NextAttributeNum" + #define ATTR_NEXT_INDEX_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 18) + #define ATTR_NEXT_INDEX_NUM_TAG_NAME "NextIndexNum" + #define ATTR_NEXT_COLLECTION_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 19) + #define ATTR_NEXT_COLLECTION_NUM_TAG_NAME "NextCollectionNum" + #define ATTR_NEXT_PREFIX_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 20) + #define ATTR_NEXT_PREFIX_NUM_TAG_NAME "NextPrefixNum" + #define ATTR_SOURCE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 21) + #define ATTR_SOURCE_TAG_NAME "Source" + #define ATTR_STATE_CHANGE_COUNT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 22) + #define ATTR_STATE_CHANGE_COUNT_TAG_NAME "StateChangeCount" + #define ATTR_XMLNS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 23) + #define ATTR_XMLNS_TAG_NAME "xmlns" + #define ATTR_ABSTRACT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 24) + #define ATTR_ABSTRACT_TAG_NAME "abstract" + #define ATTR_BASE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 25) + #define ATTR_BASE_TAG_NAME "base" + #define ATTR_BLOCK_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 26) + #define ATTR_BLOCK_TAG_NAME "block" + #define ATTR_DEFAULT_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 27) + #define ATTR_DEFAULT_TAG_NAME "default" + #define ATTR_FINAL_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 28) + #define ATTR_FINAL_TAG_NAME "final" + #define ATTR_FIXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 29) + #define ATTR_FIXED_TAG_NAME "fixed" + #define ATTR_ITEM_TYPE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 30) + #define ATTR_ITEM_TYPE_TAG_NAME "itemtype" + #define ATTR_MEMBER_TYPES_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 31) + #define ATTR_MEMBER_TYPES_TAG_NAME "membertypes" + #define ATTR_MIXED_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 32) + #define ATTR_MIXED_TAG_NAME "mixed" + #define ATTR_NILLABLE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 33) + #define ATTR_NILLABLE_TAG_NAME "nillable" + #define ATTR_REF_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 34) + #define ATTR_REF_TAG_NAME "ref" + #define ATTR_USE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 35) + #define ATTR_USE_TAG_NAME "use" + #define ATTR_VALUE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 36) + #define ATTR_VALUE_TAG_NAME "value" + #define ATTR_ADDRESS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 37) + #define ATTR_ADDRESS_TAG_NAME "address" + #define ATTR_XMLNS_XFLAIM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 38) + #define ATTR_XMLNS_XFLAIM_TAG_NAME "xmlns:xflaim" + #define ATTR_ENCRYPTION_KEY_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 39) + #define ATTR_ENCRYPTION_KEY_TAG_NAME "Key" + #define ATTR_TRANSACTION_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 40) + #define ATTR_TRANSACTION_TAG_NAME "Transaction" + #define ATTR_NEXT_ENCDEF_NUM_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 41) + #define ATTR_NEXT_ENCDEF_NUM_TAG_NAME "NextEncDefNum" + #define ATTR_ENCRYPTION_ID_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 42) + #define ATTR_ENCRYPTION_ID_TAG_NAME "encId" + #define ATTR_ENCRYPTION_KEY_SIZE_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 43) + #define ATTR_ENCRYPTION_KEY_SIZE_TAG_NAME "keySize" + #define ATTR_UNIQUE_SUB_ELEMENTS_TAG (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 44) + #define ATTR_UNIQUE_SUB_ELEMENTS_TAG_NAME "UniqueSubElements" + + // IMPORTANT NOTE: Change this value whenever adding new reserved attribute tags! + + #define XFLM_LAST_RESERVED_ATTRIBUTE_TAG ATTR_UNIQUE_SUB_ELEMENTS_TAG + + // max element number is first reserved -2 instead of -1 because we don't want + // anyone using ELM_ROOT_TAG either. + + #define XFLM_MAX_ELEMENT_NUM (XFLM_FIRST_RESERVED_ELEMENT_TAG - 2) + #define XFLM_MAX_ATTRIBUTE_NUM (XFLM_FIRST_RESERVED_ATTRIBUTE_TAG - 1) + + // Types of metadata available on DOM nodes - can index on these. + // Also, can search on these via the META_AXIS + + #define XFLM_META_NODE_ID 1 + #define XFLM_META_DOCUMENT_ID 2 + #define XFLM_META_PARENT_ID 3 + #define XFLM_META_FIRST_CHILD_ID 4 + #define XFLM_META_LAST_CHILD_ID 5 + #define XFLM_META_NEXT_SIBLING_ID 6 + #define XFLM_META_PREV_SIBLING_ID 7 + #define XFLM_META_VALUE 8 + + #define XFLM_INI_CACHE "cache" + #define XFLM_INI_CACHE_ADJUST_INTERVAL "cacheadjustinterval" + #define XFLM_INI_CACHE_CLEANUP_INTERVAL "cachecleanupinterval" + #define XFLM_INI_MAX_DIRTY_CACHE "maxdirtycache" + #define XFLM_INI_LOW_DIRTY_CACHE "lowdirtycache" + + /**************************************************************************** + Desc: Reference Counting class + ****************************************************************************/ + class XF_RefCount + { + public: + + XF_RefCount() + { + m_ui32RefCnt = 1; + } + + virtual ~XF_RefCount() + { + } + + virtual FINLINE FLMUINT getRefCount( void) + { + return( m_ui32RefCnt); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + m_ui32RefCnt++; + return m_ui32RefCnt; + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + FLMUINT32 ui32RefCnt = --m_ui32RefCnt; + + if( !ui32RefCnt) + { + delete this; + } + + return( ui32RefCnt); + } + + protected: + + FLMUINT32 m_ui32RefCnt; + }; + + // Class ID for the F_DbSystemFactory class (which is defined in fcompub.h). + // We don't need a separate interface definition because it implements the + // well-known PCIClassFactory interface. + + // {EBF905EE-43F1-45e1-A477-6C459AF26F76} + XFLM_DEFINE_GUID( Internal_CLSID_F_DbSystemFactory, 0xebf905ee, 0x43f1, + 0x45e1, 0xa4, 0x77, 0x6c, 0x45, 0x9a, 0xf2, 0x6f, 0x76); + + #define CLSID_F_DbSystemFactory (s_guid &)(Internal_CLSID_F_DbSystemFactory) + + // {B3A01545-F5F9-4618-AC6E-5FD606BF8F92} + XFLM_DEFINE_GUID(Internal_IID_IF_DbSystem, 0xb3a01545, 0xf5f9, 0x4618, + 0xac, 0x6e, 0x5f, 0xd6, 0x6, 0xbf, 0x8f, 0x92); + + #define IID_IF_DbSystem (s_guid &)(Internal_IID_IF_DbSystem) + + // Defaults for certain other settable items in the IF_DbSystem + + #define XFLM_DEFAULT_MAX_CP_INTERVAL 180 + #define XFLM_DEFAULT_CACHE_ADJUST_PERCENT 70 + #define XFLM_DEFAULT_CACHE_ADJUST_MIN (16 * 1024 * 1024) + #define XFLM_DEFAULT_CACHE_ADJUST_MAX 0xE0000000 + #define XFLM_DEFAULT_CACHE_ADJUST_MIN_TO_LEAVE 0 + #define XFLM_DEFAULT_CACHE_ADJUST_INTERVAL 15 + #define XFLM_DEFAULT_CACHE_CLEANUP_INTERVAL 15 + #define XFLM_DEFAULT_UNUSED_CLEANUP_INTERVAL 2 + #define XFLM_DEFAULT_MAX_UNUSED_TIME 120 + #define XFLM_DEFAULT_FILE_EXTEND_SIZE (8192 * 1024) + #define XFLM_MIN_BLOCK_SIZE 4096 + #define XFLM_MAX_BLOCK_SIZE 8192 + #define XFLM_DEFAULT_OPEN_THRESHOLD 100 // 100 file handles to cache + #define XFLM_DEFAULT_MAX_AVAIL_TIME 900 // 15 minutes + #define XFLM_DEFAULT_REHASH_BACKOFF_INTERVAL 60 // 1 minute + + /** + * @brief The IF_DbSystem is actually an interface definition that provides public access + * to the XFlaim database environment. + * + * This class represents the XFlaim database system. It extends XFLMIUnknown. + * The IF_DbSystem interface is used to represent the actual database system object. + * The database system object incorporates functionality to startup, shutdown, create, + * open and copy an XFlaim database. Note that the database system when XFlaim is + * running is more than the files that store the data. The database system also + * includes all of the in-memory structures and objects that are used, either + * intermittently or throughout the life of the database system. The class id for + * this interface is CLSID_F_DbSystemFactory and the interface id is IID_IF_DbSystem. + */ + xflminterface IF_DbSystem : public XFLMIUnknown + { + /** + * @brief Initializes the database system object. + * + * The init method is used to startup the database system engine. This routine may + * be called multiple times within the same process space. For each time the init + * method is called, there must be a corresponding call to exit. The database engine + * will only shut down on the last call to exit. + */ + virtual RCODE XFLMAPI init( void) = 0; + + virtual RCODE XFLMAPI updateIniFile( + const char * pszParamName, + const char * pszValue) = 0; + + /** + * @brief Shuts down the database system and closes any open databases. + * + * The exit method is used to shutdown the database system engine. This routine allows + * itself to be called multiple times, even before init is called or if the call to init fails. + */ + virtual void XFLMAPI exit() = 0; + + /** + * @brief Return an IF_FileSystem object for performing file system operations. + * + * The getFileSystem method returns an IF_FileSystem object that can be used to + * perform various operations on files. + * + * @param ppFileSystem A pointer to a file system object that can + * be used to perform various operations on files. + */ + virtual void XFLMAPI getFileSystem( + IF_FileSystem ** ppFileSystem) = 0; + + /** + * @brief Creates a new database. + * + * A pointer to a database object (IF_Db) is returned in the ppDb parameter. The database system + * engine must first be started using the init method. After creating a new XFlaim database, + * the database is open and ready to use. + * + * @param pszDbFileName Name of the control file for the database. See the XFlaim Concepts/Database + * Files for a discussion on the different database files, including the control file. + * @param pszDataDir The directory where the data files are to be created. If a NULL is passed in, + * data files will be created in the same directory as the control file (as specified by the + * pszDbFileName parameter). See the XFlaim Concepts/Database Files for a discussion on the different + * database files. + * @param pszRflDir The directory where the RFL (roll forward log) files are to be located. If a + * NULL is passed, roll forward log files will reside in the same directory as the control file + * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a + * discussion on the different database files, including the rfl (Roll-forward log) file. + * @param pszDictFileName Name of file containing dictionary definitions to be imported into the + * dictionary collection during database creation. Note that this parameter is ignored if pzDictBuf + * is non-NULL. See the XFlaim Concepts section for more information about the XFlaim Dictionary. + * @param pszDictBuf Null-terminated string buffer containing dictionary definitions in external + * XML format. If the value of this parameter is NULL, pszDictFileName will be used. If both + * pszDictFileName and pszDictBuf are NULL, the database will be created with an empty dictionary. + * See the XFlaim Concepts section for more information about the XFlaim Dictionary. + * @param pCreateOpts Create options for the database. All members of the structure should be + * initialized to specify options desired when the database is created. If NULL is passed as + * the value of this parameter, default options will be used. See the glossary for a description + * of the XFLM_CREATE_OPTS structure and the default value for each field. + * @param ppDb A pointer to a database object that references the newly created database. + * @return RCODE + */ + virtual RCODE XFLMAPI dbCreate( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pDictFileName, + const char * pszDictBuf, + XFLM_CREATE_OPTS * pCreateOpts, + IF_Db ** ppDb) = 0; + + /** + * @brief Opens an existing database. + * + * The dbOpen method opens an existing database. A pointer to a database object (IF_Db) is returned + * in the ppDb parameter. The database system engine must first be started using the init method. + * After opening an XFlaim database, the database is ready to use. + * + * @param pszDbFileName Name of the control file for the database that is to be opened. See the + * XFlaim Concepts/Database Files for a discussion on the different database files, including the control file. + * @param pszDataDir The directory where the data files are located. If a NULL is passed in, + * it is assumed that the data files are located in the same directory as the control file + * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a + * discussion on the different database files. + * @param pszRflDir The directory where the RFL (roll forward log) files are located. If a NULL + * is passed, roll forward log files are assumed to reside in the same directory as the control file + * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a + * discussion on the different database files, including the rfl (Roll-forward log) file. + * @param ppDb A pointer to a database object that references the newly created database. + * @return RCODE + */ + virtual RCODE XFLMAPI dbOpen( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMBOOL bAllowLimited, + IF_Db ** ppDb) = 0; + + /** + * @brief Rebuilds a database. + * + * The dbRebuild method will attempt to reconstruct a database, recovering everything that it can + * from the blocks of the database. + * + * @param pszSourceDbFileName Name of the source control file for the database that is to be rebuilt. + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the control file. + * @param pszSourceDataDir The directory where the data files are located. If a NULL is passed in, + * it is assumed that the data files are located in the same directory as the control file + * (as specified by the pszSourceDbFileName parameter). See the XFlaim Concepts/Database Files + * for a discussion on the different database files. + * @param pszDestDbFileName Name of the destination control file for the recovered database. + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the control file. + * @param pszDestDataDir The directory where the data files are to be located. If a NULL is + * passed in, it is assumed that the data files are to be located in the same directory as + * the control file (as specified by the pszDestDbFileName parameter). See the XFlaim + * Concepts/Database Files for a discussion on the different database files. + * @param pszDestRflDir The directory where the RFL (roll forward log) files of the + * destination database are to be created. If a NULL is passed, roll forward log files are + * assumed to reside in the same directory as the control file (as specified by the + * pszDestDbFileName parameter). See the XFlaim Concepts/Database Files for a discussion + * on the different database files, including the rfl (Roll-forward log) file. + * @param pszDictFileName The name of a file containing the dictionary definitions that + * will be used when rebuilding the database. A NULL may be passed in this parameter. + * In addition to using the definitions specified in this file, dbRebuild will attempt + * to recover any additional dictionary entries from the dictionary collection. + * See XFlaim Concepts / Dictionary for a discussion on the dictionary in XFlaim. + * @param pCreateOpts Create options for the database. All members of the structure should + * be initialized to specify options desired when the database is created. If NULL is passed + * as the value of this parameter, default options will be used. See the glossary for a description + * of the XFLM_CREATE_OPTS structure and the default value for each field. + * @param pDbRebuild A pointer to an application defined status reporting object. Methods + * on this object are used by dbRebuild to report progress during the rebuild. A NULL may be + * passed in this parameter. This object is NOT implemented by XFlaim, but is implemented by + * the application. The application must create a class that inherits from the IF_DbRebuildStatus + * interface and implements the pure virtual methods of that interface. + * @param pui64TotalNodes The total number of DOM nodes. + * @param pui64NodesRecovered The total number of DOM nodes recovered. + * @param pui64NodesDiscardedDocs The total number of documents that couldn't be recovered. + * @return RCODE + */ + virtual RCODE XFLMAPI dbRebuild( + const char * pszSourceDbPath, + const char * pszSourceDataDir, + const char * pszDestDbPath, + const char * pszDestDataDir, + const char * pszDestRflDir, + const char * pszDictPath, + const char * pszPassword, + XFLM_CREATE_OPTS * pCreateOpts, + FLMUINT64 * pui64TotNodes, + FLMUINT64 * pui64NodesRecov, + FLMUINT64 * pui64DiscardedDocs, + IF_DbRebuildStatus * pDbRebuild) = 0; + + /** + * @brief Checks a database for corruptions. + * + * The dbCheck method will check the database for corruptions ans in certain cases it can repair them. + * + * @param pszDbFileName Name of the control file for the database that is to be checked. + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the control file. + * @param pszDataDir The directory where the data files are located. If a NULL is passed in, + * it is assumed that the data files are located in the same directory as the control file + * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for a + * discussion on the different database files. + * @param pszRflDir The directory where the RFL (roll forward log) files are located. If a NULL is + * passed, roll forward log files are assumed to reside in the same directory as the control file + * (as specified by the pszDbFileName parameters). See the XFlaim Concepts/Database Files for a + * discussion on the different database files, including the rfl (Roll-forward log) file. + * @param pDbCheckStatus A pointer to an application defined status reporting object. Methods + * on this object are used by dbCheck to report progress and corruptions to the calling application. + * A NULL may be passed in this parameter. This object is NOT implemented by XFlaim, but is + * implemented by the application. The application must create a class that inherits from the + * IF_DbCheckStatus interface and implements the pure virtual methods of that interface. + * @param ppDbInfo If a non-NULL ppDbInfo pointer is passed, an IF_DbInfo object will be returned + * that contains detailed statistical information about the various B-trees in the database. + * The information includes things like percent utilization of various blocks at each level in + * a B-tree, number of keys, etc. Methods of the IF_DbInfo object provide for retrieval of this information. + * @return RCODE + */ + virtual RCODE XFLMAPI dbCheck( + const char * pszDbFileName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszPassword, + FLMUINT uiFlags, + IF_DbInfo ** ppDbInfo, + IF_DbCheckStatus * pDbCheckStatus) = 0; + + /** + * @brief Copies a database. + * + * Copies a database to a new database. The destination database will be created if it + * does not exist and overwritten if it does exist. + * + * @param pszSrcDbName Name of the control file for the database that is to be copied. + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the control file. + * @param pszSrcDataDir The directory where the source database's data files are located. + * If a NULL is passed in, it is assumed that the source database's data files are located in + * the same directory as the source database's control file (as specified by the pszSrcDbName + * parameter). See the XFlaim Concepts/Database Files for a discussion on the different database files. + * @param pszSrcRflDir The directory where the RFL (roll forward log) files of the source database + * are located. If a NULL is passed, the roll forward log files of the source database are assumed + * to reside in the same directory as the source database's control file (as specified by the pszSrcDbName + * parameter). See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the rfl (Roll-forward log) file. + * @param pszDestDbName Name of the control file for the destination database. If the destination + * database already exists, it will be overwritten. See the XFlaim Concepts/Database Files for a + * discussion on the different database files, including the control file. + * @param pszDestDataDir The directory where the destination database's data files are to be stored. + * If a NULL is passed in, the destination database's data files will be put in the same directory + * as the destination database's control file (as specified by the pszDestDbName parameter). + * See the XFlaim Concepts/Database Files for a discussion on the different database files. + * @param pszDestRflDir The directory where the RFL (roll forward log) files of the destination database + * are to be put. If a NULL is passed, the roll forward log files of the destination database will be + * put in the same directory as the destination database's control file (as specified by the + * pszDestDbName parameter). See the XFlaim Concepts/Database Files for a discussion on the different + * database files, including the rfl (Roll-forward log) file. + * @param ifpStatus A pointer to an application defined status reporting object. Methods on this object + * are used by dbCopy to report progress during the copy. A NULL may be passed in this parameter. + * This object is NOT implemented by XFlaim, but is implemented by the application. The application + * must create a class that inherits from the IF_DbCopyStatus interface and implements the pure virtual + * methods of that interface. Those methods may be called by dbCopy to report copy progress. + * @return RCODE + */ + virtual RCODE XFLMAPI dbCopy( + const char * pszSrcDbName, + const char * pszSrcDataDir, + const char * pszSrcRflDir, + const char * pszDestDbName, + const char * pszDestDataDir, + const char * pszDestRflDir, + IF_DbCopyStatus * ifpStatus) = 0; + + /** + * @brief Renames a database. + * + * This method will rename an existing database to a specified new name. + * + * @param pszDbName Name of the control file for the database that is to be renamed. + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the control file. + * @param pszDataDir The directory where the database's data files are located. If a NULL + * is passed in, it is assumed that the data files are located in the same directory as the + * control file (as specified by the pszDbName parameter). See the XFlaim Concepts/Database + * Files for a discussion on the different database files. + * @param pszRflDir The directory where the RFL (roll forward log) files of the database + * are located. If a NULL is passed, the roll forward log files of the database are assumed + * to reside in the same directory as the control file (as specified by the pszDbName parameter). + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the RFL (Roll-forward log) file. + * @param pszNewDbName Name the control file is to be renamed to. This name also determines + * what data files and RFL files will be renamed to. See the XFlaim Concepts/Database Files + * for a discussion on the different database files, including the control file. Note that the + * directory of the new database name must be the same as the directory specified in pszDbName. + * @param bOverwriteDestOk If pszNewDbName specifies the name of a file that already exists, + * this flag indicates whether that file should be deleted so that the rename can proceed. + * If FALSE, the rename will fail. + * @param ifpStatus A pointer to an application defined status reporting object. Methods on + * this object are used by dbRename to report progress during the rename. A NULL may be passed + * in this parameter. This object is NOT implemented by XFlaim, but is implemented by the + * application. The application must create a class that inherits from the IF_DbRenameStatus + * interface and implements the pure virtual methods of that interface. + * @return RCODE + */ + virtual RCODE XFLMAPI dbRename( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + const char * pszNewDbName, + FLMBOOL bOverwriteDestOk, + IF_DbRenameStatus * ifpStatus) = 0; + + /** + * @brief Removes a database. + * + * This method will remove (delete) an existing database. + * + * @param pszDbName Name of the control file for the database that is to be removed. + * See the XFlaim Concepts/Database Files for a discussion on the different database + * files, including the control file. + * @param pszDataDir The directory where the database's data files are located. If a + * NULL is passed in, it is assumed that the data files are located in the same directory + * as the control file (as specified by the pszDbName parameter). See the XFlaim + * Concepts/Database Files for a discussion on the different database files. + * @param pszRflDir The directory where the RFL (roll forward log) files of the database are + * located. If a NULL is passed, the roll forward log files of the database are assumed to + * reside in the same directory as the control file (as specified by the pszDbName parameter). + * See the XFlaim Concepts/Database Files for a discussion on the different database files, + * including the RFL (Roll-forward log) file. + * @param bRemoveRflFiles A flag that indicate whether or not the RFL file(s) should be removed as well. + * @return RCODE + */ + virtual RCODE XFLMAPI dbRemove( + const char * pszDbName, + const char * pszDataDir, + const char * pszRflDir, + FLMBOOL bRemoveRflFiles) = 0; + + /** + * @brief Restores a database from a backup set. + * + * This method will restore a database from a backup set. The client is responsible for + * providing an implementation of the IF_RestoreClient and IF_RestoreStatus interfaces. + * + * @param pszDbPath Name of the control file (including path) for the database that is to + * be restored. See the XFlaim Concepts/Database Files for a discussion on the different + * database files, including the control file. + * @param pszDataDir The directory where the database's data files are located. If a + * NULL is passed in, it is assumed that the data files are located in the same directory + * as the control file (as specified by the pszDbName parameter). See the XFlaim + * Concepts/Database Files for a discussion on the different database files. + * @param pszBackupPath The path to the backup file set. + * @param pszRflDir The directory where the RFL (roll forward log) files of the database + * are located. If a NULL is passed, the roll forward log files of the database are + * assumed to reside in the same directory as the control file (as specified by the + * pszDbName parameter). See the XFlaim Concepts/Database Files for a discussion on + * the different database files, including the RFL (Roll-forward log) file. + * @param pRestoreObj A pointer to an application defined restore client object. Methods + * on this object are used by dbRestore to read the backed-up data from the location it is + * stored in. The application is responsible for implementing this object. In this way, + * an application can have a database restored from virtually any media. + * If a NULL is passed in this parameter, pszBackupPath is used to restore from. + * Visit: Need to say something about the default backup file set. + * @param pRestoreStatus A pointer to an application defined restore status object. + * Methods on this object are used by dbRestore to report progress during the restore. + * The application is responsible for implementing this object. + * @return RCODE + */ + virtual RCODE XFLMAPI dbRestore( + const char * pszDbPath, + const char * pszDataDir, + const char * pszRflDir, + const char * pszBackupPath, + const char * pszPassword, + IF_RestoreClient * pRestoreObj, + IF_RestoreStatus * pRestoreStatus) = 0; + + /** + * @brief Duplicates the IF_Db interface object. + * + * @par + * This method will duplicate the IF_Db interface object. It is similar to the dbOpen method, + * except that it doesn't take a database file name as input, but takes an already open IF_Db + * object as input. The returned IF_Db object is the same as if it had been created by the dbOpen + * method. However, calling dbDup is slightly more efficient than calling dbOpen. + * @par + * Since IF_Db objects cannot be shared between threads (they are not thread-safe), an application + * would normally have each of its threads call dbOpen to obtain an IF_Db object. The dbDup + * method provides an alternative way to create IF_Db objects for multiple threads. + * For example, one thread could call dbOpen to obtain the first IF_Db object. Thereafter, + * it could call dbDup and pass the created IF_Db objects to different threads. + * @par + * Although the most common use of dbDup is to create IF_Db objects and pass them to different + * threads, it is conceivable that one thread might want multiple IF_Db objects on the same + * database. The uses for this are probably somewhat exotic. For example, a single thread + * using multiple IF_Db objects could have multiple different transactions open simultaneously. + * + * @param pDb The database object to be duplicated. + * @param ppDb A new database object. + * @return RCODE + */ + virtual RCODE XFLMAPI dbDup( + IF_Db * pDb, + IF_Db ** ppDb) = 0; + + /** + * @brief Converts an RCODE error number to a string equivalent. + * + * This method converts an RCODE error number to a string equivalent that can then be printed in + * a diagnostic log or some other human readable output. + * + * @param rc The RCODE to be translated. + * @return const char * + */ + virtual const char * XFLMAPI errorString( + RCODE rc) = 0; + + /** + * @brief Converts a corruption code to a string equivalent. + * + * This method converts a corruption code to a string equivalent that can then be printed in + * a diagnostic log or some other human readable output. + * + * @param iErrCode The error code to be translated. + * @return const char * + */ + virtual const char * XFLMAPI checkErrorToStr( + FLMINT iCheckErrorCode) = 0; + + /** + * @brief Opens a buffered input stream. + * + * This method opens a buffered input stream. The pucBuffer buffer holds that data that is to be + * streamed through the IF_PosIStream object. NOTE: The returned IF_PosIStream object's Release() + * method should be called when the application is done using the object. + * + * @param pucBuffer The buffer that will be associated with the input stream. + * @param uiLength The size of the buffer (bytes). + * @param ppIStream The input stream object used to read the data in. + * @return RCODE + */ + virtual RCODE XFLMAPI openBufferIStream( + const char * pucBuffer, + FLMUINT uiLength, + IF_PosIStream ** ppIStream) = 0; + + /** + * @brief Opens a file input stream. + * + * This method opens a file input stream. The pszPath parameter points to a file that holds the data + * that is to be streamed through the IF_PosIStream object. NOTE: The returned IF_PosIStream object's + * Release() method should be called when the application is done using the object. + * + * @param pszPath The name of file whose data is to be read via the IF_PosIStream object. + * @param ppIStream The input stream object used to read the data in. + * @return RCODE + */ + virtual RCODE XFLMAPI openFileIStream( + const char * pszPath, + IF_PosIStream ** ppIStream) = 0; + + /** + * @brief Open a multi-file input stream. + * + * (*ppIStream)->read() will read data from the files in the directory (pszDirectory) + * that match the base name given (pszBaseName). EOF is returned when there are no + * more files to read from. File names start with pszBaseName, then + * pszBaseName.00000001, pszBaseName.00000002, etc. The extension is a hex number. + */ + virtual RCODE XFLMAPI openMultiFileIStream( + const char * pszDirectory, + const char * pszBaseName, + IF_IStream ** ppIStream) = 0; + + /** + * @brief Convert an input stream (pIStream) into a buffered input stream (*ppIStream) + * + * When (*ppIStream)->read() is called, it will fill an internal buffer of + * uiBufferSize by reading from pIStream. Data is returned from the buffer until + * the buffer is emptied, at which time another read will be done from pIStream, + * until pIStream has no more data to return. This method allows any input stream + * to be turned into a buffered stream. + */ + virtual RCODE XFLMAPI openBufferedIStream( + IF_IStream * pIStream, + FLMUINT uiBufferSize, + IF_IStream ** ppIStream) = 0; + + /** + * @brief Read uncompressed data from a compressed stream (pIStream) + * + * When (*ppIStream)->read() is called, it will read and uncompress data from + * pIStream. + */ + virtual RCODE XFLMAPI openUncompressingIStream( + IF_IStream * pIStream, + IF_IStream ** ppIStream) = 0; + + // METHODS FOR GETTING OUTPUT STREAM OBJECTS + + /** + * @brief Create a file output stream. + * + * Data is written out to the specified file. The file may be created, overwritten, or + * appended to, depending on iAccessFlags. + */ + virtual RCODE XFLMAPI openFileOStream( + const char * pszFileName, + FLMBOOL bTruncateIfExists, + IF_OStream ** ppOStream) = 0; + + /** + * @brief Create a multi-file output stream. + * + * Data is written to the specified directory, creating files using the given + * base file name. When a file reaches the specified size, another file will + * be created by appending a suffix with an incrementing HEX number. The + * bOverwrite flag indicates whether to overwrite files that already exist. + */ + virtual RCODE XFLMAPI openMultiFileOStream( + const char * pszDirectory, + const char * pszBaseName, + FLMUINT uiMaxFileSize, + FLMBOOL bOkToOverwrite, + IF_OStream ** ppStream) = 0; + + /** + * @brief Remove a multi-file stream + */ + virtual RCODE XFLMAPI removeMultiFileStream( + const char * pszDirectory, + const char * pszBaseName) = 0; + + /** + * @brief Convert an output stream (pOStream) into a buffered output stream (*ppOStream) + * + * As data is written to *ppOStream, it is buffered before ultimately being + * written to pOStream. + */ + virtual RCODE XFLMAPI openBufferedOStream( + IF_OStream * pOStream, + FLMUINT uiBufferSize, + IF_OStream ** ppOStream) = 0; + + /** + * @brief Convert an output stream (pOStream) into a compressed output stream (*ppOStream) + * + * As data is written to *ppOStream, it is compressed before ultimately being + * written to pOStream. + */ + virtual RCODE XFLMAPI openCompressingOStream( + IF_OStream * pOStream, + IF_OStream ** ppOStream) = 0; + + /** + * @brief All data is read from the input stream (pIStream) and written + * to the output stream (pOStream). This goes until pIStream returns EOF. + */ + virtual RCODE XFLMAPI writeToOStream( + IF_IStream * pIStream, + IF_OStream * pOStream) = 0; + + /** + * @brief Opens a base64 encoder stream + * + * This method opens a stream for encoding a user-supplied input stream to + * base64 (ASCII). + * + * @param pInputStream The stream to be encoded + * @param bInsertLineBreaks A line break will be inserted every 72 + * characters + * @param ppEncodedStream The stream object used to read the encoded data. + * @return RCODE + */ + virtual RCODE XFLMAPI openBase64Encoder( + IF_IStream * pInputStream, + FLMBOOL bInsertLineBreaks, + IF_IStream ** ppEncodedStream) = 0; + + /** + * @brief Opens a base64 decoder stream + * + * This method opens a stream for decoding a user-supplied input stream from + * base64 (ASCII) to binary. + * + * @param pInputStream The stream to be decoded + * @param ppDecodedStream The stream object used to read the decoded data. + * @return RCODE + */ + virtual RCODE XFLMAPI openBase64Decoder( + IF_IStream * pInputStream, + IF_IStream ** ppDecodedStream) = 0; + + /** + * @brief Creates an IF_DataVector interface object. + * + * This method creates an IF_DataVector interface object. The IF_DataVector + * is used in index key searches. + * + * @param ifppDV The IF_DataVector object. + * @return RCODE + */ + virtual RCODE XFLMAPI createIFDataVector( + IF_DataVector ** ifppDV) = 0; + + /** + * @brief Creates an IF_ResultSet interface object. + * + * This method creates an IF_ResultSet interface object. + * + * @param ifppResultSet The IF_ResultSet object. + * @return RCODE + */ + virtual RCODE XFLMAPI createIFResultSet( + IF_ResultSet ** ifppResultSet) = 0; + + /** + * @brief Creates an IF_Query interface object. + * + * This method creates an IF_Query interface object. + * + * @param ifppQuery The IF_Query object. + * @return RCODE + */ + virtual RCODE XFLMAPI createIFQuery( + IF_Query ** ifppQuery) = 0; + + /** + * @brief Frees memory for allocations that are returned from various XFlaim methods. + * + * This method frees memory that has been allocated by various methods in XFlaim. If + * a method allocates memory that needs to be freed by this method, it will be documented + * in that method. + * + * @param ppMem Pointer to the pointer of the memory to be freed. When the memory is + * successfully freed, the pointer will be set to NULL + */ + virtual void XFLMAPI freeMem( + void ** ppMem) = 0; + + // Various configuration routines + + /** + * @brief Set a dynamic memory limit in the XFlaim database system. + * + * This method sets the dynamic memory parameters in the XFlaim database system. When this + * method is called, XFlaim is put into a mode where it periodically (every 15 seconds unless + * otherwise specified - see setCacheAdjustInterval) adjusts its cache limit. The parameters + * passed into this method are used to calculate the new limit. The new limit remains in + * effect until the next adjustment is made. + * + * @param uiCacheAdjustPercent Percentage of available physical memory to set or adjust to. + * @param uiCacheAdjustMin Minimum bytes to adjust down to. + * @param uiCacheAdjustMax Maximum bytes to adjust up to. NOTE: If this parameter is non-zero, + * the uiCacheAdjustMinToLeave parameter is ignored. + * @param uiCacheAdjustMinToLeave Minimum bytes to leave available after making adjustment. + * This is an alternative way to specify a maximum to adjust to. Using this value, XFlaim will + * calculate the maximum by subtracting this number from the total bytes it thinks is available. + * That calculated number becomes the effective maximum to adjust to. + * @return RCODE + */ + virtual RCODE XFLMAPI setDynamicMemoryLimit( + FLMUINT uiCacheAdjustPercent, + FLMUINT uiCacheAdjustMin, + FLMUINT uiCacheAdjustMax, + FLMUINT uiCacheAdjustMinToLeave) = 0; + + /** + * @brief Set a hard limit on the amount of memory that the database system can access. + * + * This method sets a hard limit on the amount of memory that the database system can access. + * The important difference between this routine and the setDynamicMemoryLimit routine is that + * the limit remains in force until a subsequent call to setHardMemoryLimit is made, or until + * a call to setDynamicMemoryLimit is made. The setDynamicMemoryLimit routine, on the other + * hand, puts XFlaim into a mode where a new limit is automatically calculated on a preset + * interval (see setCacheAdjustInterval). Thus, the purpose of the setHardMemoryLimit routine + * is to let the application control the limit instead of having it automatically adjusted periodically. + * + * @param uiPercent If this parameter is zero, the uiMax parameter determines the hard cache limit. + * Otherwise, this parameter (which must contain a number between 1 and 100) is used to determine + * a hard cache limit. The hard limit will be calculated as a percentage of available physical + * memory on the system. + * @param bPercentOfAvail A flag to indicate that the percentage (uiPercent) is to be interpreted + * as a percentage of available memory as opposed to a percentage of all of physical memory. + * This parameter is only used if uiPercent is non-zero. + * @param uiMin Minimum bytes to set the hard cache limit to. Note that this parameter is only used + * if uiPercent is non-zero and we are calculating a hard limit to set. + * @param uiMax Maximum bytes to set the hard limit to. Note that if uiPercent is zero, + * this number contains the hard limit. + * @param uiMinToLeave This parameter is only used if uiPercent is non-zero and we are calculating + * a hard limit to set. Instead of uiMax determining the maximum cache limit we could set, this + * value will determine the maximum. This number will be subtracted from the total memory on the + * system or the total memory currently available (if bPercentOfAvail is TRUE) to establish a maximum. + * @param bPreallocate Boolean flag. Used to indicate that the cache should be preallocated when XFlaim + * starts up, rather than allow it to grow as needed. The default value to FALSE. + * @return RCODE + */ + virtual RCODE XFLMAPI setHardMemoryLimit( + FLMUINT uiPercent, + FLMBOOL bPercentOfAvail, + FLMUINT uiMin, + FLMUINT uiMax, + FLMUINT uiMinToLeave, + FLMBOOL bPreallocate = FALSE) = 0; + + /** + * @brief Get a flag which indicates whether or not dynamic cache adjusting + * is allowed. + * + * This method returns a boolean TRUE or FALSE. + * + * @return FLMBOOL TRUE=supported or FALSE=not supported. + */ + virtual FLMBOOL XFLMAPI getDynamicCacheSupported( void) = 0; + + /** + * @brief Query the database system for information regarding the current cache usage. + * + * This method is used to query the database system for information regarding the current cache usage. + * + * @param pCacheInfo The cache info structure. + */ + virtual void XFLMAPI getCacheInfo( + XFLM_CACHE_INFO * pCacheInfo) = 0; + + /** + * @brief A method to either enable or disable cache debug mode. + * + * This is a method to either enable or disable cache debug mode. If bDebug is TRUE (1), + * then cache debug will be enabled. If bDebug is FALSE (0), cache debug will be disabled. + * + * @param bDebug A boolean to indicate whether to enable or disable cache debug mode. + */ + virtual void XFLMAPI enableCacheDebug( + FLMBOOL bDebug) = 0; + + /** + * @brief A method to find out if cache debug mode is either enabled or disabled. + * + * This is a method to find out if cache debug mode is either enabled or disabled. + * + * @return FLMBOOL True or False + */ + virtual FLMBOOL XFLMAPI cacheDebugEnabled( void) = 0; + + /** + * @brief Close all file handles (descriptors) that have not been used for a specified + * amount of time. + * + * This is a method to close all file handles (descriptors) that have not been used or + * accessed for a specified number of seconds. + * + * @param uiSeconds The number of seconds. File handles (descriptors) that have been + * unused for a period of time greater than or equal to this number of seconds will be + * closed. A value of zero will have the effect of closing all unused file handles + * (descriptors), regardless of how long they have been unused. + * @return RCODE + */ + virtual RCODE XFLMAPI closeUnusedFiles( + FLMUINT uiSeconds) = 0; + + /** + * @brief Set the threshold for the number of file handles (descriptors) that can be + * opened by the database system. + * + * This is a method to set the maximum number of file handles (descriptors) that can + * be kept open by the database system. + * + * @param uiThreshold The number of file handles (descriptors) that can be open at any + * one time. The default threshold is 65535. IMPORTANT NOTE: It is possible for the + * database system to temporarily have more file handles (descriptors) open than the + * specified threshold. It does this when all available file handles (descriptors) + * are in use and it needs to open a file to perform database work. However, as soon as + * it can, the database system will close file handles (descriptors) until it comes back + * down below the specified threshold. + */ + virtual void XFLMAPI setOpenThreshold( + FLMUINT uiThreshold) = 0; + + /** + * @brief Get the threshold for the number of file handles that can be opened by the + * database system. + * + * This method returns the threshold for the number of file handles (descriptors) that + * can be held open by the database system. + * + * @return FLMUINT The open threshold. + */ + virtual FLMUINT XFLMAPI getOpenThreshold( void) = 0; + + /** + * @brief Get the number of files that are currently open in the database system. + * + * This method returns the number of file handles (descriptors) that are currently + * open in the database system. + * + * @return FLMUINT The open file count + */ + virtual FLMUINT XFLMAPI getOpenFileCount( void) = 0; + + /** + * @brief Start the collection of statistics on the database system. + * + * This method starts the collection of statistics on the database system. + */ + virtual void XFLMAPI startStats( void) = 0; + + /** + * @brief Stop the collection of statistics on the database system. + * + * This method stops the collection of statistics on the database system. + */ + virtual void XFLMAPI stopStats( void) = 0; + + /** + * @brief Reset the statistics counters on the database system. + * + * This method resets the statistics counters on the database system. + */ + virtual void XFLMAPI resetStats( void) = 0; + + /** + * @brief Retrieve the statistics from the database system. + * + * This method returns the current set of statistics from the database system. + * + * @param pFlmStats The structure where statistics are returned. + * @return RCODE + */ + virtual RCODE XFLMAPI getStats( + XFLM_STATS * pFlmStats) = 0; + + /** + * @brief Free the statistic object in the database system. + * + * This method frees any memory allocations that are associated with the FLM_STATS + * structure. The FLM_STATS structure will have been populated by a call to the + * getStats method. + * + * @param pFlmStats The statistics structure whose memory allocations are to be freed. + */ + virtual void XFLMAPI freeStats( + XFLM_STATS * pFlmStats) = 0; + + /** + * @brief Set the directory where temporary files are created. + * + * This method sets the directory where temporary files are to be created. + * + * @param pszPath The temporary directory path. + * @return RCODE + */ + virtual RCODE XFLMAPI setTempDir( + const char * pszPath) = 0; + + /** + * @brief Get the directory where temporary files are created. + * + * This method returns the directory name where temporary files are created. + * If no temporary directory is set, this function returns NE_XFLM_IO_PATH_NOT_FOUND. + * + * @param pszPath The temporary directory path is returned here. + * @return RCODE + */ + virtual RCODE XFLMAPI getTempDir( + char * pszPath) = 0; + + /** + * @brief Set the time between checkpoints in the database. + * + * + * This method sets the maximum time between completed checkpoints in the database. + * This is NOT the same thing as how often a checkpoint will be performed. Checkpoints + * are actually performed and completed much more frequently, depending on how much + * update transaction activity there is. A background thread (often referred to as the + * checkpoint thread) is responsible for performing database checkpoints. The checkpoint + * thread wakes up about once a second to see if there are dirty blocks in the cache + * that need to be written to disk. If there are, and there is no currently active + * update transaction, it will begin performing a checkpoint. If no update + * transactions occur while it is performing the checkpoint, it will be able to complete + * the checkpoint. If the application attempts to start an update transaction while + * the checkpoint is being done, the checkpoint thread has the option of yielding + * to the update transaction and not completing the checkpoint. The checkpoint + * thread may also choose to not yield and complete the checkpoint. One of the + * conditions under which it will not yield is if the elapsed time since the last + * completed checkpoint is greater than the time specified by this method. In that + * case, the update transaction is held off until the checkpoint completes. + * Note that it is possible for the time between completed checkpoints to be longer than + * the interval specified in this routine. The checkpoint thread cannot interrupt an + * active transaction when it wakes up. If it wakes up and finds that a transaction + * is active, it must wait for the transaction to complete before it can start a checkpoint. + * Thus, if that transaction runs a long time, the time between completed checkpoints + * could exceed the time specified in this method. + * + * @param uiSeconds The maximum number of seconds allowed to elapse between completed + * checkpoints. Default is 180 seconds. NOTE: It is possible that the time + * between completed checkpoints can be greater than this value. This is because the + * checkpoint thread cannot run if there is an update transaction that is currently + * active. If an update transaction is active and runs for a long time, the time + * between completed checkpoints could exceed the time specified in this method. + */ + virtual void XFLMAPI setCheckpointInterval( + FLMUINT uiSeconds) = 0; + + /** + * @brief Get the current checkpoint interval. + * + * This method returns the current checkpoint interval. + * @return FLMUINT The current checkpoint interval (seconds). + */ + virtual FLMUINT XFLMAPI getCheckpointInterval( void) = 0; + + /** + * @brief Set the time interval for dynamically adjusting the cache limit. + * + * This method sets the time interval for dynamically adjusting the cache limit. + * + * @param uiSeconds The time interval for dynamically adjusting the cache limit. + */ + virtual void XFLMAPI setCacheAdjustInterval( + FLMUINT uiSeconds) = 0; + + /** + * @brief Get the time interval for dynamically adjusting the cache limit. + * + * This method returns the time interval (in seconds) for dynamically adjusting the cache limit. + * + * @return FLMUINT The curernt cache adjust interval (seconds). + */ + virtual FLMUINT XFLMAPI getCacheAdjustInterval( void) = 0; + + /** + * @brief Set the time interval for dynamically cleaning out old cache blocks from block cache. + * + * This method sets the time interval for cleaning out old cache blocks from block cache. + * + * @param uiSeconds The time interval for dynamically cleaning out old cache blocks. + */ + virtual void XFLMAPI setCacheCleanupInterval( + FLMUINT uiSeconds) = 0; + + /** + * @brief Get the time interval for dynamically cleaning out old cache blocks from block cache. + * + * This method returns the time interval (in seconds) for cleaning out old cache blocks from block cache. + * + * @return FLMUINT The current cache cleanup inerval (seconds). + */ + virtual FLMUINT XFLMAPI getCacheCleanupInterval( void) = 0; + + /** + * @brief Set time interval for cleaning up unused resources (such as file handles). + * + * This method sets the time interval for cleaning up unused resources (such as file handles). + * + * @param uiSeconds The time interval for cleaning up unused resources (such as file handles). + */ + virtual void XFLMAPI setUnusedCleanupInterval( + FLMUINT uiSeconds) = 0; + + /** + * @brief Get time interval for cleaning up unused resources (such as file handles). + * + * This method returns the time interval (in seconds) for cleaning up unused resources (such as file handles). + * + * @return FLMUINT The current unused cleanup interval (seconds). + */ + virtual FLMUINT XFLMAPI getUnusedCleanupInterval( void) = 0; + + /** + * @brief Set maximum time for a resource (such as a file handle) to be unused before it is cleaned up. + * + * This method sets the maximum time for a resource (such as a file handle) to be unused before it + * is cleaned up. + * + * @param uiSeconds The maximum time for a resource (such as a file handle) to be unused before it is + * cleaned up. + */ + virtual void XFLMAPI setMaxUnusedTime( + FLMUINT uiSeconds) = 0; + + /** + * @brief Get maximum time for a resource (such as a file handle) to be unused before it is cleaned up. + * + * This method returns the maximum time for a resource (such as a file handle) to be unused + * before it is cleaned up. + * + * @return FLMUINT The current maximum unused time (seconds). + */ + virtual FLMUINT XFLMAPI getMaxUnusedTime( void) = 0; + + /** + * @brief Set the logger client. + * + * @param pLogger Pointer to the logger client object. + */ + virtual void XFLMAPI setLogger( + IF_LoggerClient * pLogger) = 0; + + /** + * @brief Enable or disable extended server memory (ESM). + * + * @param bEnable A boolean flag. When TRUE, Extended Server Memory is enabled. + * When FALSE, Extended Server Memory is disabled. + */ + virtual void XFLMAPI enableExtendedServerMemory( + FLMBOOL bEnable) = 0; + + /** + * @brief Determine if extended server memory (ESM) is enabled or disabled. + * + * @return FLMBOOL True if enabled, otherwise False. + */ + virtual FLMBOOL XFLMAPI extendedServerMemoryEnabled( void) = 0; + + /** + * @brief Deactivate open database objects, forcing the database(s) to eventually be closed. + * + * This method deactivates all open database objects (IF_Db objects) for a particular + * database, forcing the database to eventually be closed. Passing NULL in the pszDbFileName + * parameter will deactivate all active database objects for all open databases. + * + * @param pszDatabasePath Name of the control file ( including path ) for the database that + * is to be deactivated. See the XFlaim Concepts/Database Files for a discussion on the + * different database files, including the control file. NOTE: Passing a NULL in this + * parameter will cause all open databases to be deactivated. + * @param pszDataFilePath The directory where the data files are located. If a NULL is passed + * in, it is assumed that the data files are located in the same directory as the control file + * (as specified by the pszDbFileName parameter). See the XFlaim Concepts/Database Files for + * a discussion on the different database files. + */ + virtual void XFLMAPI deactivateOpenDb( + const char * pszDatabasePath, + const char * pszDataFilePath) = 0; + + /** + * @brief Set the maximum number of queries to save when statistics gathering is enabled. + * + * This method sets the maximum number of queries to save when statistics gathering is enabled. + * + * @param uiMaxToSave The maximum number of queries to save. + */ + virtual void XFLMAPI setQuerySaveMax( + FLMUINT uiMaxToSave) = 0; + + /** + * @brief Get the maximum number of queries to save when statistics gathering is enabled. + * + * This method returns the maximum number of queries to save when statistics gathering is enabled. + * + * @return FLMUINT The maximum number of queries to save. + */ + virtual FLMUINT XFLMAPI getQuerySaveMax( void) = 0; + + /** + * @brief Set the minimum and maximum dirty cache limits. + * + * This method sets dirty cache limits - a "maximum" and a "low." The maximum specifies the maximum + * dirty cache to be allowed. When a database exceeds this amount, the checkpoint thread will kick + * in and write out dirty blocks until the dirty cache comes back down below the amount specified by + * the "low" value. In this way, the application can control how much dirty cache builds up between + * forced checkpoints (see setCheckpointInterval). The more dirty cache there is when the checkpoint + * forces a checkpoint, the longer it will take to complete the checkpoint. It should be noted that + * the overall time that will be needed to write out dirty blocks is still the same. It's just that + * the writing gets spread out more over time. Instead of doing it all in one big chunk, it gets done + * in lots of little chunks. This has both pros and cons, so this method should be used with extreme caution! + * In a bulk load situation, where there is one thread doing the bulk loading, lowering the maximum + * dirty cache could actually lengthen out the overall time it takes to complete the bulk load. This + * is because the bulk load will be interrupted more often by the checkpoint thread to do smaller units + * of writing than it otherwise would. There will also be less piggy-backing of writes. Piggy-backing + * occurs when multiple transactions write to the same data block before the block is flushed out of + * cache. If the database system is flushing dirty blocks out of cache more often, it could end up + * writing the same block multiple times, whereas it might not not have had to if it had waited longer between flushes. + * The advantage to spreading out the flushing of dirty cache blocks occurs when there are multiple threads + * trying to do update transactions. When the checkpoint thread forces a checkpoint, it holds back all + * pending update transactions. If it has to do more writing when it forces a checkpoint, it may cause + * many threads to wait for a longer period of time than it would if there were less writing to do. + * This is less efficient to the overall throughput of the system, because it is likely that each of the + * threads could be doing other useful work instead of waiting. + * + * @param uiMaxDirty The maximum amount (in bytes) of dirty cache allowed. + * @param uiLowDirty The low threshold (in bytes) for dirty cache. + */ + virtual void XFLMAPI setDirtyCacheLimits( + FLMUINT uiMaxDirty, + FLMUINT uiLowDirty) = 0; + + /** + * @brief Get the minimum and maximum dirty cache limits. + * + * This method returns the minimum and maximum dirty cache limits. + * + * @param puiMaxDirty The maximum number of dirty blocks allowed in the cache. + * @param puiLowDirty The low threshold for the number of dirty blocks in cache. + */ + virtual void XFLMAPI getDirtyCacheLimits( + FLMUINT * puiMaxDirty, + FLMUINT * puiLowDirty) = 0; + + /** + * @brief Get an information object that can be used to get information about threads owned by the database system. + * + * This method returns a thread information object which has methods for retrieving various pieces of information + * about threads owned by the database system. NOTE: When the application is done using the returned IF_ThreadInfo + * object, it should call its Release method. + * + * @param ifppThreadInfo A pointer to the allocated thread info object is returned here. + * + * @return RCODE + */ + virtual RCODE XFLMAPI getThreadInfo( + IF_ThreadInfo ** ifppThreadInfo) = 0; + + /** + * @brief Register a catcher object to catch database events in a particular category. + * + * This method registers an object to catch events in a particular category of database events. + * + * @param eCategory The category of events the application is registering for. + * @param ifpEventClient The client object whose methods are to be invoked when the event occurs. + * + * @return RCODE + */ + virtual RCODE XFLMAPI registerForEvent( + eEventCategory eCategory, + IF_EventClient * ifpEventClient) = 0; + + /** + * @brief Deregister a catcher object from catching database events in a particular category. + * + * This method deregisters an object from catching events in a particular + * category of database events. + * + * @param eCategory The type of event. + * @param ifpEventClient The client object that was passed into the registerForEvent + * method. This is necessary so that if there are multiple objects that have + * registered for an event, XFlaim can know exactly which object to + * deregister. + */ + virtual void XFLMAPI deregisterForEvent( + eEventCategory eCategory, + IF_EventClient * ifpEventClient) = 0; + + /** + * @brief Returns the metaphone codes for the next word in an input stream. + * + * This parses the next word from the input stream (ifpIStream) and returns the metaphone codes for it. This + * method returns NE_XFLM_EOF_HIT when it hits the end of the input stream. Visit: This should probably be a + * method on the IF_IStream interface, not the IF_DbSystem interface. + * + * @param ifpIStream Input stream object. + * @param puiMetaphone Primary metaphone returned for the next word in the input stream. + * @param puiAltMetaphone Alternate metaphone returned for the next word in the input stream. + * + * @return RCODE + */ + virtual RCODE XFLMAPI getNextMetaphone( + IF_IStream * ifpIStream, + FLMUINT * puiMetaphone, + FLMUINT * puiAltMetaphone = NULL) = 0; + + /** + * @brief Return an IF_Pool object for memory allocations + */ + virtual RCODE XFLMAPI createMemoryPool( + IF_Pool ** ppPool) = 0; + + /** + * @brief Compares two UTF-8 strings + */ + virtual RCODE XFLMAPI compareUTF8Strings( + const FLMBYTE * pucLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMBYTE * pucRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult) = 0; + + /** + * @brief Compares two Unicode strings + */ + virtual RCODE XFLMAPI compareUnicodeStrings( + const FLMUNICODE * puzLString, + FLMUINT uiLStrBytes, + FLMBOOL bLeftWild, + const FLMUNICODE * puzRString, + FLMUINT uiRStrBytes, + FLMBOOL bRightWild, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMINT * piResult) = 0; + + virtual RCODE XFLMAPI utf8IsSubStr( + const FLMBYTE * pszString, + const FLMBYTE * pszSubString, + FLMUINT uiCompareRules, + FLMUINT uiLanguage, + FLMBOOL * pbExists) = 0; + + virtual FLMBOOL XFLMAPI uniIsUpper( + FLMUNICODE uzChar) = 0; + + virtual FLMBOOL XFLMAPI uniIsLower( + FLMUNICODE uzChar) = 0; + + virtual FLMBOOL XFLMAPI uniIsAlpha( + FLMUNICODE uzChar) = 0; + + virtual FLMBOOL XFLMAPI uniIsDecimalDigit( + FLMUNICODE uzChar) = 0; + + virtual FLMUNICODE XFLMAPI uniToLower( + FLMUNICODE uzChar) = 0; + + // When the nextUCS2Char method is called, the UCS-2 version of the character + // pointed to by *ppszUTF8 is stored in *puzChar and *ppszUTF8 is updated to + // point to the next the next character. If *ppszUTF8 >= pszEndOfUTF8String + // or if **ppszUTF8 == 0, *puzChar returns 0, but no error is returned. + // Note: Remember to keep a copy of the pointer to the start of the + // string, because whatever is passed in as ppszUTF8 will be modified. + + virtual RCODE XFLMAPI nextUCS2Char( + const FLMBYTE ** ppszUTF8, + const FLMBYTE * pszEndOfUTF8String, + FLMUNICODE * puzChar) = 0; + + virtual RCODE XFLMAPI numUCS2Chars( + const FLMBYTE * pszUTF8, + FLMUINT * puiNumChars) = 0; + + /** + * @brief Waits for a specific database to close + * + * @param pszDbName Name of the control file for the database that we + * want to have closed. + * + * @return RCODE + */ + virtual RCODE XFLMAPI waitToClose( + const char * pszDbFileName) = 0; + + /** + * @brief Creates an IF_NodeInfo interface object. + * + * This method creates an IF_NodeInfo interface object. + * + * @param ifppNodeInfo The IF_NodeInfo object. + * @return RCODE + */ + virtual RCODE XFLMAPI createIFNodeInfo( + IF_NodeInfo ** ifppNodeInfo) = 0; + + /** + * @brief Creates an IF_BTreeInfo interface object. + * + * This method creates an IF_BTreeInfo interface object. + * + * @param ifppBTreeInfo The IF_BTreeInfo object. + * @return RCODE + */ + virtual RCODE XFLMAPI createIFBTreeInfo( + IF_BTreeInfo ** ifppBTreeInfo) = 0; + + /** + * @brief A method to attempt to remove everything from cache + * + * This method will attempt to remove all blocks and nodes from + * cache. The pDb parameter is optional. If provided (and an update + * transaction is active), any dirty cache items associated with the + * database will be flushed. + * + * @return RCODE + */ + virtual RCODE XFLMAPI clearCache( + IF_Db * pDb) = 0; + }; + + typedef enum + { + XFLM_EXPORT_NO_FORMAT = 0x00, // No Formatting + XFLM_EXPORT_NEW_LINE = 0x01, // New Line For Each Element + XFLM_EXPORT_INDENT = 0x02, // Indent Elements + XFLM_EXPORT_INDENT_DATA = 0x03 // Indent Data - this changes the data + } eExportFormatType; + + /* -------------------------------------------------------------------- + * An alternative way to get a DbSystem object when not + * using COM. + * -------------------------------------------------------------------- */ + + XFLMEXTC RCODE FlmAllocDbSystem( + IF_DbSystem ** ppDbSystem); + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_Db : public XF_RefCount + { + virtual RCODE XFLMAPI transBegin( + eDbTransType eTransType, + FLMUINT uiMaxLockWait = XFLM_NO_TIMEOUT, + FLMUINT uiFlags = 0, + XFLM_DB_HDR * pDbHeader = NULL) = 0; + + virtual RCODE XFLMAPI transBegin( + IF_Db * pDb) = 0; + + virtual RCODE XFLMAPI transCommit( + FLMBOOL * pbEmpty = NULL) = 0; + + virtual RCODE XFLMAPI transAbort( void) = 0; + + virtual eDbTransType XFLMAPI getTransType( void) = 0; + + virtual RCODE XFLMAPI doCheckpoint( + FLMUINT uiTimeout) = 0; + + virtual RCODE XFLMAPI dbLock( + eDbLockType eLockType, + FLMINT iPriority, + FLMUINT uiTimeout) = 0; + + virtual RCODE XFLMAPI dbUnlock( void) = 0; + + virtual RCODE XFLMAPI getLockType( + eDbLockType * peLockType, + FLMBOOL * pbImplicit) = 0; + + virtual RCODE XFLMAPI getLockInfo( + FLMINT iPriority, + eDbLockType * peCurrLockType, + FLMUINT * puiThreadId, + FLMUINT * puiNumExclQueued, + FLMUINT * puiNumSharedQueued, + FLMUINT * puiPriorityCount) = 0; + + virtual RCODE XFLMAPI indexStatus( + FLMUINT uiIndexNum, + XFLM_INDEX_STATUS * pIndexStatus) = 0; + + virtual RCODE XFLMAPI indexGetNext( + FLMUINT * puiIndexNum) = 0; + + virtual RCODE XFLMAPI indexSuspend( + FLMUINT uiIndexNum) = 0; + + virtual RCODE XFLMAPI indexResume( + FLMUINT uiIndexNum) = 0; + + virtual RCODE XFLMAPI keyRetrieve( + FLMUINT uiIndex, + IF_DataVector * pSearchKey, + FLMUINT uiFlags, + IF_DataVector * pFoundKey) = 0; + + virtual RCODE XFLMAPI enableEncryption( void) = 0; + + virtual RCODE XFLMAPI wrapKey( + const char * pszPassword = NULL) = 0; + + virtual RCODE XFLMAPI rollOverDbKey( void) = 0; + + virtual RCODE XFLMAPI changeItemState( + FLMUINT uiDictType, + FLMUINT uiDictNum, + const char * pszState) = 0; + + virtual RCODE XFLMAPI reduceSize( + FLMUINT uiCount, + FLMUINT * puiCount) = 0; + + virtual RCODE XFLMAPI upgrade( + IF_UpgradeClient * pUpgradeClient) = 0; + + virtual RCODE XFLMAPI createRootElement( + FLMUINT uiCollection, + FLMUINT uiNameId, + IF_DOMNode ** ppElementNode, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE XFLMAPI createDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE XFLMAPI getFirstDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode) = 0; + + virtual RCODE XFLMAPI getLastDocument( + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode) = 0; + + virtual RCODE XFLMAPI getDocument( + FLMUINT uiCollection, + FLMUINT uiFlags, + FLMUINT64 ui64DocumentId, + IF_DOMNode ** ppDocumentNode) = 0; + + virtual RCODE XFLMAPI documentDone( + FLMUINT uiCollection, + FLMUINT64 ui64RootId) = 0; + + virtual RCODE XFLMAPI documentDone( + IF_DOMNode * pDocNode) = 0; + + virtual RCODE XFLMAPI createElementDef( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT uiDataType, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI createElementDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT uiDataType, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI createUniqueElmDef( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI createUniqueElmDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT * puiElementNameId = NULL, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI getElementNameId( + const char * pszNamespaceURI, + const char * pszElementName, + FLMUINT * puiElementNameId) = 0; + + virtual RCODE XFLMAPI getElementNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzElementName, + FLMUINT * puiElementNameId) = 0; + + virtual RCODE XFLMAPI createAttributeDef( + const char * pszNamespaceURI, + const char * pszAttributeName, + FLMUINT uiDataType, + FLMUINT * puiAttributeNameId, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI createAttributeDef( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzAttributeName, + FLMUINT uiDataType, + FLMUINT * puiAttributeNameId, + IF_DOMNode ** ppDocumentNode = NULL) = 0; + + virtual RCODE XFLMAPI getAttributeNameId( + const char * pszNamespaceURI, + const char * pszAttributeName, + FLMUINT * puiAttributeNameId) = 0; + + virtual RCODE XFLMAPI getAttributeNameId( + const FLMUNICODE * puzNamespaceURI, + const FLMUNICODE * puzAttributeName, + FLMUINT * puiAttributeNameId) = 0; + + virtual RCODE XFLMAPI createPrefixDef( + const char * pszPrefixName, + FLMUINT * puiPrefixNumber) = 0; + + virtual RCODE XFLMAPI createPrefixDef( + const FLMUNICODE * puzPrefixName, + FLMUINT * puiPrefixNumber) = 0; + + virtual RCODE XFLMAPI getPrefixId( + const char * pszPrefixName, + FLMUINT * puiPrefixNumber) = 0; + + virtual RCODE XFLMAPI getPrefixId( + const FLMUNICODE * puzPrefixName, + FLMUINT * puiPrefixNumber) = 0; + + virtual RCODE XFLMAPI createEncDef( + const char * pszEncType, + const char * pszEncName, + FLMUINT uiKeySize, + FLMUINT * puiEncDefNumber) = 0; + + virtual RCODE XFLMAPI createEncDef( + const FLMUNICODE * puzEncType, + const FLMUNICODE * puzEncName, + FLMUINT uiKeySize, + FLMUINT * puiEncDefNumber) = 0; + + virtual RCODE XFLMAPI getEncDefId( + const char * pszEncDefName, + FLMUINT * puiPrefixNumber) = 0; + + virtual RCODE XFLMAPI getEncDefId( + const FLMUNICODE * puzEncDefName, + FLMUINT * puiEncDefNumber) = 0; + + virtual RCODE XFLMAPI createCollectionDef( + const char * pszCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncNumber = 0) = 0; + + virtual RCODE XFLMAPI createCollectionDef( + const FLMUNICODE * puzCollectionName, + FLMUINT * puiCollectionNumber, + FLMUINT uiEncNumber = 0) = 0; + + virtual RCODE XFLMAPI getCollectionNumber( + const char * pszCollectionName, + FLMUINT * puiCollectionNumber) = 0; + + virtual RCODE XFLMAPI getCollectionNumber( + const FLMUNICODE * puzCollectionName, + FLMUINT * puiCollectionNumber) = 0; + + virtual RCODE XFLMAPI getIndexNumber( + const char * pszIndexName, + FLMUINT * puiIndexNumber) = 0; + + virtual RCODE XFLMAPI getIndexNumber( + const FLMUNICODE * puzIndexName, + FLMUINT * puiIndexNumber) = 0; + + virtual RCODE XFLMAPI getDictionaryDef( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + IF_DOMNode ** ppDocumentNode) = 0; + + virtual RCODE XFLMAPI getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + char * pszName, + FLMUINT * puiNameBufSize, + char * pszNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL) = 0; + + virtual RCODE XFLMAPI getDictionaryName( + FLMUINT uiDictType, + FLMUINT uiDictNumber, + FLMUNICODE * puzName, + FLMUINT * puiNameBufSize, + FLMUNICODE * puzNamespace = NULL, + FLMUINT * puiNamespaceBufSize = NULL) = 0; + + virtual RCODE XFLMAPI getNode( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + IF_DOMNode ** ppNode) = 0; + + virtual RCODE XFLMAPI getAttribute( + FLMUINT uiCollection, + FLMUINT64 ui64ElementNodeId, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppNode) = 0; + + virtual RCODE XFLMAPI getDataType( + FLMUINT uiDictType, + FLMUINT uiNameId, + FLMUINT * puiDataType) = 0; + + virtual RCODE XFLMAPI backupBegin( + eDbBackupType eBackupType, + eDbTransType eTransType, + FLMUINT uiMaxLockWait, + IF_Backup ** ppBackup) = 0; + + virtual void XFLMAPI getRflFileName( + FLMUINT uiFileNum, + FLMBOOL bBaseOnly, + char * pszFileName, + FLMUINT * puiFileNameBufSize, + FLMBOOL * pbNameTruncated = NULL) = 0; + + virtual RCODE XFLMAPI import( + IF_IStream * pIStream, + FLMUINT uiCollection, + IF_DOMNode * pNodeToLinkTo = NULL, + eNodeInsertLoc eInsertLoc = XFLM_LAST_CHILD, + XFLM_IMPORT_STATS * pImportStats = NULL) = 0; + + virtual RCODE XFLMAPI importDocument( + IF_IStream * ifpStream, + FLMUINT uiCollection, + IF_DOMNode ** ppDocumentNode = NULL, + XFLM_IMPORT_STATS * pImportStats = NULL) = 0; + + virtual RCODE XFLMAPI exportXML( + IF_DOMNode * pStartNode, + IF_OStream * pOStream, + eExportFormatType eFormat = XFLM_EXPORT_INDENT) = 0; + + virtual RCODE XFLMAPI setNextNodeId( + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId) = 0; + + virtual RCODE XFLMAPI setNextDictNum( + FLMUINT uiDictType, + FLMUINT uiDictNumber) = 0; + + // Configuration "set" and "get" methods + + virtual RCODE XFLMAPI setRflKeepFilesFlag( + FLMBOOL bKeep) = 0; + + virtual RCODE XFLMAPI getRflKeepFlag( + FLMBOOL * pbKeep) = 0; + + virtual RCODE XFLMAPI setRflDir( + const char * pszNewRflDir) = 0; + + virtual void XFLMAPI getRflDir( + char * pszRflDir) = 0; + + virtual RCODE XFLMAPI getRflFileNum( + FLMUINT * puiRflFileNum) = 0; + + virtual RCODE XFLMAPI getHighestNotUsedRflFileNum( + FLMUINT * puiHighestNotUsedRflFileNum) = 0; + + virtual RCODE XFLMAPI setRflFileSizeLimits( + FLMUINT uiMinRflSize, + FLMUINT uiMaxRflSize) = 0; + + virtual RCODE XFLMAPI getRflFileSizeLimits( + FLMUINT * puiRflMinFileSize, + FLMUINT * puiRflMaxFileSize) = 0; + + virtual RCODE XFLMAPI rflRollToNextFile( void) = 0; + + virtual RCODE XFLMAPI setKeepAbortedTransInRflFlag( + FLMBOOL bKeep) = 0; + + virtual RCODE XFLMAPI getKeepAbortedTransInRflFlag( + FLMBOOL * pbKeep) = 0; + + virtual RCODE XFLMAPI setAutoTurnOffKeepRflFlag( + FLMBOOL bAutoTurnOff) = 0; + + virtual RCODE XFLMAPI getAutoTurnOffKeepRflFlag( + FLMBOOL * pbAutoTurnOff) = 0; + + virtual void XFLMAPI setFileExtendSize( + FLMUINT uiFileExtendSize) = 0; + + virtual FLMUINT XFLMAPI getFileExtendSize( void) = 0; + + virtual void XFLMAPI setAppData( + void * pvAppData) = 0; + + virtual void * XFLMAPI getAppData( void) = 0; + + virtual void XFLMAPI setDeleteStatusObject( + IF_DeleteStatus * pDeleteStatus) = 0; + + virtual void XFLMAPI setCommitClientObject( + IF_CommitClient * pCommitClient) = 0; + + virtual void XFLMAPI setIndexingClientObject( + IF_IxClient * pIxClient) = 0; + + virtual void XFLMAPI setIndexingStatusObject( + IF_IxStatus * pIxStatus) = 0; + + // Configuration information getting methods + + virtual FLMUINT XFLMAPI getDbVersion( void) = 0; + + virtual FLMUINT XFLMAPI getBlockSize( void) = 0; + + virtual FLMUINT XFLMAPI getDefaultLanguage( void) = 0; + + virtual FLMUINT64 XFLMAPI getTransID( void) = 0; + + virtual void XFLMAPI getCheckpointInfo( + XFLM_CHECKPOINT_INFO * pCheckpointInfo) = 0; + + virtual RCODE XFLMAPI getDbControlFileName( + char * pszControlFileName, + FLMUINT uiControlFileBufSize) = 0; + + virtual RCODE XFLMAPI getLockWaiters( + IF_LockInfoClient * pLockInfo) = 0; + + virtual RCODE XFLMAPI getLastBackupTransID( + FLMUINT64 * pui64LastBackupTransID) = 0; + + virtual RCODE XFLMAPI getBlocksChangedSinceBackup( + FLMUINT * puiBlocksChangedSinceBackup) = 0; + + virtual RCODE XFLMAPI getNextIncBackupSequenceNum( + FLMUINT * puiNextIncBackupSequenceNum) = 0; + + virtual void XFLMAPI getSerialNumber( + char * pucSerialNumber) = 0; + + virtual RCODE XFLMAPI getDiskSpaceUsage( + FLMUINT64 * pui64DataSize, + FLMUINT64 * pui64RollbackSize, + FLMUINT64 * pui64RflSize) = 0; + + virtual RCODE XFLMAPI getMustCloseRC( void) = 0; + + virtual RCODE XFLMAPI getAbortRC( void) = 0; + + virtual void XFLMAPI setMustAbortTrans( + RCODE rc) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DOMNode : public XF_RefCount + { + virtual RCODE XFLMAPI createNode( + IF_Db * pDb, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewNode, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE XFLMAPI createChildElement( + IF_Db * pDb, + FLMUINT uiChildElementNameId, + eNodeInsertLoc eLocation, + IF_DOMNode ** ppNewChildElementNode, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE XFLMAPI deleteNode( + IF_Db * pDb) = 0; + + virtual RCODE XFLMAPI deleteChildren( + IF_Db * pDb, + FLMUINT uiNameId = 0) = 0; + + virtual RCODE XFLMAPI createAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE XFLMAPI getFirstAttribute( + IF_Db * pDb, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE XFLMAPI getLastAttribute( + IF_Db * pDb, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE XFLMAPI getAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode) = 0; + + virtual RCODE XFLMAPI deleteAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId) = 0; + + virtual RCODE XFLMAPI hasAttribute( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DOMNode ** ppAttrNode = NULL) = 0; + + virtual RCODE XFLMAPI hasAttributes( + IF_Db * pDb, + FLMBOOL * pbHasAttrs) = 0; + + virtual RCODE XFLMAPI hasNextSibling( + IF_Db * pDb, + FLMBOOL * pbHasNextSibling) = 0; + + virtual RCODE XFLMAPI hasPreviousSibling( + IF_Db * pDb, + FLMBOOL * pbHasPreviousSibling) = 0; + + virtual RCODE XFLMAPI hasChildren( + IF_Db * pDb, + FLMBOOL * pbHasChildren) = 0; + + virtual RCODE XFLMAPI isNamespaceDecl( + IF_Db * pDb, + FLMBOOL * pbIsNamespaceDecl) = 0; + + virtual eDomNodeType XFLMAPI getNodeType( void) = 0; + + virtual RCODE XFLMAPI getNodeId( + IF_Db * pDb, + FLMUINT64 * pui64NodeId) = 0; + + virtual RCODE XFLMAPI getParentId( + IF_Db * pDb, + FLMUINT64 * pui64ParentId) = 0; + + virtual RCODE XFLMAPI getDocumentId( + IF_Db * pDb, + FLMUINT64 * pui64DocumentId) = 0; + + virtual RCODE XFLMAPI getPrevSibId( + IF_Db * pDb, + FLMUINT64 * pui64PrevSibId) = 0; + + virtual RCODE XFLMAPI getNextSibId( + IF_Db * pDb, + FLMUINT64 * pui64NextSibId) = 0; + + virtual RCODE XFLMAPI getFirstChildId( + IF_Db * pDb, + FLMUINT64 * pui64FirstChildId) = 0; + + virtual RCODE XFLMAPI getLastChildId( + IF_Db * pDb, + FLMUINT64 * pui64LastChildId) = 0; + + virtual RCODE XFLMAPI getNameId( + IF_Db * pDb, + FLMUINT * puiNameId) = 0; + + virtual RCODE XFLMAPI getEncDefId( + IF_Db * pDb, + FLMUINT * puiEncDefId) = 0; + + virtual RCODE XFLMAPI getDataType( + IF_Db * pDb, + FLMUINT * puiDataType) = 0; + + virtual RCODE XFLMAPI getDataLength( + IF_Db * pDb, + FLMUINT * puiLength) = 0; + + virtual RCODE XFLMAPI getUINT32( + IF_Db * pDb, + FLMUINT32 * pui32Value) = 0; + + virtual RCODE XFLMAPI getUINT( + IF_Db * pDb, + FLMUINT * puiValue) = 0; + + virtual RCODE XFLMAPI getUINT64( + IF_Db * pDb, + FLMUINT64 * pui64Value) = 0; + + virtual RCODE XFLMAPI getINT32( + IF_Db * pDb, + FLMINT32 * pi32Value) = 0; + + virtual RCODE XFLMAPI getINT( + IF_Db * pDb, + FLMINT * piValue) = 0; + + virtual RCODE XFLMAPI getINT64( + IF_Db * pDb, + FLMINT64 * pi64Value) = 0; + + virtual RCODE XFLMAPI getMetaValue( + IF_Db * pDb, + FLMUINT64 * pui64Value) = 0; + + virtual RCODE XFLMAPI getUnicodeChars( + IF_Db * pDb, + FLMUINT * puiNumChars) = 0; + + virtual RCODE XFLMAPI getUnicode( + IF_Db * pDb, + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE XFLMAPI getUnicode( + IF_Db * pDb, + FLMUNICODE ** ppuzUnicodeValue) = 0; + + virtual RCODE XFLMAPI getUnicode( + IF_Db * pDb, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE XFLMAPI getUTF8( + IF_Db * pDb, + FLMBYTE * pucValueBuffer, + FLMUINT uiBufferSize, + FLMUINT uiCharOffset, + FLMUINT uiMaxCharsRequested, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE XFLMAPI getUTF8( + IF_Db * pDb, + FLMBYTE ** ppszUTF8Value) = 0; + + virtual RCODE XFLMAPI getUTF8( + IF_Db * pDb, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE XFLMAPI getBinary( + IF_Db * pDb, + void * pvValue, + FLMUINT uiByteOffset, + FLMUINT uiBytesRequested, + FLMUINT * puiBytesReturned) = 0; + + virtual RCODE XFLMAPI getBinary( + IF_Db * pDb, + IF_DynaBuf * pBuffer) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT32( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT32 * pui32Num) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT32( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT32 * pui32Num, + FLMUINT32 ui32NotFoundDefault) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT * puiNum) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT * puiNum, + FLMUINT uiNotFoundDefault) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Num) = 0; + + virtual RCODE XFLMAPI getAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 * pui64Num, + FLMUINT64 ui64NotFoundDefault) = 0; + + virtual RCODE XFLMAPI getAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT * piNum) = 0; + + virtual RCODE XFLMAPI getAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT * piNum, + FLMINT iNotFoundDefault) = 0; + + virtual RCODE XFLMAPI getAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT64 * pi64Num) = 0; + + virtual RCODE XFLMAPI getAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT64 * pi64Num, + FLMINT64 i64NotFoundDefault) = 0; + + virtual RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUNICODE * puzValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUNICODE ** ppuzValueBuffer) = 0; + + virtual RCODE XFLMAPI getAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMBYTE * pucValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL, + FLMUINT * puiBufferBytesUsed = NULL) = 0; + + virtual RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMBYTE ** ppszValueBuffer) = 0; + + virtual RCODE XFLMAPI getAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE XFLMAPI getAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrNameId, + void * pvValueBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiValueLength) = 0; + + virtual RCODE XFLMAPI getAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrNameId, + IF_DynaBuf * pDynaBuf) = 0; + + virtual RCODE XFLMAPI setUINT( + IF_Db * pDb, + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setUINT64( + IF_Db * pDb, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setINT( + IF_Db * pDb, + FLMINT iValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setINT64( + IF_Db * pDb, + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setMetaValue( + IF_Db * pDb, + FLMUINT64 ui64Value) = 0; + + virtual RCODE XFLMAPI setUnicode( + IF_Db * pDb, + const FLMUNICODE * puzValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setUTF8( + IF_Db * pDb, + const FLMBYTE * pszValue, + FLMUINT uiValueLength = 0, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setBinary( + IF_Db * pDb, + const void * pvValue, + FLMUINT uiValueLength, + FLMBOOL bLast = TRUE, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueUINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT uiValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueUINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMUINT64 ui64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueINT( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT iValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueINT64( + IF_Db * pDb, + FLMUINT uiAttrNameId, + FLMINT64 i64Value, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueUnicode( + IF_Db * pDb, + FLMUINT uiAttrNameId, + const FLMUNICODE * puzValue, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueUTF8( + IF_Db * pDb, + FLMUINT uiAttrNameId, + const FLMBYTE * pucValue, + FLMUINT uiLength = 0, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI setAttributeValueBinary( + IF_Db * pDb, + FLMUINT uiAttrNameId, + const void * pvValue, + FLMUINT uiLength, + FLMUINT uiEncDefId = 0) = 0; + + virtual RCODE XFLMAPI getDocumentNode( + IF_Db * pDb, + IF_DOMNode ** ppDocument) = 0; + + virtual RCODE XFLMAPI getNextDocument( + IF_Db * pDb, + IF_DOMNode ** ppNextDocument) = 0; + + virtual RCODE XFLMAPI getPreviousDocument( + IF_Db * pDb, + IF_DOMNode ** ppPrevDocument) = 0; + + virtual RCODE XFLMAPI getParentNode( + IF_Db * pDb, + IF_DOMNode ** ppParent) = 0; + + virtual RCODE XFLMAPI getFirstChild( + IF_Db * pDb, + IF_DOMNode ** ppFirstChild) = 0; + + virtual RCODE XFLMAPI getLastChild( + IF_Db * pDb, + IF_DOMNode ** ppLastChild) = 0; + + virtual RCODE XFLMAPI getNextSibling( + IF_Db * pDb, + IF_DOMNode ** ppNextSibling) = 0; + + virtual RCODE XFLMAPI getPreviousSibling( + IF_Db * pDb, + IF_DOMNode ** ppPrevSibling) = 0; + + virtual RCODE XFLMAPI getChild( + IF_Db * pDb, + eDomNodeType eNodeType, + IF_DOMNode ** ppChild) = 0; + + virtual RCODE XFLMAPI getChildElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppChild, + FLMUINT uiFlags = 0) = 0; + + virtual RCODE XFLMAPI getSiblingElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + FLMBOOL bNext, + IF_DOMNode ** ppSibling) = 0; + + virtual RCODE XFLMAPI getAncestorElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppAncestor) = 0; + + virtual RCODE XFLMAPI getDescendantElement( + IF_Db * pDb, + FLMUINT uiElementNameId, + IF_DOMNode ** ppDescendant) = 0; + + virtual RCODE XFLMAPI insertBefore( + IF_Db * pDb, + IF_DOMNode * pNewChild, + IF_DOMNode * pRefChild) = 0; + + virtual RCODE XFLMAPI getPrefix( + IF_Db * pDb, + FLMUNICODE * puzPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getPrefix( + IF_Db * pDb, + char * pszPrefixBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getPrefixId( + IF_Db * pDb, + FLMUINT * puiPrefixId) = 0; + + virtual RCODE XFLMAPI setPrefix( + IF_Db * pDb, + const FLMUNICODE * puzPrefix) = 0; + + virtual RCODE XFLMAPI setPrefix( + IF_Db * pDb, + const char * pszPrefix) = 0; + + virtual RCODE XFLMAPI setPrefixId( + IF_Db * pDb, + FLMUINT uiPrefixId) = 0; + + virtual RCODE XFLMAPI getNamespaceURI( + IF_Db * pDb, + FLMUNICODE * puzNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getNamespaceURI( + IF_Db * pDb, + char * pszNamespaceURIBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getLocalName( + IF_Db * pDb, + FLMUNICODE * puzLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getLocalName( + IF_Db * pDb, + char * pszLocalNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getQualifiedName( + IF_Db * pDb, + FLMUNICODE * puzQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getQualifiedName( + IF_Db * pDb, + char * pszQualifiedNameBuffer, + FLMUINT uiBufferSize, + FLMUINT * puiCharsReturned = NULL) = 0; + + virtual RCODE XFLMAPI getCollection( + IF_Db * pDb, + FLMUINT * puiCollection) = 0; + + virtual RCODE XFLMAPI createAnnotation( + IF_Db * pDb, + IF_DOMNode ** ppAnnotation, + FLMUINT64 * pui64NodeId = NULL) = 0; + + virtual RCODE XFLMAPI getAnnotation( + IF_Db * pDb, + IF_DOMNode ** ppAnnotation) = 0; + + virtual RCODE XFLMAPI getAnnotationId( + IF_Db * pDb, + FLMUINT64 * pui64AnnotationId) = 0; + + virtual RCODE XFLMAPI hasAnnotation( + IF_Db * pDb, + FLMBOOL * pbHasAnnotation) = 0; + + virtual RCODE XFLMAPI getIStream( + IF_Db * pDb, + IF_PosIStream ** ppIStream, + FLMUINT * puiDataType = NULL, + FLMUINT * puiDataLength = NULL) = 0; + + virtual RCODE XFLMAPI getTextIStream( + IF_Db * pDb, + IF_PosIStream ** ppIStream, + FLMUINT * puiNumChars = NULL) = 0; + + virtual FLMUINT XFLMAPI compareNode( + IF_DOMNode * pNode, + IF_Db * pDb1, + IF_Db * pDb2, + char * pszErrBuff, + FLMUINT uiErrBuffLen) = 0; + + virtual RCODE XFLMAPI isDataLocalToNode( + IF_Db * pDb, + FLMBOOL * pbDataIsLocal) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DataVector : public XF_RefCount + { + virtual void XFLMAPI setDocumentID( + FLMUINT64 ui64DocumentID) = 0; + + virtual RCODE XFLMAPI setID( + FLMUINT uiElementNumber, + FLMUINT64 ui64ID) = 0; + + virtual RCODE XFLMAPI setNameId( + FLMUINT uiElementNumber, + FLMUINT uiNameId, + FLMBOOL bIsAttr, + FLMBOOL bIsData) = 0; + + virtual RCODE XFLMAPI setINT( + FLMUINT uiElementNumber, + FLMINT iNum) = 0; + + virtual RCODE XFLMAPI setINT64( + FLMUINT uiElementNumber, + FLMINT64 i64Num) = 0; + + virtual RCODE XFLMAPI setUINT( + FLMUINT uiElementNumber, + FLMUINT uiNum) = 0; + + virtual RCODE XFLMAPI setUINT64( + FLMUINT uiElementNumber, + FLMUINT64 ui64Num) = 0; + + virtual RCODE XFLMAPI setUnicode( + FLMUINT uiElementNumber, + const FLMUNICODE * puzUnicode) = 0; + + virtual RCODE XFLMAPI setUTF8( + FLMUINT uiElementNumber, + const FLMBYTE * pszUtf8, + FLMUINT uiBytesInBuffer = 0) = 0; + + virtual RCODE XFLMAPI setBinary( + FLMUINT uiElementNumber, + const void * pvBinary, + FLMUINT uiBinaryLen) = 0; + + virtual void XFLMAPI setRightTruncated( + FLMUINT uiElementNumber) = 0; + + virtual void XFLMAPI setLeftTruncated( + FLMUINT uiElementNumber) = 0; + + virtual void XFLMAPI clearRightTruncated( + FLMUINT uiElementNumber) = 0; + + virtual void XFLMAPI clearLeftTruncated( + FLMUINT uiElementNumber) = 0; + + virtual FLMBOOL XFLMAPI isRightTruncated( + FLMUINT uiElementNumber) = 0; + + virtual FLMBOOL XFLMAPI isLeftTruncated( + FLMUINT uiElementNumber) = 0; + + virtual FLMUINT64 XFLMAPI getDocumentID( void) = 0; + + virtual FLMUINT64 XFLMAPI getID( + FLMUINT uiElementNumber) = 0; + + virtual FLMUINT XFLMAPI getNameId( + FLMUINT uiElementNumber) = 0; + + virtual FLMBOOL XFLMAPI isAttr( + FLMUINT uiElementNumber) = 0; + + virtual FLMBOOL XFLMAPI isDataComponent( + FLMUINT uiElementNumber) = 0; + + virtual FLMBOOL XFLMAPI isKeyComponent( + FLMUINT uiElementNumber) = 0; + + virtual FLMUINT XFLMAPI getDataLength( + FLMUINT uiElementNumber) = 0; + + virtual FLMUINT XFLMAPI getDataType( + FLMUINT uiElementNumber) = 0; + + virtual RCODE XFLMAPI getUTF8Ptr( + FLMUINT uiElementNumber, + const FLMBYTE ** ppszUTF8, + FLMUINT * puiBufLen) = 0; + + virtual RCODE XFLMAPI getINT( + FLMUINT uiElementNumber, + FLMINT * piNum) = 0; + + virtual RCODE XFLMAPI getINT64( + FLMUINT uiElementNumber, + FLMINT64 * pi64Num) = 0; + + virtual RCODE XFLMAPI getUINT( + FLMUINT uiElementNumber, + FLMUINT * puiNum) = 0; + + virtual RCODE XFLMAPI getUINT64( + FLMUINT uiElementNumber, + FLMUINT64 * pui64Num) = 0; + + virtual RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + FLMUNICODE ** ppuzUnicode) = 0; + + virtual RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + FLMUNICODE * puzUnicode, + FLMUINT * puiBufLen) = 0; + + virtual RCODE XFLMAPI getUnicode( + FLMUINT uiElementNumber, + IF_DynaBuf * pBuffer) = 0; + + virtual RCODE XFLMAPI getUTF8( + FLMUINT uiElementNumber, + FLMBYTE * pszUTF8, + FLMUINT * puiBufLen) = 0; + + virtual RCODE XFLMAPI getBinary( + FLMUINT uiElementNumber, + void * pvBuffer, + FLMUINT * puiBufferLen) = 0; + + virtual RCODE XFLMAPI outputKey( + IF_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT uiMatchFlags, + FLMBYTE * pucKeyBuf, + FLMUINT uiKeyBufSize, + FLMUINT * puiKeyLen) = 0; + + virtual RCODE XFLMAPI outputData( + IF_Db * pDb, + FLMUINT uiIndexNum, + FLMBYTE * pucDataBuf, + FLMUINT uiDataBufSize, + FLMUINT * puiDataLen) = 0; + + virtual RCODE XFLMAPI inputKey( + IF_Db * pDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucKey, + FLMUINT uiKeyLen) = 0; + + virtual RCODE XFLMAPI inputData( + IF_Db * pDb, + FLMUINT uiIndexNum, + const FLMBYTE * pucData, + FLMUINT uiDataLen) = 0; + + // Miscellaneous methods + + virtual void XFLMAPI reset( void) = 0; + + virtual const void * XFLMAPI getDataPtr( + FLMUINT uiElementNumber) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_Backup : public XF_RefCount + { + virtual FLMUINT64 XFLMAPI getBackupTransId( void) = 0; + + virtual FLMUINT64 XFLMAPI getLastBackupTransId( void) = 0; + + virtual RCODE XFLMAPI backup( + const char * pszBackupPath, + const char * pszPassword, + IF_BackupClient * ifpClient, + IF_BackupStatus * ifpStatus, + FLMUINT * puiIncSeqNum) = 0; + + virtual RCODE XFLMAPI endBackup( void) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_ThreadInfo : public XF_RefCount + { + virtual FLMUINT XFLMAPI getNumThreads( void) = 0; + + virtual void XFLMAPI getThreadInfo( + FLMUINT uiThreadNum, + FLMUINT * puiThreadId, + FLMUINT * puiThreadGroup, + FLMUINT * puiAppId, + FLMUINT * puiStartTime, + const char ** ppszThreadName, + const char ** ppszThreadStatus) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_IStream : public XF_RefCount + { + /** + * @brief Reads data from the input stream. + * + */ + virtual RCODE XFLMAPI read( + void * pvBuffer, + FLMUINT uiBytesToRead, + FLMUINT * puiBytesRead = NULL) = 0; + + /** + * @brief Close the input stream. + * + */ + virtual void XFLMAPI close( void) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_PosIStream : public IF_IStream + { + virtual FLMUINT64 XFLMAPI totalSize( void) = 0; + + virtual FLMUINT64 XFLMAPI remainingSize( void) = 0; + + virtual RCODE XFLMAPI positionTo( + FLMUINT64 ui64Position) = 0; + + virtual FLMUINT64 XFLMAPI getCurrPosition( void) = 0; + }; + + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_OStream : public XF_RefCount + { + /** + * @brief Writes data to the output stream. + * + */ + virtual RCODE XFLMAPI write( + const void * pvBuffer, + FLMUINT uiBytesToWrite, + FLMUINT * puiBytesWritten = NULL) = 0; + + /** + * @brief Close the output stream. + * + */ + virtual RCODE XFLMAPI close( void) = 0; + }; + + + // Note: Any interfaces ending in Client or Status are interfaces + // that XFlaim does not provide implementations of. They exist to + // allow XFlaim to pass data back to the client. Interfaces ending in + // Status are, generally, informational only, while interfaces ending + // in Client exist to allow the client to modify the data or take + // other action. + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_BackupClient : public XF_RefCount + { + virtual RCODE XFLMAPI WriteData( + const void * pvBuffer, + FLMUINT uiBytesToWrite) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_BackupStatus : public XF_RefCount + { + virtual RCODE XFLMAPI backupStatus( + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_CommitClient : public XF_RefCount + { + virtual void XFLMAPI commit( + IF_Db * pDb) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_EventClient : public XF_RefCount + { + virtual void XFLMAPI catchEvent( + eEventType eEvent, + IF_Db * pDb, + FLMUINT uiThreadId, + FLMUINT64 ui64TransID, + FLMUINT uiIndexOrCollection, + FLMUINT64 ui64NodeId, + RCODE rc) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_IxClient : public XF_RefCount + { + virtual RCODE XFLMAPI doIndexing( + IF_Db * pDb, + FLMUINT uiIndexNum, + FLMUINT uiCollectionNum, + IF_DOMNode * pDocNode) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_LockInfoClient : public XF_RefCount + { + virtual FLMBOOL XFLMAPI setLockCount( // Return TRUE to continue, FALSE to stop + FLMUINT uiTotalLocks) = 0; + + virtual FLMBOOL XFLMAPI addLockInfo( // Return TRUE to continue, FALSE to stop + FLMUINT uiLockNum, // Position in queue (0 = lock holder, + // 1 ... n = lock waiter) + FLMUINT uiThreadID, // Thread ID of the lock holder/waiter + FLMUINT uiTime) = 0; // For the lock holder, this is the + // time when the lock was obtained. + // For a lock waiter, this is the time + // that the waiter was placed in the queue. + // Both times are presented in milliseconds. + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_LoggerClient : public XF_RefCount + { + virtual IF_LogMessageClient * XFLMAPI beginMessage( + eLogMessageType eMsgType) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_LogMessageClient : public XF_RefCount + { + virtual void XFLMAPI changeColor( + eColorType eForeColor, + eColorType eBackColor) = 0; + + virtual void XFLMAPI appendString( + const char * pszStr) = 0; + + virtual void XFLMAPI newline( void) = 0; + + virtual void XFLMAPI endMessage( void) = 0; + + virtual void XFLMAPI pushForegroundColor( void) = 0; + + virtual void XFLMAPI popForegroundColor( void) = 0; + + virtual void XFLMAPI pushBackgroundColor( void) = 0; + + virtual void XFLMAPI popBackgroundColor( void) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_RestoreStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportProgress( + eRestoreAction * peAction, + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone) = 0; + + virtual RCODE XFLMAPI reportError( + eRestoreAction * peAction, + RCODE rcErr) = 0; + + virtual RCODE XFLMAPI reportOpenRflFile( + eRestoreAction * peAction, + FLMUINT uiFileNum) = 0; + + virtual RCODE XFLMAPI reportRflRead( + eRestoreAction * peAction, + FLMUINT uiFileNum, + FLMUINT uiBytesRead) = 0; + + virtual RCODE XFLMAPI reportBeginTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportCommitTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportAbortTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportBlockChainFree( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT64 ui64MaintDocNum, + FLMUINT uiStartBlkAddr, + FLMUINT uiEndBlkAddr, + FLMUINT uiCount) = 0; + + virtual RCODE XFLMAPI reportIndexSuspend( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiIndexNum) = 0; + + virtual RCODE XFLMAPI reportIndexResume( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiIndexNum) = 0; + + virtual RCODE XFLMAPI reportReduce( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCount) = 0; + + virtual RCODE XFLMAPI reportUpgrade( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiOldDbVersion, + FLMUINT uiNewDbVersion) = 0; + + virtual RCODE XFLMAPI reportEnableEncryption( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportWrapKey( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportRollOverDbKey( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) = 0; + + virtual RCODE XFLMAPI reportDocumentDone( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64DocumentId) = 0; + + virtual RCODE XFLMAPI reportNodeDelete( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) = 0; + + virtual RCODE XFLMAPI reportAttributeDelete( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64ElementId, + FLMUINT uiAttrNameId) = 0; + + virtual RCODE XFLMAPI reportNodeChildrenDelete( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64ParentNodeId, + FLMUINT uiNameId) = 0; + + virtual RCODE XFLMAPI reportNodeCreate( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64RefNodeId, + eDomNodeType eNodeType, + FLMUINT uiNameId, + eNodeInsertLoc eLocation) = 0; + + virtual RCODE XFLMAPI reportInsertBefore( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64ParentNodeId, + FLMUINT64 ui64NewChildNodeId, + FLMUINT64 ui64RefChildNodeId) = 0; + + virtual RCODE XFLMAPI reportNodeUpdate( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) = 0; + + virtual RCODE XFLMAPI reportNodeSetValue( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId) = 0; + + virtual RCODE XFLMAPI reportAttributeSetValue( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64ElementNodeId, + FLMUINT uiAttrNameId) = 0; + + virtual RCODE XFLMAPI reportNodeFlagsUpdate( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiFlags, + FLMBOOL bAdd) = 0; + + virtual RCODE XFLMAPI reportNodeSetPrefixId( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + FLMUINT uiPrefixId) = 0; + + virtual RCODE XFLMAPI reportNodeSetMetaValue( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT64 ui64MetaValue) = 0; + + virtual RCODE XFLMAPI reportSetNextNodeId( + eRestoreAction * peAction, + FLMUINT64 ui64TransId, + FLMUINT uiCollection, + FLMUINT64 ui64NextNodeId) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_RestoreClient : public XF_RefCount + { + virtual RCODE XFLMAPI openBackupSet( void) = 0; + + virtual RCODE XFLMAPI openRflFile( // Open an RFL file + FLMUINT uiFileNum) = 0; + + virtual RCODE XFLMAPI openIncFile( // Open an incremental backup file + FLMUINT uiFileNum) = 0; + + virtual RCODE XFLMAPI read( + FLMUINT uiLength, // Number of bytes to read + void * pvBuffer, // Buffer to place read bytes into + FLMUINT * puiBytesRead) = 0; // [out] Number of bytes read + + virtual RCODE XFLMAPI close( void) = 0; // Close the current file + + virtual RCODE XFLMAPI abortFile( void) = 0; // Abort processing the file + // and close file handles, etc. + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_UpgradeClient : public XF_RefCount + { + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DeleteStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportDelete( + FLMUINT uiIndexOrCollectionNum, + FLMBOOL bIsIndex, + FLMUINT uiBlocksDeleted, + FLMUINT uiBlockSize) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DbCopyStatus : public XF_RefCount + { + virtual RCODE XFLMAPI dbCopyStatus( + FLMUINT64 ui64BytesToCopy, + FLMUINT64 ui64BytesCopied, + FLMBOOL bNewSrcFile, + const char * pszSrcFileName, + const char * pszDestFileName) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DbRebuildStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportRebuild( + XFLM_REBUILD_INFO * pRebuild) = 0; + + virtual RCODE XFLMAPI reportRebuildErr( + XFLM_CORRUPT_INFO * pCorruptInfo) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DbCheckStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportProgress( + XFLM_PROGRESS_CHECK_INFO * pProgCheck) = 0; + + virtual RCODE XFLMAPI reportCheckErr( + XFLM_CORRUPT_INFO * pCorruptInfo, + FLMBOOL * pbFix) = 0; + // [OUT] - If the client sets this to true, then XFlaim will + // attempt to fix the problem. NOTE: It is allowable for + // XFlaim to pass in NULL here!! (This means that the client + // doesn't have a choice regarding XFlaim's actions.) The + // client must check for NULL before attempting to assing a + // value to this parameter!! + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DbRenameStatus : public XF_RefCount + { + virtual RCODE XFLMAPI dbRenameStatus( + const char * pszSrcFileName, + const char * pszDstFileName) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_IxStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportIndex( + FLMUINT64 ui64LastDocumentId) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_FileSystem : public XF_RefCount + { + virtual RCODE XFLMAPI Create( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) = 0; + + virtual RCODE XFLMAPI CreateBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFileHdl) = 0; + + virtual RCODE XFLMAPI CreateUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) = 0; + + virtual RCODE XFLMAPI Open( + const char * pszFileName, + FLMUINT uiIoFlags, + IF_FileHdl ** ppFileHdl) = 0; + + virtual RCODE XFLMAPI OpenBlockFile( + const char * pszFileName, + FLMUINT uiIoFlags, + FLMUINT uiBlockSize, + IF_FileHdl ** ppFileHdl) = 0; + + virtual RCODE XFLMAPI OpenDir( + const char * pszDirName, + const char * pszPattern, + IF_DirHdl ** ppDirHdl) = 0; + + virtual RCODE XFLMAPI CreateDir( + const char * pszDirName) = 0; + + virtual RCODE XFLMAPI RemoveDir( + const char * pszDirName, + FLMBOOL bClear = FALSE) = 0; + + virtual RCODE XFLMAPI Exists( + const char * pszFileName) = 0; + + virtual FLMBOOL XFLMAPI IsDir( + const char * pszFileName) = 0; + + virtual RCODE XFLMAPI GetTimeStamp( + const char * pszFileName, + FLMUINT * puiTimeStamp) = 0; + + virtual RCODE XFLMAPI Delete( + const char * pszFileName) = 0; + + virtual RCODE XFLMAPI Copy( + const char * pszSrcFileName, + const char * pszDestFileName, + FLMBOOL bOverwrite, + FLMUINT64 * pui64BytesCopied) = 0; + + virtual RCODE XFLMAPI Rename( + const char * pszFileName, + const char * pszNewFileName) = 0; + + virtual RCODE XFLMAPI GetSectorSize( + const char * pszFileName, + FLMUINT * puiSectorSize) = 0; + + virtual void XFLMAPI pathParse( + const char * pszPath, + char * pszServer, + char * pszVolume, + char * pszDirPath, + char * pszFileName) = 0; + + virtual RCODE XFLMAPI pathReduce( + const char * pszSourcePath, + char * pszDestPath, + char * pszString) = 0; + + virtual RCODE XFLMAPI pathAppend( + char * pszPath, + const char * pszPathComponent) = 0; + + virtual RCODE XFLMAPI pathToStorageString( + const char * pPath, + char * pszString) = 0; + + virtual void XFLMAPI pathCreateUniqueName( + FLMUINT * puiTime, + char * pFileName, + const char * pFileExt, + FLMBYTE * pHighChars, + FLMBOOL bModext) = 0; + + virtual FLMBOOL XFLMAPI doesFileMatch( + const char * pszFileName, + const char * pszTemplate) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_FileHdl : public XF_RefCount + { + virtual RCODE XFLMAPI Close( void) = 0; + + virtual RCODE XFLMAPI Create( + const char * pszFileName, + FLMUINT uiIoFlags) = 0; + + virtual RCODE XFLMAPI CreateUnique( + const char * pszDirName, + const char * pszFileExtension, + FLMUINT uiIoFlags) = 0; + + virtual RCODE XFLMAPI Open( + const char * pszFileName, + FLMUINT uiIoFlags) = 0; + + virtual RCODE XFLMAPI Flush( void) = 0; + + virtual RCODE XFLMAPI Read( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead) = 0; + + virtual RCODE XFLMAPI Seek( + FLMUINT64 ui64Offset, + FLMINT iWhence, + FLMUINT64 * pui64NewOffset) = 0; + + virtual RCODE XFLMAPI Size( + FLMUINT64 * pui64Size) = 0; + + virtual RCODE XFLMAPI Tell( + FLMUINT64 * pui64Offset) = 0; + + virtual RCODE XFLMAPI Truncate( + FLMUINT64 ui64Size) = 0; + + virtual RCODE XFLMAPI Write( + FLMUINT64 ui64Offset, + FLMUINT uiLength, + const void * pvBuffer, + FLMUINT * puiBytesWritten) = 0; + + // Some I/O subsystems (such as direct IO) can only read and write sectors + // (512 byte chunks). If uiOffset is not on a sector boundary or + // uiLength is not an exact multiple of a sector size, the I/O system + // would have to try to read or write a partial sector - something that + // requires extra overhead, particularly for write operations - because + // in order to write a partial sector, the I/O subsystem first has to + // read the sector in to memory before writing it out in order to + // preserve the part of the sector that was not being written to. + + // The SectorRead and SectorWrite routines are provided to allow + // the caller to tell the I/O subsystem that it is OK to do full + // sector reads or writes if it needs to, because pvBuffer is + // guaranteed to be a multiple of 512 bytes big. If the I/O + // subsystem can only do sector reads and writes, it can use the + // extra buffer space in pvBuffer. When a program calls SectorWrite + // it is also telling the I/O subsystem that it does not need to + // read a partially written sector from disk before writing it out. + // It will be OK to write whatever data is in the pvBuffer to fill out + // the sector. + + virtual RCODE XFLMAPI SectorRead( + FLMUINT64 ui64ReadOffset, + FLMUINT uiBytesToRead, + void * pvBuffer, + FLMUINT * puiBytesReadRV) = 0; + + virtual RCODE XFLMAPI SectorWrite( + FLMUINT64 ui64WriteOffset, + FLMUINT uiBytesToWrite, + const void * pvBuffer, + FLMUINT uiBufferSize, + void * pvBufferObj, + FLMUINT * puiBytesWrittenRV, + FLMBOOL bZeroFill = TRUE) = 0; + + virtual FLMBOOL XFLMAPI CanDoAsync( void) = 0; + + virtual void XFLMAPI setExtendSize( + FLMUINT uiExtendSize) = 0; + + virtual void XFLMAPI setMaxAutoExtendSize( + FLMUINT uiMaxAutoExtendSize) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DirHdl : public XF_RefCount + { + virtual RCODE XFLMAPI Next( void) = 0; + + virtual const char * XFLMAPI CurrentItemName( void) = 0; + + virtual void XFLMAPI CurrentItemPath( + char * pszPath) = 0; + + virtual FLMUINT64 XFLMAPI CurrentItemSize( void) = 0; + + virtual FLMBOOL XFLMAPI CurrentItemIsDir( void) = 0; + + virtual RCODE XFLMAPI OpenDir( + const char * pszDirName, + const char * pszPattern) = 0; + + virtual RCODE XFLMAPI CreateDir( + const char * pDirName) = 0; + + virtual RCODE XFLMAPI RemoveDir( + const char * pDirPath) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_DbInfo : public XF_RefCount + { + virtual FLMUINT XFLMAPI getNumCollections( void) = 0; + + virtual FLMUINT XFLMAPI getNumIndexes( void) = 0; + + virtual FLMUINT XFLMAPI getNumLogicalFiles( void) = 0; + + virtual FLMUINT64 XFLMAPI getFileSize( void) = 0; + + virtual const XFLM_DB_HDR * XFLMAPI getDbHdr( void) = 0; + + virtual void XFLMAPI getAvailBlockStats( + FLMUINT64 * pui64BytesUsed, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors) = 0; + + virtual void XFLMAPI getLFHBlockStats( + FLMUINT64 * pui64BytesUsed, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors) = 0; + + virtual void XFLMAPI getBTreeInfo( + FLMUINT uiNthLogicalFile, + FLMUINT * puiLfNum, + eLFileType * peLfType, + FLMUINT * puiRootBlkAddress, + FLMUINT * puiNumLevels) = 0; + + virtual void XFLMAPI getBTreeBlockStats( + FLMUINT uiNthLogicalFile, + FLMUINT uiLevel, + FLMUINT64 * pui64KeyCount, + FLMUINT64 * pui64BytesUsed, + FLMUINT64 * pui64ElementCount, + FLMUINT64 * pui64ContElementCount, + FLMUINT64 * pui64ContElmBytes, + FLMUINT * puiBlockCount, + FLMINT * piLastError, + FLMUINT * puiNumErrors) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_ResultSetCompare : public XF_RefCount + { + virtual RCODE XFLMAPI compare( + const void * pvData1, + FLMUINT uiLength1, + const void * pvData2, + FLMUINT uiLength2, + FLMINT * piCompare) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_ResultSetSortStatus : public XF_RefCount + { + virtual RCODE XFLMAPI reportSortStatus( + FLMUINT64 ui64EstTotalUnits, + FLMUINT64 ui64UnitsDone) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_ResultSet : public XF_RefCount + { + virtual RCODE XFLMAPI setupResultSet( + const char * pszPath, + IF_ResultSetCompare * pCompare, + FLMUINT uiEntrySize, + FLMBOOL bDropDuplicates = TRUE, + FLMBOOL bEntriesInOrder = FALSE, + const char * pszFileName = NULL) = 0; + + virtual void XFLMAPI setSortStatus( + IF_ResultSetSortStatus * pSortStatus) = 0; + + virtual FLMUINT64 XFLMAPI getTotalEntries( void) = 0; + + // Methods for building a result set. + + virtual RCODE XFLMAPI addEntry( + const void * pvEntry, + FLMUINT uiEntryLength = 0) = 0; + + virtual RCODE XFLMAPI finalizeResultSet( + FLMUINT64 * pui64TotalEntries = NULL) = 0; + + // Methods for reading entries from a result set + + virtual RCODE XFLMAPI getFirst( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL) = 0; + + virtual RCODE XFLMAPI getNext( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL) = 0; + + virtual RCODE XFLMAPI getLast( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL) = 0; + + virtual RCODE XFLMAPI getPrev( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL) = 0; + + virtual RCODE XFLMAPI getCurrent( + void * pvEntryBuffer, + FLMUINT uiBufferLength = 0, + FLMUINT * puiEntryLength = NULL) = 0; + + virtual RCODE XFLMAPI findMatch( + const void * pvMatchEntry, + void * pvFoundEntry) = 0; + + virtual RCODE XFLMAPI findMatch( + const void * pvMatchEntry, + FLMUINT uiMatchEntryLength, + void * pvFoundEntry, + FLMUINT * puiFoundEntryLength) = 0; + + virtual RCODE XFLMAPI modifyCurrent( + const void * pvEntry, + FLMUINT uiEntryLength = 0) = 0; + + virtual FLMUINT64 XFLMAPI getPosition( void) = 0; + + virtual RCODE XFLMAPI setPosition( + FLMUINT64 ui64Position) = 0; + + virtual RCODE XFLMAPI resetResultSet( + FLMBOOL bDelete = TRUE) = 0; + + virtual RCODE XFLMAPI flushToFile( void) = 0; + + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_QueryStatus : public XF_RefCount + { + virtual RCODE XFLMAPI queryStatus( + XFLM_OPT_INFO * pOptInfo) = 0; + + virtual RCODE XFLMAPI newSource( + XFLM_OPT_INFO * pOptInfo) = 0; + + virtual RCODE XFLMAPI resultSetStatus( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed, + FLMBOOL bCanRetrieveDocs) = 0; + + virtual RCODE XFLMAPI resultSetComplete( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_QueryValidator : public XF_RefCount + { + virtual RCODE XFLMAPI validateNode( + IF_Db * pDb, + IF_DOMNode * pNode, + FLMBOOL * pbPassed) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_QueryValFunc : public XF_RefCount + { + // NOTE: pDynaBuf should only be used when returning XFLM_UTF8_VAL or + // XFLM_BINARY_VAL. pvVal should be used for all other types. + // If there are no more values, return NE_XFLM_EOF_HIT or + // NE_XFLM_BOF_HIT, depending on eValueToGet. + + virtual RCODE XFLMAPI getValue( + IF_Db * pDb, + IF_DOMNode * pContextNode, + ValIterator eValueToGet, + eValTypes * peValType, + FLMBOOL * pbLastValue, + void * pvVal, + IF_DynaBuf * pDynaBuf = NULL) = 0; + + virtual RCODE XFLMAPI cloneSelf( + IF_QueryValFunc ** ppNewObj) = 0; + }; + + /*============================================================================ + Desc: Abstract base class which provides the interface that + XFLAIM uses to allow an application to embed a node source + inside an XPATH component. + ============================================================================*/ + xflminterface IF_QueryNodeSource : public XF_RefCount + { + public: + + // Method that returns the search cost of this object in providing + // nodes for a query. + + virtual RCODE XFLMAPI searchCost( + IF_Db * pDb, + FLMBOOL bNotted, + FLMUINT * puiCost, + FLMBOOL * pbMustScan) = 0; + + // Position to and return the first node that satisfies the predicate. + + virtual RCODE XFLMAPI getFirst( + IF_Db * pDb, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, // milliseconds + IF_QueryStatus * pQueryStatus) = 0; + + // Position to and return the last node that satisfies the predicate. + + virtual RCODE XFLMAPI getLast( + IF_Db * pDb, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, // milliseconds + IF_QueryStatus * pQueryStatus) = 0; + + // Position to and return the next node that satisfies the predicate. + // If no prior positioning has been done, + // position to and return the first node. + + virtual RCODE XFLMAPI getNext( + IF_Db * pDb, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, // milliseconds + IF_QueryStatus * pQueryStatus) = 0; + + // Position to and return the previous node that satisfies the predicate. + // If no prior positioning has been done, + // position to and return the last node. + + virtual RCODE XFLMAPI getPrev( + IF_Db * pDb, + IF_DOMNode * pContextNode, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, // milliseconds + IF_QueryStatus * pQueryStatus) = 0; + + // Return index being used, 0 if none. + + virtual RCODE XFLMAPI getIndex( + IF_Db * pDb, + FLMUINT * puiIndex, + FLMBOOL * pbHaveMultiple) = 0; + + virtual RCODE XFLMAPI getOptInfoCount( + IF_Db * pDb, + FLMUINT * puiOptInfoCount) = 0; + + virtual RCODE XFLMAPI getOptInfo( + IF_Db * pDb, + XFLM_OPT_INFO * pOptInfoArray, + FLMUINT uiNumOptInfoStructsToGet) = 0; + + // Return a copy of the object. Result set should be + // emptied, score should be unset - only the predicate + // should be preserved. + // Returns NULL if the copy fails. + + virtual RCODE copy( + IF_QueryNodeSource ** ppNodeSourceCopy) = 0; + + virtual void releaseResources( void) = 0; + }; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_OperandComparer : public XF_RefCount + { + virtual RCODE XFLMAPI compare( + IF_PosIStream * pLeftOperandStream, + IF_PosIStream * pRightOperandStream, + FLMINT * piCompare) = 0; + }; + + #define XFLM_MAX_SORT_KEYS 32 + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_Query : public XF_RefCount + { + virtual RCODE XFLMAPI setLanguage( + FLMUINT uiLanguage) = 0; + + virtual RCODE XFLMAPI setCollection( + FLMUINT uiCollection) = 0; + + virtual RCODE XFLMAPI setupQueryExpr( + IF_Db * pDb, + const FLMUNICODE * puzQuery) = 0; + + virtual RCODE XFLMAPI setupQueryExpr( + IF_Db * pDb, + const char * pszQueryExpr) = 0; + + virtual RCODE XFLMAPI copyCriteria( + IF_Query * pSrcQuery) = 0; + + virtual RCODE XFLMAPI addXPathComponent( + eXPathAxisTypes eXPathAxis, + eDomNodeType eNodeType, + FLMUINT uiNameId, + IF_QueryNodeSource * pNodeSource = NULL) = 0; + + virtual RCODE XFLMAPI addOperator( + eQueryOperators eOperator, + FLMUINT uiCompareRules = 0, + IF_OperandComparer * pOpComparer = NULL) = 0; + + virtual RCODE XFLMAPI addUnicodeValue( + const FLMUNICODE * puzVal) = 0; + + virtual RCODE XFLMAPI addUTF8Value( + const char * pszVal, + FLMUINT uiUTF8Len = 0) = 0; + + virtual RCODE XFLMAPI addBinaryValue( + const void * pvVal, + FLMUINT uiValLen) = 0; + + virtual RCODE XFLMAPI addUINTValue( + FLMUINT uiVal) = 0; + + virtual RCODE XFLMAPI addINTValue( + FLMINT iVal) = 0; + + virtual RCODE XFLMAPI addUINT64Value( + FLMUINT64 ui64Val) = 0; + + virtual RCODE XFLMAPI addINT64Value( + FLMINT64 i64Val) = 0; + + virtual RCODE XFLMAPI addBoolean( + FLMBOOL bVal, + FLMBOOL bUnknown = FALSE) = 0; + + virtual RCODE XFLMAPI addFunction( + eQueryFunctions eFunction) = 0; + + virtual RCODE XFLMAPI addFunction( + IF_QueryValFunc * pFuncObj, + FLMBOOL bHasXPathExpr) = 0; + + virtual RCODE XFLMAPI getFirst( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0) = 0; // milliseconds + + virtual RCODE XFLMAPI getLast( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0) = 0; // milliseconds + + virtual RCODE XFLMAPI getNext( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0, // milliseconds + FLMUINT uiNumToSkip = 0, + FLMUINT * puiNumSkipped = NULL) = 0; + + virtual RCODE XFLMAPI getPrev( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit = 0, // milliseconds + FLMUINT uiNumToSkip = 0, + FLMUINT * puiNumSkipped = NULL) = 0; + + virtual RCODE XFLMAPI getCurrent( + IF_Db * pDb, + IF_DOMNode ** ppNode) = 0; + + virtual void XFLMAPI resetQuery( void) = 0; + + virtual RCODE XFLMAPI getStatsAndOptInfo( + FLMUINT * puiNumOptInfos, + XFLM_OPT_INFO ** ppOptInfo) = 0; + + virtual void XFLMAPI freeStatsAndOptInfo( + XFLM_OPT_INFO ** ppOptInfo) = 0; + + virtual void XFLMAPI setDupHandling( + FLMBOOL bRemoveDups) = 0; + + virtual RCODE XFLMAPI setIndex( + FLMUINT uiIndex) = 0; + + virtual RCODE XFLMAPI getIndex( + IF_Db * pDb, + FLMUINT * puiIndex, + FLMBOOL * pbHaveMultiple) = 0; + + virtual RCODE XFLMAPI addSortKey( + void * pvSortKeyContext, + FLMBOOL bChildToContext, + FLMBOOL bElement, + FLMUINT uiNameId, + FLMUINT uiCompareRules, + FLMUINT uiLimit, + FLMUINT uiKeyComponent, + FLMBOOL bSortDescending, + FLMBOOL bSortMissingHigh, + void ** ppvContext) = 0; + + virtual RCODE XFLMAPI enablePositioning( void) = 0; + + virtual RCODE XFLMAPI positionTo( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + FLMUINT uiPosition) = 0; + + virtual RCODE XFLMAPI positionTo( + IF_Db * pDb, + IF_DOMNode ** ppNode, + FLMUINT uiTimeLimit, + IF_DataVector * pSearchKey, + FLMUINT uiFlags) = 0; + + virtual RCODE XFLMAPI getPosition( + IF_Db * pDb, + FLMUINT * puiPosition) = 0; + + virtual RCODE XFLMAPI buildResultSet( + IF_Db * pDb, + FLMUINT uiTimeLimit) = 0; + + virtual void XFLMAPI stopBuildingResultSet( void) = 0; + + virtual RCODE XFLMAPI getCounts( + IF_Db * pDb, + FLMUINT uiTimeLimit, + FLMBOOL bPartialCountOk, + FLMUINT * puiReadCount, + FLMUINT * puiPassedCount, + FLMUINT * puiPositionableToCount, + FLMBOOL * pbDoneBuildingResultSet = NULL) = 0; + + virtual void XFLMAPI enableResultSetEncryption( void) = 0; + + virtual void XFLMAPI setQueryStatusObject( + IF_QueryStatus * pQueryStatus) = 0; + + virtual void XFLMAPI setQueryValidatorObject( + IF_QueryValidator * pQueryValidator) = 0; + }; + + /**************************************************************************** + Desc: Pool memory allocator + ****************************************************************************/ + xflminterface IF_Pool : public XF_RefCount + { + virtual void poolInit( + FLMUINT uiBlockSize) = 0; + + virtual RCODE poolAlloc( + FLMUINT uiSize, + void ** ppvPtr) = 0; + + virtual RCODE poolCalloc( + FLMUINT uiSize, + void ** ppvPtr) = 0; + + virtual void poolFree( void) = 0; + + virtual void poolReset( + void * pvMark, + FLMBOOL bReduceFirstBlock = FALSE) = 0; + + virtual void * poolMark( void) = 0; + + virtual FLMUINT getBlockSize( void) = 0; + + virtual FLMUINT getBytesAllocated( void) = 0; + }; + + /**************************************************************************** + Desc: Dynamic buffer + ****************************************************************************/ + xflminterface IF_DynaBuf : public XF_RefCount + { + virtual void truncateData( + FLMUINT uiSize) = 0; + + virtual RCODE appendData( + const void * pvData, + FLMUINT uiSize) = 0; + + virtual RCODE allocSpace( + FLMUINT uiSize, + void ** ppvAlloc) = 0; + }; + + + typedef struct XFLM_NODE_INFO_ITEM + { + FLMUINT64 ui64Count; + FLMUINT64 ui64Bytes; + } XFLM_NODE_INFO_ITEM; + + typedef struct XFLM_NODE_INFO + { + XFLM_NODE_INFO_ITEM headerSize; + XFLM_NODE_INFO_ITEM nodeAndDataType; + XFLM_NODE_INFO_ITEM flags; + XFLM_NODE_INFO_ITEM nameId; + XFLM_NODE_INFO_ITEM prefixId; + XFLM_NODE_INFO_ITEM baseId; + XFLM_NODE_INFO_ITEM documentId; + XFLM_NODE_INFO_ITEM parentId; + XFLM_NODE_INFO_ITEM prevSibId; + XFLM_NODE_INFO_ITEM nextSibId; + XFLM_NODE_INFO_ITEM firstChildId; + XFLM_NODE_INFO_ITEM lastChildId; + XFLM_NODE_INFO_ITEM childElmCount; + XFLM_NODE_INFO_ITEM dataChildCount; + XFLM_NODE_INFO_ITEM attrCount; + XFLM_NODE_INFO_ITEM attrBaseId; + XFLM_NODE_INFO_ITEM attrFlags; + XFLM_NODE_INFO_ITEM attrPayloadLen; + XFLM_NODE_INFO_ITEM annotationId; + XFLM_NODE_INFO_ITEM metaValue; + XFLM_NODE_INFO_ITEM encDefId; + XFLM_NODE_INFO_ITEM unencDataLen; + XFLM_NODE_INFO_ITEM childElmNameId; + XFLM_NODE_INFO_ITEM childElmNodeId; + XFLM_NODE_INFO_ITEM encIV; + XFLM_NODE_INFO_ITEM encPadding; + + // Total overhead - sum of all of the above types of overhead. + + XFLM_NODE_INFO_ITEM totalOverhead; + + // Data totals + + XFLM_NODE_INFO_ITEM dataNodata; + XFLM_NODE_INFO_ITEM dataString; + XFLM_NODE_INFO_ITEM dataNumeric; + XFLM_NODE_INFO_ITEM dataBinary; + + // Summary - contains both overhead and data for each type of node + + XFLM_NODE_INFO_ITEM attributeNode; + XFLM_NODE_INFO_ITEM elementNode; + XFLM_NODE_INFO_ITEM dataNode; + XFLM_NODE_INFO_ITEM commentNode; + XFLM_NODE_INFO_ITEM otherNode; + } XFLM_NODE_INFO; + + /**************************************************************************** + Desc: Node Info. Gatherer + ****************************************************************************/ + xflminterface IF_NodeInfo : public XF_RefCount + { + virtual void XFLMAPI clearNodeInfo( void) = 0; + + virtual RCODE XFLMAPI addNodeInfo( + IF_Db * pDb, + IF_DOMNode * pNode, + FLMBOOL bDoSubTree, + FLMBOOL bDoSelf = TRUE) = 0; + + virtual FLMUINT64 XFLMAPI getTotalNodeCount( void) = 0; + + virtual void XFLMAPI getNodeInfo( + XFLM_NODE_INFO * pNodeInfo) = 0; + }; + + /**************************************************************************** + Desc: Types of information that can be gathered about a B-Tree. + ****************************************************************************/ + typedef struct XFLM_BTREE_LEVEL_INFO + { + FLMUINT64 ui64BlockCount; + FLMUINT64 ui64BlockLength; + FLMUINT64 ui64BlockFreeSpace; + FLMUINT64 ui64ElmOffsetOverhead; + FLMUINT64 ui64ElmCount; + FLMUINT64 ui64ContElmCount; + FLMUINT64 ui64ElmFlagOvhd; + FLMUINT64 ui64ElmKeyLengthOvhd; + FLMUINT64 ui64ElmCountsOvhd; + FLMUINT64 ui64ElmChildAddrsOvhd; + FLMUINT64 ui64ElmDataLenOvhd; + FLMUINT64 ui64ElmOADataLenOvhd; + FLMUINT64 ui64ElmKeyLength; + FLMUINT64 ui64ElmDataLength; + + // The following three are how ui64ElmKeyLength is subdivided. + // They are only applicable to index keys. + + FLMUINT64 ui64KeyDataSize; + FLMUINT64 ui64KeyIdSize; + FLMUINT64 ui64KeyComponentLengthsSize; + + // Data only blocks + + FLMUINT64 ui64DataOnlyBlockCount; + FLMUINT64 ui64DataOnlyBlockLength; + FLMUINT64 ui64DataOnlyBlockFreeSpace; + } XFLM_BTREE_LEVEL_INFO; + + /**************************************************************************** + Desc: + ****************************************************************************/ + xflminterface IF_BTreeInfoStatus : public XF_RefCount + { + virtual RCODE XFLMAPI infoStatus( + FLMUINT uiCurrLfNum, + FLMBOOL bIsCollection, + char * pszCurrLfName, + FLMUINT uiCurrLevel, + FLMUINT64 ui64CurrLfBlockCount, + FLMUINT64 ui64CurrLevelBlockCount, + FLMUINT64 ui64TotalBlockCount) = 0; + }; + + /**************************************************************************** + Desc: BTree Info. Gatherer + ****************************************************************************/ + xflminterface IF_BTreeInfo : public XF_RefCount + { + virtual void XFLMAPI clearBTreeInfo( void) = 0; + + virtual RCODE XFLMAPI collectIndexInfo( + IF_Db * pDb, + FLMUINT uiIndexNum, + IF_BTreeInfoStatus * pInfoStatus) = 0; + + virtual RCODE XFLMAPI collectCollectionInfo( + IF_Db * pDb, + FLMUINT uiCollectionNum, + IF_BTreeInfoStatus * pInfoStatus) = 0; + + virtual FLMUINT XFLMAPI getNumIndexes( void) = 0; + + virtual FLMUINT XFLMAPI getNumCollections( void) = 0; + + virtual FLMBOOL XFLMAPI getIndexInfo( + FLMUINT uiNthIndex, + FLMUINT * puiIndexNum, + char ** ppszIndexName, + FLMUINT * puiNumLevels) = 0; + + virtual FLMBOOL XFLMAPI getCollectionInfo( + FLMUINT uiNthCollection, + FLMUINT * puiCollectionNum, + char ** ppszCollectionName, + FLMUINT * puiNumLevels) = 0; + + virtual FLMBOOL XFLMAPI getIndexLevelInfo( + FLMUINT uiNthIndex, + FLMUINT uiBTreeLevel, + XFLM_BTREE_LEVEL_INFO * pLevelInfo) = 0; + + virtual FLMBOOL XFLMAPI getCollectionLevelInfo( + FLMUINT uiNthCollection, + FLMUINT uiBTreeLevel, + XFLM_BTREE_LEVEL_INFO * pLevelInfo) = 0; + }; + + /**************************************************************************** + 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 XFLM_ERROR_BASE(e) ((RCODE)((int)(0x81050000+(e)))) + + /**************************************************************************** + Desc: General FLAIM errors + ****************************************************************************/ + #define NE_XFLM_OK 0 + + #define NE_XFLM_FIRST_COMMON_ERROR XFLM_ERROR_BASE( 0x0000) // NOTE: This is not an error code - do not document it + #define NE_XFLM_NOT_IMPLEMENTED XFLM_ERROR_BASE( 0x0001) // NE_NOT_IMPLEMENTED - Attempt was made to use a feature that is not implemented. + #define NE_XFLM_MEM XFLM_ERROR_BASE( 0x0002) // NE_INSUFFICIENT_MEMORY - Attempt to allocate memory failed. + #define NE_XFLM_INVALID_PARM XFLM_ERROR_BASE( 0x0005) // NE_INVALID_PARAMETER - Invalid parameter passed into a function. + #define NE_XFLM_TIMEOUT XFLM_ERROR_BASE( 0x0009) // NE_WAIT_TIMEOUT - Database operation timed out (usually a query operation). + #define NE_XFLM_NOT_FOUND XFLM_ERROR_BASE( 0x000A) // NE_OBJECT_NOT_FOUND - An object was not found. + #define NE_XFLM_EXISTS XFLM_ERROR_BASE( 0x000C) // NE_OBJECT_ALREADY_EXISTS - Object already exists. + #define NE_XFLM_USER_ABORT XFLM_ERROR_BASE( 0x0010) // NE_CALLBACK_CANCELLED - User or application aborted (canceled) the operation + #define NE_XFLM_FAILURE XFLM_ERROR_BASE( 0x0011) // NE_RECOVERABLE_FAILURE - Internal failure. + #define NE_XFLM_LAST_COMMON_ERROR XFLM_ERROR_BASE( 0x0012) // NOTE: This is not an error code - do not document. + + #define NE_XFLM_FIRST_GENERAL_ERROR XFLM_ERROR_BASE( 0x0100) // NOTE: This is not an error code - do not document + #define NE_XFLM_BOF_HIT XFLM_ERROR_BASE( 0x0101) // Beginning of results encountered. This error is may be returned when reading query results in reverse order (from last to first). + #define NE_XFLM_EOF_HIT XFLM_ERROR_BASE( 0x0102) // End of results encountered. This error may be returned when reading query results in forward order (first to last). + #define NE_XFLM_END XFLM_ERROR_BASE( 0x0103) // End of roll-forward log packets encountered. NOTE: This error code should never be returned to an application. + #define NE_XFLM_BAD_PREFIX XFLM_ERROR_BASE( 0x0104) // Invalid XLM namespace prefix specified. Either a prefix name or number that was specified was not defined. + #define NE_XFLM_ATTRIBUTE_PURGED XFLM_ERROR_BASE( 0x0105) // XML attribute cannot be used - it is being deleted from the database. + #define NE_XFLM_BAD_COLLECTION XFLM_ERROR_BASE( 0x0106) // Invalid collection number specified. Collection is not defined. + #define NE_XFLM_DATABASE_LOCK_REQ_TIMEOUT XFLM_ERROR_BASE( 0x0107) // Request to lock the database timed out. + #define NE_XFLM_ILLEGAL_DATA_COMPONENT XFLM_ERROR_BASE( 0x0108) // Cannot use ELM_ROOT_TAG as a data component in an index. + #define NE_XFLM_BAD_DATA_TYPE XFLM_ERROR_BASE( 0x0109) // Attempt to set/get data on an XML element or attribute using a data type that is incompatible with the data type specified in the dictionary. + #define NE_XFLM_MUST_INDEX_ON_PRESENCE XFLM_ERROR_BASE( 0x010A) // When using ELM_ROOT_TAG in an index component, must specify PRESENCE indexing only. + #define NE_XFLM_BAD_IX XFLM_ERROR_BASE( 0x010B) // Invalid index number specified. Index is not defined. + #define NE_XFLM_BACKUP_ACTIVE XFLM_ERROR_BASE( 0x010C) // Operation could not be performed because a backup is currently in progress. + #define NE_XFLM_SERIAL_NUM_MISMATCH XFLM_ERROR_BASE( 0x010D) // Serial number on backup file does not match the serial number that is expected. + #define NE_XFLM_BAD_RFL_DB_SERIAL_NUM XFLM_ERROR_BASE( 0x010E) // Bad database serial number in roll-forward log file header. + #define NE_XFLM_BTREE_ERROR XFLM_ERROR_BASE( 0x010F) // A B-Tree in the database is bad. + #define NE_XFLM_BTREE_FULL XFLM_ERROR_BASE( 0x0110) // A B-tree in the database is full, or a b-tree being used for a temporary result set is full. + #define NE_XFLM_BAD_RFL_FILE_NUMBER XFLM_ERROR_BASE( 0x0111) // Bad roll-forward log file number in roll-forward log file header. + #define NE_XFLM_CANNOT_DEL_ELEMENT XFLM_ERROR_BASE( 0x0112) // Cannot delete an XML element definition in the dictionary because it is in use. + #define NE_XFLM_CANNOT_MOD_DATA_TYPE XFLM_ERROR_BASE( 0x0113) // Cannot modify the data type for an XML element or attribute definition in the dictionary. + #define NE_XFLM_CANNOT_INDEX_DATA_TYPE XFLM_ERROR_BASE( 0x0114) // Data type of XML element or attribute is not one that can be indexed. + #define NE_XFLM_CONV_BAD_DIGIT XFLM_ERROR_BASE( 0x0115) // Non-numeric digit found in text to numeric conversion. + #define NE_XFLM_CONV_DEST_OVERFLOW XFLM_ERROR_BASE( 0x0116) // Destination buffer not large enough to hold data. + #define NE_XFLM_CONV_ILLEGAL XFLM_ERROR_BASE( 0x0117) // Attempt to convert between data types is an unsupported conversion. + #define NE_XFLM_CONV_NULL_SRC XFLM_ERROR_BASE( 0x0118) // Data source cannot be NULL when doing data conversion. + #define NE_XFLM_CONV_NUM_OVERFLOW XFLM_ERROR_BASE( 0x0119) // Numeric overflow (> upper bound) converting to numeric type. + #define NE_XFLM_CONV_NUM_UNDERFLOW XFLM_ERROR_BASE( 0x011A) // Numeric underflow (< lower bound) converting to numeric type. + #define NE_XFLM_BAD_ELEMENT_NUM XFLM_ERROR_BASE( 0x011B) // Bad element number specified - element not defined in dictionary. + #define NE_XFLM_BAD_ATTRIBUTE_NUM XFLM_ERROR_BASE( 0x011C) // Bad attribute number specified - attribute not defined in dictionary. + #define NE_XFLM_BAD_ENCDEF_NUM XFLM_ERROR_BASE( 0x011D) // Bad encryption number specified - encryption definition not defined in dictionary. + #define NE_XFLM_DATA_ERROR XFLM_ERROR_BASE( 0x011E) // Encountered data in the database that was corrupted. + #define NE_XFLM_INVALID_FILE_SEQUENCE XFLM_ERROR_BASE( 0x011F) // Incremental backup file number provided during a restore is invalid. + #define NE_XFLM_ILLEGAL_OP XFLM_ERROR_BASE( 0x0120) // Attempt to perform an illegal operation. + #define NE_XFLM_DUPLICATE_ELEMENT_NUM XFLM_ERROR_BASE( 0x0121) // Element number specified in element definition is already in use. + #define NE_XFLM_ILLEGAL_TRANS_TYPE XFLM_ERROR_BASE( 0x0122) // Illegal transaction type specified for transaction begin operation. + #define NE_XFLM_UNSUPPORTED_VERSION XFLM_ERROR_BASE( 0x0123) // Version of database found in database header is not supported. + #define NE_XFLM_ILLEGAL_TRANS_OP XFLM_ERROR_BASE( 0x0124) // Illegal operation for transaction type. + #define NE_XFLM_INCOMPLETE_LOG XFLM_ERROR_BASE( 0x0125) // Incomplete rollback log. + #define NE_XFLM_ILLEGAL_INDEX_DEF XFLM_ERROR_BASE( 0x0126) // Index definition document is illegal - does not conform to the expected form of an index definition document. + #define NE_XFLM_ILLEGAL_INDEX_ON XFLM_ERROR_BASE( 0x0127) // The "IndexOn" attribute of an index definition has an illegal value. + #define NE_XFLM_ILLEGAL_STATE_CHANGE XFLM_ERROR_BASE( 0x0128) // Attempted an illegal state change on an element or attribute definition. + #define NE_XFLM_BAD_RFL_SERIAL_NUM XFLM_ERROR_BASE( 0x0129) // Serial number in roll-forward log file header does not match expected serial number. + #define NE_XFLM_NEWER_FLAIM XFLM_ERROR_BASE( 0x012A) // Running old code on a newer version of database. Newer code must be used. + #define NE_XFLM_CANNOT_MOD_ELEMENT_STATE XFLM_ERROR_BASE( 0x012B) // Attempted to change state of a predefined element definition. + #define NE_XFLM_CANNOT_MOD_ATTRIBUTE_STATE XFLM_ERROR_BASE( 0x012C) // Attempted to change state of a predefined attribute definition. + #define NE_XFLM_NO_MORE_ELEMENT_NUMS XFLM_ERROR_BASE( 0x012D) // The highest element number has already been used, cannot create more element definitions. + #define NE_XFLM_NO_TRANS_ACTIVE XFLM_ERROR_BASE( 0x012E) // Operation must be performed inside a database transaction. + #define NE_XFLM_NOT_UNIQUE XFLM_ERROR_BASE( 0x012F) // Attempt was made to insert a key into a b-tree that was already in the b-tree. + #define NE_XFLM_NOT_FLAIM XFLM_ERROR_BASE( 0x0130) // The file specified is not a FLAIM database. + #define NE_XFLM_OLD_VIEW XFLM_ERROR_BASE( 0x0131) // Unable to maintain read transaction's view of the database. + #define NE_XFLM_SHARED_LOCK XFLM_ERROR_BASE( 0x0132) // Attempted to perform an operation on the database that requires exclusive access, but cannot because there is a shared lock. + #define NE_XFLM_SYNTAX XFLM_ERROR_BASE( 0x0133) // Syntax error while parsing XML or query. + #define NE_XFLM_TRANS_ACTIVE XFLM_ERROR_BASE( 0x0134) // Operation cannot be performed while a transaction is active. + #define NE_XFLM_RFL_TRANS_GAP XFLM_ERROR_BASE( 0x0135) // A gap was found in the transaction sequence in the roll-forward log. + #define NE_XFLM_BAD_COLLATED_KEY XFLM_ERROR_BASE( 0x0136) // Something in collated key is bad. + #define NE_XFLM_UNSUPPORTED_FEATURE XFLM_ERROR_BASE( 0x0137) // Attempting to use a feature for which full support has been disabled. + #define NE_XFLM_MUST_DELETE_INDEXES XFLM_ERROR_BASE( 0x0138) // Attempting to delete a collection that has indexes defined for it. Associated indexes must be deleted before the collection can be deleted. + #define NE_XFLM_RFL_INCOMPLETE XFLM_ERROR_BASE( 0x0139) // Roll-forward log file is incomplete. + #define NE_XFLM_CANNOT_RESTORE_RFL_FILES XFLM_ERROR_BASE( 0x013A) // Cannot restore roll-forward log files - not using multiple roll-forward log files. + #define NE_XFLM_INCONSISTENT_BACKUP XFLM_ERROR_BASE( 0x013B) // A problem (corruption, etc.) was detected in a backup set. + #define NE_XFLM_BLOCK_CRC XFLM_ERROR_BASE( 0x013C) // CRC for database block was invalid. May indicate problems in reading from or writing to disk. + #define NE_XFLM_ABORT_TRANS XFLM_ERROR_BASE( 0x013D) // Attempted operation after a critical error - transaction should be aborted. + #define NE_XFLM_NOT_RFL XFLM_ERROR_BASE( 0x013E) // File was not a roll-forward log file as expected. + #define NE_XFLM_BAD_RFL_PACKET XFLM_ERROR_BASE( 0x013F) // Roll-forward log file packet was bad. + #define NE_XFLM_DATA_PATH_MISMATCH XFLM_ERROR_BASE( 0x0140) // Bad data path specified to open database. Does not match data path specified for prior opens of the database. + #define NE_XFLM_STREAM_EXISTS XFLM_ERROR_BASE( 0x0141) // Attempt to create stream, but the file(s) already exists. + #define NE_XFLM_FILE_EXISTS XFLM_ERROR_BASE( 0x0142) // Attempt to create a database, but the file already exists. + #define NE_XFLM_COULD_NOT_CREATE_SEMAPHORE XFLM_ERROR_BASE( 0x0143) // Could not create a semaphore. + #define NE_XFLM_MUST_CLOSE_DATABASE XFLM_ERROR_BASE( 0x0144) // Database must be closed due to a critical error. + #define NE_XFLM_INVALID_ENCKEY_CRC XFLM_ERROR_BASE( 0x0145) // Encryption key CRC could not be verified. + #define NE_XFLM_BAD_UTF8 XFLM_ERROR_BASE( 0x0146) // An invalid byte sequence was found in a UTF-8 string + #define NE_XFLM_COULD_NOT_CREATE_MUTEX XFLM_ERROR_BASE( 0x0147) // Could not create a mutex. + #define NE_XFLM_ERROR_WAITING_ON_SEMPAHORE XFLM_ERROR_BASE( 0x0148) // Error occurred while waiting on a sempahore. + #define NE_XFLM_BAD_PLATFORM_FORMAT XFLM_ERROR_BASE( 0x0149) // Cannot support platform format. NOTE: No need to document this one, it is strictly internal. + #define NE_XFLM_HDR_CRC XFLM_ERROR_BASE( 0x014A) // Database header has a bad CRC. + #define NE_XFLM_NO_NAME_TABLE XFLM_ERROR_BASE( 0x014B) // No name table was set up for the database. + #define NE_XFLM_MULTIPLE_MATCHES XFLM_ERROR_BASE( 0x014C) // Multiple entries match the name in the name table. Need to pass a namespace to disambiguate. + #define NE_XFLM_UNALLOWED_UPGRADE XFLM_ERROR_BASE( 0x014D) // Cannot upgrade database from one version to another. + #define NE_XFLM_BTREE_BAD_STATE XFLM_ERROR_BASE( 0x014E) // Btree function called before proper setup steps taken. + #define NE_XFLM_DUPLICATE_ATTRIBUTE_NUM XFLM_ERROR_BASE( 0x014F) // Attribute number specified in attribute definition is already in use. + #define NE_XFLM_DUPLICATE_INDEX_NUM XFLM_ERROR_BASE( 0x0150) // Index number specified in index definition is already in use. + #define NE_XFLM_DUPLICATE_COLLECTION_NUM XFLM_ERROR_BASE( 0x0151) // Collection number specified in collection definition is already in use. + #define NE_XFLM_DUPLICATE_ELEMENT_NAME XFLM_ERROR_BASE( 0x0152) // Element name+namespace specified in element definition is already in use. + #define NE_XFLM_DUPLICATE_ATTRIBUTE_NAME XFLM_ERROR_BASE( 0x0153) // Attribute name+namespace specified in attribute definition is already in use. + #define NE_XFLM_DUPLICATE_INDEX_NAME XFLM_ERROR_BASE( 0x0154) // Index name specified in index definition is already in use. + #define NE_XFLM_DUPLICATE_COLLECTION_NAME XFLM_ERROR_BASE( 0x0155) // Collection name specified in collection definition is already in use. + #define NE_XFLM_ELEMENT_PURGED XFLM_ERROR_BASE( 0x0156) // XML element cannot be used - it is deleted from the database. + #define NE_XFLM_TOO_MANY_OPEN_DATABASES XFLM_ERROR_BASE( 0x0157) // Too many open databases, cannot open another one. + #define NE_XFLM_DATABASE_OPEN XFLM_ERROR_BASE( 0x0158) // Operation cannot be performed because the database is currently open. + #define NE_XFLM_CACHE_ERROR XFLM_ERROR_BASE( 0x0159) // Cached database block has been compromised while in cache. + #define NE_XFLM_BTREE_KEY_SIZE XFLM_ERROR_BASE( 0x015A) // Key too large to insert/lookup in a b-tree. + #define NE_XFLM_DB_FULL XFLM_ERROR_BASE( 0x015B) // Database is full, cannot create more blocks. + #define NE_XFLM_QUERY_SYNTAX XFLM_ERROR_BASE( 0x015C) // Query expression had improper syntax. + #define NE_XFLM_COULD_NOT_START_THREAD XFLM_ERROR_BASE( 0x015D) // Error occurred while attempting to start a thread. + #define NE_XFLM_INDEX_OFFLINE XFLM_ERROR_BASE( 0x015E) // Index is offline, cannot be used in a query. + #define NE_XFLM_RFL_DISK_FULL XFLM_ERROR_BASE( 0x015F) // Disk which contains roll-forward log is full. + #define NE_XFLM_MUST_WAIT_CHECKPOINT XFLM_ERROR_BASE( 0x0160) // Must wait for a checkpoint before starting transaction - due to disk problems - usually in disk containing roll-forward log files. + #define NE_XFLM_MISSING_ENC_ALGORITHM XFLM_ERROR_BASE( 0x0161) // Encryption definition is missing an encryption algorithm. + #define NE_XFLM_INVALID_ENC_ALGORITHM XFLM_ERROR_BASE( 0x0162) // Invalid encryption algorithm specified in encryption definition. + #define NE_XFLM_INVALID_ENC_KEY_SIZE XFLM_ERROR_BASE( 0x0163) // Invalid key size specified in encryption definition. + #define NE_XFLM_ILLEGAL_DATA_TYPE XFLM_ERROR_BASE( 0x0164) // Data type specified for XML element or attribute definition is illegal. + #define NE_XFLM_ILLEGAL_STATE XFLM_ERROR_BASE( 0x0165) // State specified for index definition or XML element or attribute definition is illegal. + #define NE_XFLM_ILLEGAL_ELEMENT_NAME XFLM_ERROR_BASE( 0x0166) // XML element name specified in element definition is illegal. + #define NE_XFLM_ILLEGAL_ATTRIBUTE_NAME XFLM_ERROR_BASE( 0x0167) // XML attribute name specified in attribute definition is illegal. + #define NE_XFLM_ILLEGAL_COLLECTION_NAME XFLM_ERROR_BASE( 0x0168) // Collection name specified in collection definition is illegal. + #define NE_XFLM_ILLEGAL_INDEX_NAME XFLM_ERROR_BASE( 0x0169) // Index name specified is illegal + #define NE_XFLM_ILLEGAL_ELEMENT_NUMBER XFLM_ERROR_BASE( 0x016A) // Element number specified in element definition or index definition is illegal. + #define NE_XFLM_ILLEGAL_ATTRIBUTE_NUMBER XFLM_ERROR_BASE( 0x016B) // Attribute number specified in attribute definition or index definition is illegal. + #define NE_XFLM_ILLEGAL_COLLECTION_NUMBER XFLM_ERROR_BASE( 0x016C) // Collection number specified in collection definition or index definition is illegal. + #define NE_XFLM_ILLEGAL_INDEX_NUMBER XFLM_ERROR_BASE( 0x016D) // Index number specified in index definition is illegal. + #define NE_XFLM_ILLEGAL_ENCDEF_NUMBER XFLM_ERROR_BASE( 0x016E) // Encryption definition number specified in encryption definition is illegal. + #define NE_XFLM_COLLECTION_NAME_MISMATCH XFLM_ERROR_BASE( 0x016F) // Collection name and number specified in index definition do not correspond to each other. + #define NE_XFLM_ELEMENT_NAME_MISMATCH XFLM_ERROR_BASE( 0x0170) // Element name+namespace and number specified in index definition do not correspond to each other. + #define NE_XFLM_ATTRIBUTE_NAME_MISMATCH XFLM_ERROR_BASE( 0x0171) // Attribute name+namespace and number specified in index definition do not correspond to each other. + #define NE_XFLM_INVALID_COMPARE_RULE XFLM_ERROR_BASE( 0x0172) // Invalid comparison rule specified in index definition. + #define NE_XFLM_DUPLICATE_KEY_COMPONENT XFLM_ERROR_BASE( 0x0173) // Duplicate key component number specified in index definition. + #define NE_XFLM_DUPLICATE_DATA_COMPONENT XFLM_ERROR_BASE( 0x0174) // Duplicate data component number specified in index definition. + #define NE_XFLM_MISSING_KEY_COMPONENT XFLM_ERROR_BASE( 0x0175) // Index definition is missing a key component. + #define NE_XFLM_MISSING_DATA_COMPONENT XFLM_ERROR_BASE( 0x0176) // Index definition is missing a data component. + #define NE_XFLM_INVALID_INDEX_OPTION XFLM_ERROR_BASE( 0x0177) // Invalid index option specified on index definition. + #define NE_XFLM_NO_MORE_ATTRIBUTE_NUMS XFLM_ERROR_BASE( 0x0178) // The highest attribute number has already been used, cannot create more. + #define NE_XFLM_MISSING_ELEMENT_NAME XFLM_ERROR_BASE( 0x0179) // Missing element name in XML element definition. + #define NE_XFLM_MISSING_ATTRIBUTE_NAME XFLM_ERROR_BASE( 0x017A) // Missing attribute name in XML attribute definition. + #define NE_XFLM_MISSING_ELEMENT_NUMBER XFLM_ERROR_BASE( 0x017B) // Missing element number in XML element definition. + #define NE_XFLM_MISSING_ATTRIBUTE_NUMBER XFLM_ERROR_BASE( 0x017C) // Missing attribute number from XML attribute definition. + #define NE_XFLM_MISSING_INDEX_NAME XFLM_ERROR_BASE( 0x017D) // Missing index name in index definition. + #define NE_XFLM_MISSING_INDEX_NUMBER XFLM_ERROR_BASE( 0x017E) // Missing index number in index definition. + #define NE_XFLM_MISSING_COLLECTION_NAME XFLM_ERROR_BASE( 0x017F) // Missing collection name in collection definition. + #define NE_XFLM_MISSING_COLLECTION_NUMBER XFLM_ERROR_BASE( 0x0180) // Missing collection number in collection definition. + #define NE_XFLM_BAD_SEN XFLM_ERROR_BASE( 0x0181) // Invalid simple encoded number. + #define NE_XFLM_MISSING_ENCDEF_NAME XFLM_ERROR_BASE( 0x0182) // Missing encryption definition name in encryption definition. + #define NE_XFLM_MISSING_ENCDEF_NUMBER XFLM_ERROR_BASE( 0x0183) // Missing encryption definition number in encryption definition. + #define NE_XFLM_NO_MORE_INDEX_NUMS XFLM_ERROR_BASE( 0x0184) // The highest index number has already been used, cannot create more. + #define NE_XFLM_NO_MORE_COLLECTION_NUMS XFLM_ERROR_BASE( 0x0185) // The highest collection number has already been used, cannot create more. + #define NE_XFLM_CANNOT_DEL_ATTRIBUTE XFLM_ERROR_BASE( 0x0186) // Cannot delete an XML attribute definition because it is in use. + #define NE_XFLM_TOO_MANY_PENDING_NODES XFLM_ERROR_BASE( 0x0187) // Too many documents in the pending document list. + #define NE_XFLM_UNSUPPORTED_INTERFACE XFLM_ERROR_BASE( 0x0188) // Requested COM interface is not supported. + #define NE_XFLM_BAD_USE_OF_ELM_ROOT_TAG XFLM_ERROR_BASE( 0x0189) // ELM_ROOT_TAG, if used, must be the sole root component of an index definition. + #define NE_XFLM_DUP_SIBLING_IX_COMPONENTS XFLM_ERROR_BASE( 0x018A) // Sibling components in an index definition cannot have the same XML element or attribute number. + #define NE_XFLM_RFL_FILE_NOT_FOUND XFLM_ERROR_BASE( 0x018B) // Could not open a roll-forward log file - was not found in the roll-forward log directory. + #define NE_XFLM_BAD_RCODE_TABLE XFLM_ERROR_BASE( 0x018C) // The error code tables are incorrect. NOTE: This is an internal error that does not need to be documented. + #define NE_XFLM_ILLEGAL_KEY_COMPONENT_NUM XFLM_ERROR_BASE( 0x018D) // Key component of zero in index definition is not allowed. + #define NE_XFLM_ILLEGAL_DATA_COMPONENT_NUM XFLM_ERROR_BASE( 0x018E) // Data component of zero in index definition is not allowed. + #define NE_XFLM_CLASS_NOT_AVAILABLE XFLM_ERROR_BASE( 0x018F) // Requested COM class is not available. + #define NE_XFLM_BUFFER_OVERFLOW XFLM_ERROR_BASE( 0x0190) // Buffer overflow. + #define NE_XFLM_ILLEGAL_PREFIX_NUMBER XFLM_ERROR_BASE( 0x0191) // Prefix number specified in prefix definition is illegal. + #define NE_XFLM_MISSING_PREFIX_NAME XFLM_ERROR_BASE( 0x0192) // Missing prefix name in prefix definition. + #define NE_XFLM_MISSING_PREFIX_NUMBER XFLM_ERROR_BASE( 0x0193) // Missing prefix number in prefix definition. + #define NE_XFLM_UNDEFINED_ELEMENT_NAME XFLM_ERROR_BASE( 0x0194) // XML element name+namespace that was specified in index definition or XML document is not defined in dictionary. + #define NE_XFLM_UNDEFINED_ATTRIBUTE_NAME XFLM_ERROR_BASE( 0x0195) // XML attribute name+namespace that was specified in index definition or XML document is not defined in dictionary. + #define NE_XFLM_DUPLICATE_PREFIX_NAME XFLM_ERROR_BASE( 0x0196) // Prefix name specified in prefix definition is already in use. + #define NE_XFLM_KEY_OVERFLOW XFLM_ERROR_BASE( 0x0197) // Generated index key too large. + #define NE_XFLM_UNESCAPED_METACHAR XFLM_ERROR_BASE( 0x0198) // Unescaped metacharacter in regular expression. + #define NE_XFLM_ILLEGAL_QUANTIFIER XFLM_ERROR_BASE( 0x0199) // Illegal quantifier in regular expression. + #define NE_XFLM_UNEXPECTED_END_OF_EXPR XFLM_ERROR_BASE( 0x019A) // Unexpected end of regular expression. + #define NE_XFLM_ILLEGAL_MIN_COUNT XFLM_ERROR_BASE( 0x019B) // Illegal minimum count in regular expression quantifier. + #define NE_XFLM_ILLEGAL_MAX_COUNT XFLM_ERROR_BASE( 0x019C) // Illegal maximum count in regular expression quantifier. + #define NE_XFLM_EMPTY_BRANCH_IN_EXPR XFLM_ERROR_BASE( 0x019D) // Illegal empty branch in a regular expression. + #define NE_XFLM_ILLEGAL_RPAREN_IN_EXPR XFLM_ERROR_BASE( 0x019E) // Illegal right paren in a regular expression. + #define NE_XFLM_ILLEGAL_CLASS_SUBTRACTION XFLM_ERROR_BASE( 0x019F) // Illegal class subtraction in regular expression. + #define NE_XFLM_ILLEGAL_CHAR_RANGE_IN_EXPR XFLM_ERROR_BASE( 0x01A0) // Illegal character range in regular expression. + #define NE_XFLM_BAD_BASE64_ENCODING XFLM_ERROR_BASE( 0x01A1) // Illegal character(s) found in a base64 stream. + #define NE_XFLM_NAMESPACE_NOT_ALLOWED XFLM_ERROR_BASE( 0x01A2) // Cannot define a namespace for XML attributes whose name begins with "xmlns:" or that is equal to "xmlns" + #define NE_XFLM_INVALID_NAMESPACE_DECL XFLM_ERROR_BASE( 0x01A3) // Name for namespace declaration attribute must be "xmlns" or begin with "xmlns:" + #define NE_XFLM_ILLEGAL_NAMESPACE_DECL_DATATYPE XFLM_ERROR_BASE( 0x01A4) // Data type for XML attributes that are namespace declarations must be text. + #define NE_XFLM_UNEXPECTED_END_OF_INPUT XFLM_ERROR_BASE( 0x01A5) // Encountered unexpected end of input when parsing XPATH expression. + #define NE_XFLM_NO_MORE_PREFIX_NUMS XFLM_ERROR_BASE( 0x01A6) // The highest prefix number has already been used, cannot create more. + #define NE_XFLM_NO_MORE_ENCDEF_NUMS XFLM_ERROR_BASE( 0x01A7) // The highest encryption definition number has already been used, cannot create more. + #define NE_XFLM_COLLECTION_OFFLINE XFLM_ERROR_BASE( 0x01A8) // Collection is encrypted, cannot be accessed while in operating in limited mode. + #define NE_XFLM_INVALID_XML XFLM_ERROR_BASE( 0x01A9) // Invalid XML encountered while parsing document. + #define NE_XFLM_READ_ONLY XFLM_ERROR_BASE( 0x01AA) // Item is read-only and cannot be updated. + #define NE_XFLM_DELETE_NOT_ALLOWED XFLM_ERROR_BASE( 0x01AB) // Item cannot be deleted. + #define NE_XFLM_RESET_NEEDED XFLM_ERROR_BASE( 0x01AC) // Used during check operations to indicate we need to reset the view. NOTE: This is an internal error code and should not be documented. + #define NE_XFLM_ILLEGAL_REQUIRED_VALUE XFLM_ERROR_BASE( 0x01AD) // An illegal value was specified for the "Required" attribute in an index definition. + #define NE_XFLM_ILLEGAL_INDEX_COMPONENT XFLM_ERROR_BASE( 0x01AE) // A leaf index component in an index definition was not marked as a data component or key component. + #define NE_XFLM_ILLEGAL_UNIQUE_SUB_ELEMENT_VALUE XFLM_ERROR_BASE( 0x01AF) // Illegal value for the "UniqueSubElements" attribute in an element definition. + #define NE_XFLM_DATA_TYPE_MUST_BE_NO_DATA XFLM_ERROR_BASE( 0x01B0) // Data type for an element definition with UniqueSubElements="yes" must be nodata. + #define NE_XFLM_ILLEGAL_FLAG XFLM_ERROR_BASE( 0x01B1) // Illegal flag passed to getChildElement method. Must be zero for elements that can have non-unique child elements. + #define NE_XFLM_CANNOT_SET_REQUIRED XFLM_ERROR_BASE( 0x01B2) // Cannot set the "Required" attribute on a non-key index component in index definition. + #define NE_XFLM_CANNOT_SET_LIMIT XFLM_ERROR_BASE( 0x01B3) // Cannot set the "Limit" attribute on a non-key index component in index definition. + #define NE_XFLM_CANNOT_SET_INDEX_ON XFLM_ERROR_BASE( 0x01B4) // Cannot set the "IndexOn" attribute on a non-key index component in index definition. + #define NE_XFLM_CANNOT_SET_COMPARE_RULES XFLM_ERROR_BASE( 0x01B5) // Cannot set the "CompareRules" on a non-key index component in index definition. + #define NE_XFLM_INPUT_PENDING XFLM_ERROR_BASE( 0x01B6) // Attempt to set a value while an input stream is still open. + #define NE_XFLM_INVALID_NODE_TYPE XFLM_ERROR_BASE( 0x01B7) // Bad node type + #define NE_XFLM_INVALID_CHILD_ELM_NODE_ID XFLM_ERROR_BASE( 0x01B8) // Attempt to insert a unique child element that has a lower node ID than the parent element + #define NE_XFLM_LAST_GENERAL_ERROR XFLM_ERROR_BASE( 0x01B9) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: DOM Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_DOM_ERROR XFLM_ERROR_BASE( 0x1100) // NOTE: This is not an error code - do not document + #define NE_XFLM_DOM_HIERARCHY_REQUEST_ERR XFLM_ERROR_BASE( 0x1101) // Attempt to insert a DOM node somewhere it doesn't belong. + #define NE_XFLM_DOM_WRONG_DOCUMENT_ERR XFLM_ERROR_BASE( 0x1102) // A DOM node is being used in a different document than the one that created it. + #define NE_XFLM_DOM_DATA_ERROR XFLM_ERROR_BASE( 0x1103) // Links between DOM nodes in a document are corrupt. + #define NE_XFLM_DOM_NODE_NOT_FOUND XFLM_ERROR_BASE( 0x1104) // The requested DOM node does not exist. + #define NE_XFLM_DOM_INVALID_CHILD_TYPE XFLM_ERROR_BASE( 0x1105) // Attempting to insert a child DOM node whose type cannot be inserted as a child node. + #define NE_XFLM_DOM_NODE_DELETED XFLM_ERROR_BASE( 0x1106) // DOM node being accessed has been deleted. + #define NE_XFLM_DOM_DUPLICATE_ELEMENT XFLM_ERROR_BASE( 0x1107) // Node already has a child element with the given name id - this node's child nodes must all be unique. + #define NE_XFLM_LAST_DOM_ERROR XFLM_ERROR_BASE( 0x1108) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: I/O Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_IO_ERROR XFLM_ERROR_BASE( 0x2100) // NOTE: This is not an error code - do not document + #define NE_XFLM_IO_ACCESS_DENIED XFLM_ERROR_BASE( 0x2101) // Access to file is denied. Caller is not allowed access to a file. + #define NE_XFLM_IO_BAD_FILE_HANDLE XFLM_ERROR_BASE( 0x2102) // Bad file handle or file descriptor. + #define NE_XFLM_IO_COPY_ERR XFLM_ERROR_BASE( 0x2103) // Error occurred while copying a file. + #define NE_XFLM_IO_DISK_FULL XFLM_ERROR_BASE( 0x2104) // Disk full. + #define NE_XFLM_IO_END_OF_FILE XFLM_ERROR_BASE( 0x2105) // End of file reached while reading from the file. + #define NE_XFLM_IO_OPEN_ERR XFLM_ERROR_BASE( 0x2106) // Error while opening the file. + #define NE_XFLM_IO_SEEK_ERR XFLM_ERROR_BASE( 0x2107) // Error occurred while positioning (seeking) within a file. + #define NE_XFLM_IO_DIRECTORY_ERR XFLM_ERROR_BASE( 0x2108) // Error occurred while accessing or deleting a directory. + #define NE_XFLM_IO_PATH_NOT_FOUND XFLM_ERROR_BASE( 0x2109) // File not found. + #define NE_XFLM_IO_TOO_MANY_OPEN_FILES XFLM_ERROR_BASE( 0x210A) // Too many files open. + #define NE_XFLM_IO_PATH_TOO_LONG XFLM_ERROR_BASE( 0x210B) // File name too long. + #define NE_XFLM_IO_NO_MORE_FILES XFLM_ERROR_BASE( 0x210C) // No more files in directory. + #define NE_XFLM_IO_DELETING_FILE XFLM_ERROR_BASE( 0x210D) // Error occurred while deleting a file. + #define NE_XFLM_IO_FILE_LOCK_ERR XFLM_ERROR_BASE( 0x210E) // Error attempting to acquire a byte-range lock on a file. + #define NE_XFLM_IO_FILE_UNLOCK_ERR XFLM_ERROR_BASE( 0x210F) // Error attempting to release a byte-range lock on a file. + #define NE_XFLM_IO_PATH_CREATE_FAILURE XFLM_ERROR_BASE( 0x2110) // Error occurred while attempting to create a directory or sub-directory. + #define NE_XFLM_IO_RENAME_FAILURE XFLM_ERROR_BASE( 0x2111) // Error occurred while renaming a file. + #define NE_XFLM_IO_INVALID_PASSWORD XFLM_ERROR_BASE( 0x2112) // Invalid file password. + #define NE_XFLM_SETTING_UP_FOR_READ XFLM_ERROR_BASE( 0x2113) // Error occurred while setting up to perform a file read operation. + #define NE_XFLM_SETTING_UP_FOR_WRITE XFLM_ERROR_BASE( 0x2114) // Error occurred while setting up to perform a file write operation. + #define NE_XFLM_IO_CANNOT_REDUCE_PATH XFLM_ERROR_BASE( 0x2115) // Cannot reduce file name into more components. + #define NE_XFLM_INITIALIZING_IO_SYSTEM XFLM_ERROR_BASE( 0x2116) // Error occurred while setting up to access the file system. + #define NE_XFLM_FLUSHING_FILE XFLM_ERROR_BASE( 0x2117) // Error occurred while flushing file data buffers to disk. + #define NE_XFLM_IO_INVALID_FILENAME XFLM_ERROR_BASE( 0x2118) // Invalid file name. + #define NE_XFLM_IO_CONNECT_ERROR XFLM_ERROR_BASE( 0x2119) // Error connecting to a remote network resource. + #define NE_XFLM_OPENING_FILE XFLM_ERROR_BASE( 0x211A) // Unexpected error occurred while opening a file. + #define NE_XFLM_DIRECT_OPENING_FILE XFLM_ERROR_BASE( 0x211B) // Unexpected error occurred while opening a file in direct access mode. + #define NE_XFLM_CREATING_FILE XFLM_ERROR_BASE( 0x211C) // Unexpected error occurred while creating a file. + #define NE_XFLM_DIRECT_CREATING_FILE XFLM_ERROR_BASE( 0x211D) // Unexpected error occurred while creating a file in direct access mode. + #define NE_XFLM_READING_FILE XFLM_ERROR_BASE( 0x211E) // Unexpected error occurred while reading a file. + #define NE_XFLM_DIRECT_READING_FILE XFLM_ERROR_BASE( 0x211F) // Unexpected error occurred while reading a file in direct access mode. + #define NE_XFLM_WRITING_FILE XFLM_ERROR_BASE( 0x2120) // Unexpected error occurred while writing to a file. + #define NE_XFLM_DIRECT_WRITING_FILE XFLM_ERROR_BASE( 0x2121) // Unexpected error occurred while writing a file in direct access mode. + #define NE_XFLM_POSITIONING_IN_FILE XFLM_ERROR_BASE( 0x2122) // Unexpected error occurred while positioning within a file. + #define NE_XFLM_GETTING_FILE_SIZE XFLM_ERROR_BASE( 0x2123) // Unexpected error occurred while getting a file's size. + #define NE_XFLM_TRUNCATING_FILE XFLM_ERROR_BASE( 0x2124) // Unexpected error occurred while truncating a file. + #define NE_XFLM_PARSING_FILE_NAME XFLM_ERROR_BASE( 0x2125) // Unexpected error occurred while parsing a file's name. + #define NE_XFLM_CLOSING_FILE XFLM_ERROR_BASE( 0x2126) // Unexpected error occurred while closing a file. + #define NE_XFLM_GETTING_FILE_INFO XFLM_ERROR_BASE( 0x2127) // Unexpected error occurred while getting information about a file. + #define NE_XFLM_EXPANDING_FILE XFLM_ERROR_BASE( 0x2128) // Unexpected error occurred while expanding a file. + #define NE_XFLM_CHECKING_FILE_EXISTENCE XFLM_ERROR_BASE( 0x2129) // Unexpected error occurred while checking to see if a file exists. + #define NE_XFLM_RENAMING_FILE XFLM_ERROR_BASE( 0x212A) // Unexpected error occurred while renaming a file. + #define NE_XFLM_SETTING_FILE_INFO XFLM_ERROR_BASE( 0x212B) // Unexpected error occurred while setting a file's information. + #define NE_XFLM_LAST_IO_ERROR XFLM_ERROR_BASE( 0x212C) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: Network Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_NET_ERROR XFLM_ERROR_BASE( 0x3100) // NOTE: This is not an error code - do not document + #define NE_XFLM_SVR_NOIP_ADDR XFLM_ERROR_BASE( 0x3101) // IP address not found + #define NE_XFLM_SVR_SOCK_FAIL XFLM_ERROR_BASE( 0x3102) // IP socket failure + #define NE_XFLM_SVR_CONNECT_FAIL XFLM_ERROR_BASE( 0x3103) // TCP/IP connection failure + #define NE_XFLM_SVR_BIND_FAIL XFLM_ERROR_BASE( 0x3104) // The TCP/IP services on your system may not be configured or installed. If this POA is not to run Client/Server, use the /notcpip startup switch or disable TCP/IP through the NWADMIN snapin + #define NE_XFLM_SVR_LISTEN_FAIL XFLM_ERROR_BASE( 0x3105) // TCP/IP listen failed + #define NE_XFLM_SVR_ACCEPT_FAIL XFLM_ERROR_BASE( 0x3106) // TCP/IP accept failed + #define NE_XFLM_SVR_SELECT_ERR XFLM_ERROR_BASE( 0x3107) // TCP/IP select failed + #define NE_XFLM_SVR_SOCKOPT_FAIL XFLM_ERROR_BASE( 0x3108) // TCP/IP socket operation failed + #define NE_XFLM_SVR_DISCONNECT XFLM_ERROR_BASE( 0x3109) // TCP/IP disconnected + #define NE_XFLM_SVR_READ_FAIL XFLM_ERROR_BASE( 0x310A) // TCP/IP read failed + #define NE_XFLM_SVR_WRT_FAIL XFLM_ERROR_BASE( 0x310B) // TCP/IP write failed + #define NE_XFLM_SVR_READ_TIMEOUT XFLM_ERROR_BASE( 0x310C) // TCP/IP read timeout + #define NE_XFLM_SVR_WRT_TIMEOUT XFLM_ERROR_BASE( 0x310D) // TCP/IP write timeout + #define NE_XFLM_SVR_ALREADY_CLOSED XFLM_ERROR_BASE( 0x310E) // Connection already closed + #define NE_XFLM_LAST_NET_ERROR XFLM_ERROR_BASE( 0x310F) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: Query Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_QUERY_ERROR XFLM_ERROR_BASE( 0x4100) // NOTE: This is not an error code - do not document + #define NE_XFLM_Q_UNMATCHED_RPAREN XFLM_ERROR_BASE( 0x4101) // Query setup error: Unmatched right paren. + #define NE_XFLM_Q_UNEXPECTED_LPAREN XFLM_ERROR_BASE( 0x4102) // Query setup error: Unexpected left paren. + #define NE_XFLM_Q_UNEXPECTED_RPAREN XFLM_ERROR_BASE( 0x4103) // Query setup error: Unexpected right paren. + #define NE_XFLM_Q_EXPECTING_OPERAND XFLM_ERROR_BASE( 0x4104) // Query setup error: Expecting an operand. + #define NE_XFLM_Q_EXPECTING_OPERATOR XFLM_ERROR_BASE( 0x4105) // Query setup error: Expecting an operator. + #define NE_XFLM_Q_UNEXPECTED_COMMA XFLM_ERROR_BASE( 0x4106) // Query setup error: Unexpected comma. + #define NE_XFLM_Q_EXPECTING_LPAREN XFLM_ERROR_BASE( 0x4107) // Query setup error: Expecting a left paren. + #define NE_XFLM_Q_UNEXPECTED_VALUE XFLM_ERROR_BASE( 0x4108) // Query setup error: Unexpected value. + #define NE_XFLM_Q_INVALID_NUM_FUNC_ARGS XFLM_ERROR_BASE( 0x4109) // Query setup error: Invalid number of arguments for a function. + #define NE_XFLM_Q_UNEXPECTED_XPATH_COMPONENT XFLM_ERROR_BASE( 0x410A) // Query setup error: Unexpected XPATH componenent. + #define NE_XFLM_Q_ILLEGAL_LBRACKET XFLM_ERROR_BASE( 0x410B) // Query setup error: Illegal left bracket ([). + #define NE_XFLM_Q_ILLEGAL_RBRACKET XFLM_ERROR_BASE( 0x410C) // Query setup error: Illegal right bracket (]). + #define NE_XFLM_Q_ILLEGAL_OPERAND XFLM_ERROR_BASE( 0x410D) // Query setup error: Operand for some operator is not valid for that operator type. + #define NE_XFLM_Q_ALREADY_OPTIMIZED XFLM_ERROR_BASE( 0x410E) // Operation is illegal, cannot change certain things after query has been optimized. + #define NE_XFLM_Q_MISMATCHED_DB XFLM_ERROR_BASE( 0x410F) // Database handle passed in does not match database associated with query. + #define NE_XFLM_Q_ILLEGAL_OPERATOR XFLM_ERROR_BASE( 0x4110) // Illegal operator - cannot pass this operator into the addOperator method. + #define NE_XFLM_Q_ILLEGAL_COMPARE_RULES XFLM_ERROR_BASE( 0x4111) // Illegal combination of comparison rules passed to addOperator method. + #define NE_XFLM_Q_INCOMPLETE_QUERY_EXPR XFLM_ERROR_BASE( 0x4112) // Query setup error: Query expression is incomplete. + #define NE_XFLM_Q_NOT_POSITIONED XFLM_ERROR_BASE( 0x4113) // Query not positioned due to previous error, cannot call getNext, getPrev, or getCurrent + #define NE_XFLM_Q_INVALID_NODE_ID_VALUE XFLM_ERROR_BASE( 0x4114) // Query setup error: Invalid type of value constant used for node id value comparison. + #define NE_XFLM_Q_INVALID_META_DATA_TYPE XFLM_ERROR_BASE( 0x4115) // Query setup error: Invalid meta data type specified. + #define NE_XFLM_Q_NEW_EXPR_NOT_ALLOWED XFLM_ERROR_BASE( 0x4116) // Query setup error: Cannot add an expression to an XPATH component after having added an expression that tests context position. + #define NE_XFLM_Q_INVALID_CONTEXT_POS XFLM_ERROR_BASE( 0x4117) // Invalid context position value encountered - must be a positive number. + #define NE_XFLM_Q_INVALID_FUNC_ARG XFLM_ERROR_BASE( 0x4118) // Query setup error: Parameter to user-defined functions must be a single XPATH only. + #define NE_XFLM_Q_EXPECTING_RPAREN XFLM_ERROR_BASE( 0x4119) // Query setup error: Expecting right paren. + #define NE_XFLM_Q_TOO_LATE_TO_ADD_SORT_KEYS XFLM_ERROR_BASE( 0x411A) // Query setup error: Cannot add sort keys after having called getFirst, getLast, getNext, or getPrev. + #define NE_XFLM_Q_INVALID_SORT_KEY_COMPONENT XFLM_ERROR_BASE( 0x411B) // Query setup error: Invalid sort key component number specified in query. + #define NE_XFLM_Q_DUPLICATE_SORT_KEY_COMPONENT XFLM_ERROR_BASE( 0x411C) // Query setup error: Duplicate sort key component number specified in query. + #define NE_XFLM_Q_MISSING_SORT_KEY_COMPONENT XFLM_ERROR_BASE( 0x411D) // Query setup error: Missing sort key component number in sort keys that were specified for query. + #define NE_XFLM_Q_NO_SORT_KEY_COMPONENTS_SPECIFIED XFLM_ERROR_BASE( 0x411E) // Query setup error: addSortKeys was called, but no sort key components were specified. + #define NE_XFLM_Q_SORT_KEY_CONTEXT_MUST_BE_ELEMENT XFLM_ERROR_BASE( 0x411F) // Query setup error: A sort key context cannot be an XML attribute. + #define NE_XFLM_Q_INVALID_ELEMENT_NUM_IN_SORT_KEYS XFLM_ERROR_BASE( 0x4120) // Query setup error: The XML element number specified for a sort key in a query is invalid - no element definition in the dictionary. + #define NE_XFLM_Q_INVALID_ATTR_NUM_IN_SORT_KEYS XFLM_ERROR_BASE( 0x4121) // Query setup error: The XML attribute number specified for a sort key in a query is invalid - no attribute definition in the dictionary. + #define NE_XFLM_Q_NON_POSITIONABLE_QUERY XFLM_ERROR_BASE( 0x4122) // Attempt is being made to position in a query that is not positionable. + #define NE_XFLM_Q_INVALID_POSITION XFLM_ERROR_BASE( 0x4123) // Attempt is being made to position to an invalid position in the result set. + #define NE_XFLM_LAST_QUERY_ERROR XFLM_ERROR_BASE( 0x4124) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: Stream Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_STREAM_ERROR XFLM_ERROR_BASE( 0x6100) // NOTE: This is not an error code - do not document + #define NE_XFLM_STREAM_DECOMPRESS_ERROR XFLM_ERROR_BASE( 0x6101) // Error decompressing data stream. + #define NE_XFLM_STREAM_NOT_COMPRESSED XFLM_ERROR_BASE( 0x6102) // Attempting to decompress a data stream that is not compressed. + #define NE_XFLM_STREAM_TOO_MANY_FILES XFLM_ERROR_BASE( 0x6103) // Too many files in input stream. + #define NE_XFLM_LAST_STREAM_ERROR XFLM_ERROR_BASE( 0x6104) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Desc: NICI / Encryption Errors + ****************************************************************************/ + + #define NE_XFLM_FIRST_NICI_ERROR XFLM_ERROR_BASE( 0x7100) // NOTE: This is not an error code - do not document + #define NE_XFLM_NICI_CONTEXT XFLM_ERROR_BASE( 0x7101) // Error occurred while creating NICI context for encryption/decryption. + #define NE_XFLM_NICI_ATTRIBUTE_VALUE XFLM_ERROR_BASE( 0x7102) // Error occurred while accessing an attribute on a NICI encryption key. + #define NE_XFLM_NICI_BAD_ATTRIBUTE XFLM_ERROR_BASE( 0x7103) // Value retrieved from an attribute on a NICI encryption key was bad. + #define NE_XFLM_NICI_WRAPKEY_FAILED XFLM_ERROR_BASE( 0x7104) // Error occurred while wrapping a NICI encryption key in another NICI encryption key. + #define NE_XFLM_NICI_UNWRAPKEY_FAILED XFLM_ERROR_BASE( 0x7105) // Error occurred while unwrapping a NICI encryption key that is wrapped in another NICI encryption key. + #define NE_XFLM_NICI_INVALID_ALGORITHM XFLM_ERROR_BASE( 0x7106) // Attempt to use invalid NICI encryption algorithm. + #define NE_XFLM_NICI_GENKEY_FAILED XFLM_ERROR_BASE( 0x7107) // Error occurred while attempting to generate a NICI encryption key. + #define NE_XFLM_NICI_BAD_RANDOM XFLM_ERROR_BASE( 0x7108) // Error occurred while generating random data using NICI. + #define NE_XFLM_PBE_ENCRYPT_FAILED XFLM_ERROR_BASE( 0x7109) // Error occurred while attempting to wrap a NICI encryption key in a password. + #define NE_XFLM_PBE_DECRYPT_FAILED XFLM_ERROR_BASE( 0x710A) // Error occurred while attempting to unwrap a NICI encryption key that was previously wrapped in a password. + #define NE_XFLM_DIGEST_INIT_FAILED XFLM_ERROR_BASE( 0x710B) // Error occurred while attempting to initialize the NICI digest functionality. + #define NE_XFLM_DIGEST_FAILED XFLM_ERROR_BASE( 0x710C) // Error occurred while attempting to create a NICI digest. + #define NE_XFLM_INJECT_KEY_FAILED XFLM_ERROR_BASE( 0x710D) // Error occurred while attempting to inject an encryption key into NICI. + #define NE_XFLM_NICI_FIND_INIT XFLM_ERROR_BASE( 0x710E) // Error occurred while attempting to initialize NICI to find information on a NICI encryption key. + #define NE_XFLM_NICI_FIND_OBJECT XFLM_ERROR_BASE( 0x710F) // Error occurred while attempting to find information on a NICI encryption key. + #define NE_XFLM_NICI_KEY_NOT_FOUND XFLM_ERROR_BASE( 0x7110) // Could not find the NICI encryption key or information on the NICI encryption key. + #define NE_XFLM_NICI_ENC_INIT_FAILED XFLM_ERROR_BASE( 0x7111) // Error occurred while initializing NICI to encrypt data. + #define NE_XFLM_NICI_ENCRYPT_FAILED XFLM_ERROR_BASE( 0x7112) // Error occurred while encrypting data. + #define NE_XFLM_NICI_DECRYPT_INIT_FAILED XFLM_ERROR_BASE( 0x7113) // Error occurred while initializing NICI to decrypt data. + #define NE_XFLM_NICI_DECRYPT_FAILED XFLM_ERROR_BASE( 0x7114) // Error occurred while decrypting data. + #define NE_XFLM_NICI_WRAPKEY_NOT_FOUND XFLM_ERROR_BASE( 0x7115) // Could not find the NICI encryption key used to wrap another NICI encryption key. + #define NE_XFLM_NOT_EXPECTING_PASSWORD XFLM_ERROR_BASE( 0x7116) // Password supplied when none was expected. + #define NE_XFLM_EXPECTING_PASSWORD XFLM_ERROR_BASE( 0x7117) // No password supplied when one was required. + #define NE_XFLM_EXTRACT_KEY_FAILED XFLM_ERROR_BASE( 0x7118) // Error occurred while attempting to extract a NICI encryption key. + #define NE_XFLM_NICI_INIT_FAILED XFLM_ERROR_BASE( 0x7119) // Error occurred while initializing NICI. + #define NE_XFLM_BAD_ENCKEY_SIZE XFLM_ERROR_BASE( 0x711A) // Bad encryption key size found in roll-forward log packet. + #define NE_XFLM_ENCRYPTION_UNAVAILABLE XFLM_ERROR_BASE( 0x711B) // Attempt was made to encrypt data when NICI is unavailable. + #define NE_XFLM_LAST_NICI_ERROR XFLM_ERROR_BASE( 0x711C) // NOTE: This is not an error code - do not document + + /**************************************************************************** + Dictionary Document Definitions - below are comments that document valid + dictionary objects and their structure. + ****************************************************************************/ + + /* + Element Definition + Desc: The XML syntax given below is used to define an element in the + dictionary collection. + + + + Notes: + + 1) If the xflaim:type attribute is missing, any type of data may be stored on the attribute. + 2) If the xflaim:State attribute is missing, the attribute's state is, by default, active. + + */ + + /* + Attribute Definition + Desc: The XML syntax given below is used to define an attribute in the + dictionary collection. + + + + Notes: + + 1) If the xflaim:type attribute is missing, any type of data may be stored on the attribute. + 2) If the xflaim:State attribute is missing, the attribute's state is, by default, active. + + */ + + /* + Collection Definition + Desc: The XML syntax given below is used to define a collection in the + dictionary collection. + + + */ + + /* + Prefix Definition + Desc: The XML syntax given below is used to define a namespace prefix in the + dictionary collection. + + + */ + + /* + Index Definition + Desc: The XML syntax given below is used to define an index in the + dictionary collection. + + + + ... + + NOTE: The IndexOn, Required, Limit, and CompareRules attributes can only be set for + key components - i.e., when the KeyComponent attribute is also specified. + + xflaim:ElementComponent elements may have one or more xflaim:ElementComponent + or xflaim:AttributeComponent sub-elements. + + ... + + NOTE: The IndexOn, Required, Limit, and CompareRules attributes can only be set for + key components - i.e., when the KeyComponent attribute is also specified. + + xflaim:AttributeComponent elements must be subordinate to a xflaim:ElementComponent + element. They may not have any elements subordinate to them. + + + + Notes: + + 1) It is not valid to specify both an xflaim:CollectionName and an xflaim:CollectionNumber + unless they refer to the same collection. Only one of the two is needed. If both + are omitted, the collection that will be indexed is the default data collection. + 2) Valid values for the IndexOptions attribute are any combination of the following: + a) abspos - enables storing of absolute positioning information + 3) For the xflaim:ElementComponent and xflaim:AttributeComponent, it is not + valid to have both an xflaim:DictNumber and an xflaim:name attribute, unless they both specify the + same element or attribute. One or the other must be specified. + 4) If the xflaim:type attribute is specified on the xflaim:ElementComponent or xflaim:AttributeComponent + element, it indicates what type the component is to be coerced to for indexing purposes. + This is only relevant if the xflaim:IndexOn attribute is set to "value". For example, if a + type of integer is specified, then the value will be coerced to an integer before putting it + into the index. If the value were a string of "123" it would be coerced to an integer value of + 123 for indexing purposes. If the xflaim:type attribute is omitted, then the element or attribute + specified by the component must have a type specified in the element or attribute definition, + and that is the type that will be used. + 5) If the xflaim:Required attribute is missing from the xflaim:ElementComponent or xflaim:AttributeComponent + element, or does not have a value of "yes", "on", "1", or "true", the index component is assumed to + be optional. + 6) The xflaim:CompareRules attribute specifies special comparison rules for the index component if the + element or attribute is of type xflaim:string. Rules may be any combination of the following key words: + + a) caseinsensitive - don't compare case + b) whitespaceasspace - treat whitespace as space (must be applied before minspaces, + ignoreleadingspaces, ignoretrailingspaces, or nospaces) + c) minspaces - compress out extra spaces + d) ignoreleadingspaces - remove leading spaces + e) ignoretrailingspaces - remove trailing spaces + f) nospaces - remove all whitespace + g) nounderscore - change all underscores to spaces (must be applied before minspaces, + ignoreleadingspaces, ignoretrailingspaces, or nospaces) + h) nodashes - remove all dashes. + i) sortdescending - sort in descending order. + j) sortmissinghigh - sort missing values high. + + Note that if the xflaim:CompareRules attribute is omitted, the default comparison rule for xflaim:string + values is case sensitive. Note also that the xflaim:CompareRules attribute is ignored if the + xflaim:IndexOn attribute is set to "presence". In that case, comparison rules are irrelevant. + */ + + + /* + EncDef Definition + Desc: The XML syntax given below is used to define an encryption definition in the + dictionary collection. + + + + Notes: + + 1) Only AES and DES3 (Triple DES) encryption algorithms are supported + + 2) AES supports three key lengths: 256, 192 and 128 bits in length. + + 3) DES3 keys are 168 bits in length. + + 4. The xflaim:keySize attribute is optional. If it is not specified, the maximum + key size allowed for the chosen algorithm will be selected. For AES, the key size + chosen will depend on what is supported by the NICI installation. + + */ + + +#endif // XFLAIM_H diff --git a/version5/util/basictestsrv.cpp b/version5/util/basictestsrv.cpp new file mode 100644 index 0000000..3a13d94 --- /dev/null +++ b/version5/util/basictestsrv.cpp @@ -0,0 +1,994 @@ +//------------------------------------------------------------------------------ +// Desc: This is the main implementation of BasicTest component of our unit +// test suite. It handles basic operations such as: creating a db, +// deleting a db, backup and restore and add and remove nodes. +// +// 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: basictestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\TST.DB" + #define BACKUP_NAME_STR "SYS:\\TST.BAK" + #define NEW_NAME_STR "SYS:\\NEW.DB" +#else + #define DB_NAME_STR "tst.db" + #define BACKUP_NAME_STR "tst.bak" + #define NEW_NAME_STR "new.db" +#endif + +#define BACKUP_PASSWORD_STR "password" + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestImpl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IFlmTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IFlmTestImpl::getName() +{ + return( "Basic Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestImpl::execute() +{ + RCODE rc = NE_XFLM_OK; + char szMsgBuf[ 64]; + FLMUINT uiTmp; + FLMUINT64 ui64Tmp; + FLMUINT uiLoop; + FLMUINT uiHighNode; + FLMUINT uiDefNum; + FLMUINT uiDefNum1; + FLMUINT uiDefNum2; + FLMUINT uiDefNum3; + FLMUINT uiLargeTextSize; + IF_Backup * pBackup = NULL; + IF_DOMNode * pDoc = NULL; + IF_DOMNode * pRootElement = NULL; + IF_DOMNode * pUniqueElement = NULL; + IF_DOMNode * pNonUniqueElement = NULL; + IF_DOMNode * pElement = NULL; + IF_DOMNode * pData = NULL; + IF_DOMNode * pAttr = NULL; + IF_DOMNode * pTmpNode = NULL; + FLMBYTE * pucLargeBuf = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bDibCreated = FALSE; + + beginTest( + "Character conversion test", + "Test character conversions from UTF8, Unicode, and Native", + "", + ""); + + endTest("PASS"); + + beginTest( + "dbCreate", + "Create a FLAIM Database", + "Get the DbSystemFactory object through COM and call dbCreate", + "No Additional Details."); + + if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_FLM_ERROR_STRING( "Failed to initialize test state.", + m_szDetails, rc); + goto Exit; + } + bDibCreated = TRUE; + + endTest("PASS"); + + beginTest( + "createDocument", + "Create a document and append 50000 child nodes", + "createDocument; createElement; appendChild", + "No Additional Details."); + + // Start an update transaction + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + // Create some schema definitions + + uiDefNum = 0; + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, "element1", XFLM_TEXT_TYPE, &uiDefNum))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + + + // Create a document + + if( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDoc))) + { + MAKE_FLM_ERROR_STRING( "createDocument failed", m_szDetails, rc); + goto Exit; + } + + // Create an element + + if( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, ELM_ELEMENT_TAG, + XFLM_FIRST_CHILD, &pRootElement))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + // Generate a large text value + + uiLargeTextSize = 1024; + if( (pucLargeBuf = (FLMBYTE *)malloc( uiLargeTextSize)) == NULL) + { + rc = NE_XFLM_MEM; + MAKE_FLM_ERROR_STRING( "malloc failed", m_szDetails, rc); + goto Exit; + } + + for( uiLoop = 0; uiLoop < uiLargeTextSize - 1; uiLoop++) + { + pucLargeBuf[ uiLoop] = (FLMBYTE)('A' + (uiLoop % 26)); + } + + pucLargeBuf[ uiLargeTextSize - 1] = 0; + + for( uiLoop = 0; uiLoop < 5000; uiLoop++) + { + // Create an element + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, ELEMENT_NODE, + uiDefNum, XFLM_LAST_CHILD, &pElement))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + // Create an attribute + + if( RC_BAD( rc = pElement->createAttribute( + m_pDb, ATTR_NEXT_INDEX_NUM_TAG, &pAttr))) + { + MAKE_FLM_ERROR_STRING( "createAttribute failed", m_szDetails, rc); + goto Exit; + } + + // Set the attribute's value + +#ifdef FLM_UNIX + if( RC_BAD( rc = pAttr->setUINT64( m_pDb, 0x8a306d2d713a8cfeULL))) +#else + if( RC_BAD( rc = pAttr->setUINT64( m_pDb, 0x8a306d2d713a8cfe))) +#endif + { + MAKE_FLM_ERROR_STRING( "setUINT64 failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT64( m_pDb, &ui64Tmp))) + { + MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); + goto Exit; + } + +#ifdef FLM_UNIX + if( ui64Tmp != 0x8a306d2d713a8cfeULL) +#else + if( ui64Tmp != 0x8a306d2d713a8cfe) +#endif + { + rc = NE_XFLM_FAILURE; + MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", + m_szDetails, rc); + goto Exit; + } + + // Create another attribute + + if( RC_BAD( rc = pElement->createAttribute( + m_pDb, ATTR_COMPARE_RULES_TAG, &pAttr))) + { + MAKE_FLM_ERROR_STRING( "createAttribute failed", m_szDetails, rc); + goto Exit; + } + + // Set the attribute's value + + if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"1234567890", 0))) + { + MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTmp))) + { + MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); + goto Exit; + } + + if( uiTmp != 1234567890) + { + rc = NE_XFLM_FAILURE; + MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", + m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"987", 0, TRUE))) + { + MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiTmp))) + { + MAKE_FLM_ERROR_STRING( "getUINT failed", m_szDetails, rc); + goto Exit; + } + + if( uiTmp != 987) + { + rc = NE_XFLM_FAILURE; + MAKE_FLM_ERROR_STRING( "getUINT returned invalid data", + m_szDetails, rc); + goto Exit; + } + + // Set a long value into the attribute + + if( RC_BAD( rc = pAttr->setUTF8( m_pDb, pucLargeBuf, 0, TRUE))) + { + MAKE_FLM_ERROR_STRING( "setNative failed", m_szDetails, rc); + goto Exit; + } + + memset( pucLargeBuf, 0, uiLargeTextSize); + + if( RC_BAD( rc = pAttr->getUTF8( m_pDb, pucLargeBuf, + uiLargeTextSize, 0, uiLargeTextSize - 1))) + { + MAKE_FLM_ERROR_STRING( "getNative failed", m_szDetails, rc); + goto Exit; + } + + // Create a data node + + if( RC_BAD( rc = pElement->createNode( m_pDb, DATA_NODE, + 0, XFLM_LAST_CHILD, &pData))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( (uiLoop % 1000) == 0) + { + if ( m_bDisplayVerbose) + { + if( RC_BAD( rc = pData->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + +#ifdef FLM_WIN + sprintf( szMsgBuf, "Node count = %I64u", ui64Tmp); +#else + sprintf( szMsgBuf, "Node count = %llu", ui64Tmp); +#endif + display( szMsgBuf); + } + } + } + + endTest("PASS"); + + // Read the nodes + beginTest( + "read nodes", + "Read and verify the DOM nodes", + "Self-Explanatory", + "No Additional Details."); + + if( RC_BAD( rc = pData->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + uiHighNode = (FLMUINT)ui64Tmp; + for( uiLoop = 1; uiLoop < uiHighNode; uiLoop++) + { + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, + uiLoop, &pTmpNode))) + { + MAKE_FLM_ERROR_STRING( "getNode failed", m_szDetails, rc); + goto Exit; + } + + if( (uiLoop % 1000) == 0) + { + if ( m_bDisplayVerbose) + { +#ifdef FLM_WIN + sprintf( szMsgBuf, "Read = %I64u", (FLMUINT64)uiLoop); +#else + sprintf( szMsgBuf, "Read = %llu", (FLMUINT64)uiLoop); +#endif + display( szMsgBuf); + } + } + } + + endTest("PASS"); + + // Delete the document + beginTest( + "Document Delete", + "delete the document we just created", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = pDoc->deleteNode( m_pDb))) + { + MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); + goto Exit; + } + + // Commit the transaction + + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); + goto Exit; + } + + // Close the database + + m_pDb->Release(); + m_pDb = NULL; + + endTest("PASS"); + + if( uiTmp) + { + goto Exit; + } + + // Tests for unique child elements. + + beginTest( + "Unique Child Elements", + "Create/Read/Delete unique child elements", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); + goto Exit; + } + + // Start an update transaction + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + // Create two element definitions, one that requires uniqueness, + // another that does not. + + uiDefNum1 = 0; + if( RC_BAD( rc = m_pDb->createUniqueElmDef( + NULL, "u_element1", &uiDefNum1))) + { + MAKE_FLM_ERROR_STRING( "createUniqueElmDef failed", m_szDetails, rc); + goto Exit; + } + uiDefNum2 = 0; + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, "u_element2", XFLM_NODATA_TYPE, &uiDefNum2))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + + // Create 200 unique element definitions. + + uiDefNum3 = uiDefNum2 + 1; + for (uiLoop = 0; uiLoop < 200; uiLoop++) + { + char szName [80]; + FLMUINT uiNumToUse = uiDefNum3 + uiLoop; + + f_sprintf( szName, "c_element%u", (unsigned)uiLoop); + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, szName, XFLM_NODATA_TYPE, &uiNumToUse))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + } + + // Create a root node, with two child nodes. + + if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, + ELM_ELEMENT_TAG, &pRootElement))) + { + MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum1, XFLM_FIRST_CHILD, &pUniqueElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum2, XFLM_LAST_CHILD, &pNonUniqueElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + // Under each child node create 200 nodes, with a unique ID + + for (uiLoop = 0; uiLoop < 200; uiLoop++) + { + // Should not allow a data node. + + if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, + DATA_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + if (rc == NE_XFLM_BAD_DATA_TYPE || rc == NE_XFLM_DOM_INVALID_CHILD_TYPE) + { + rc = NE_XFLM_OK; + } + else + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + } + else + { + // Should not have succeeded. + + endTest( "FAIL"); + goto Finish_Test; + } + + // 1st add should work, 2nd add under unique parent should fail + + if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + if (rc == NE_XFLM_DOM_DUPLICATE_ELEMENT) + { + rc = NE_XFLM_OK; + } + else + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + } + else + { + // Should not have succeeded. + + endTest("FAIL"); + goto Finish_Test; + } + + // Should be able to do it twice under non-unique parent + + if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + // Should not allow a data node. + + if( RC_BAD( rc = pNonUniqueElement->createNode( m_pDb, + DATA_NODE, uiDefNum3 + uiLoop, XFLM_LAST_CHILD, &pElement, NULL))) + { + if( rc == NE_XFLM_BAD_DATA_TYPE || + rc == NE_XFLM_DOM_INVALID_CHILD_TYPE) + { + rc = NE_XFLM_OK; + } + else + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + } + else + { + // Should not have succeeded. + + endTest("FAIL"); + goto Finish_Test; + } + } + + // Go through the loop and get all 200, then delete, and get again + + for( uiLoop = 0; uiLoop < 200; uiLoop++) + { + // 1st get should work, then delete, 2nd get should fail + + if( RC_BAD( rc = pUniqueElement->getChildElement( m_pDb, + uiDefNum3 + uiLoop, &pElement))) + { + MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = pElement->deleteNode( m_pDb))) + { + MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pUniqueElement->getChildElement( m_pDb, + uiDefNum3 + uiLoop, &pElement))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); + goto Exit; + } + } + else + { + // Should not have succeeded. + + endTest("FAIL"); + goto Finish_Test; + } + + // 1st get should work, then delete, 2nd should also work + + if( RC_BAD( rc = pNonUniqueElement->getChildElement( m_pDb, + uiDefNum3 + uiLoop, &pElement))) + { + MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = pElement->deleteNode( m_pDb))) + { + MAKE_FLM_ERROR_STRING( "deleteNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pNonUniqueElement->getChildElement( m_pDb, + uiDefNum3 + uiLoop, &pElement))) + { + MAKE_FLM_ERROR_STRING( "getChildElement failed", m_szDetails, rc); + goto Exit; + } + } + + // Commit the transaction + + bStartedTrans = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + +Finish_Test: + + if( bStartedTrans) + { + m_pDb->transAbort(); + bStartedTrans = FALSE; + } + + // Close the database. + + m_pDb->Release(); + m_pDb = NULL; + + // Re-open the database + + beginTest( + "backup", + "backup the database", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); + goto Exit; + } + + // Backup the database + + if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, + XFLM_READ_TRANS, 0, &pBackup))) + { + MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pBackup->backup( BACKUP_NAME_STR, + NULL, NULL, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); + goto Exit; + } + + pBackup->Release(); + pBackup = NULL; + + endTest("PASS"); + + // Close the database again + + beginTest( + "dbRemove", + "remove the database", + "Self-explanatory", + "No Additional Details."); + + m_pDb->Release(); + m_pDb = NULL; + + if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) + { + MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); + goto Exit; + } + + // Remove the database + + if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + // Restore the database + + beginTest( + "dbRestore", + "restore the database from the backup we just made", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, + NULL, NULL, BACKUP_NAME_STR, NULL, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "backup (w/ password)", + "backup the database using a password", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); + goto Exit; + } + + // Backup the database + + if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, + XFLM_READ_TRANS, 0, &pBackup))) + { + MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); + goto Exit; + } + + rc = pBackup->backup( BACKUP_NAME_STR, + BACKUP_PASSWORD_STR, NULL, NULL, NULL); + +#ifdef FLM_USE_NICI + // The criteria for a successful test varies depending on whether we're + // compiled with NICI or not... + if( RC_BAD( rc)) +#else + if (rc != NE_XFLM_UNSUPPORTED_FEATURE) +#endif + { + MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); + goto Exit; + } + + pBackup->Release(); + pBackup = NULL; + + m_pDb->Release(); + m_pDb = NULL; + + endTest("PASS"); + +#ifdef FLM_USE_NICI + // No point in trying to restore with a password when we + // don't have NICI because there's no way a password + // based backup could work... + // Restore the database (using the password) + beginTest( + "dbRestore (w/ password)", + "restore the database from the backup we just made", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) + { + MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); + goto Exit; + } + + // Remove the database + + if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, + NULL, NULL, BACKUP_NAME_STR, BACKUP_PASSWORD_STR, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + // Wrap the database key back in the server key + + beginTest( + "wrapKey", + "wrap the database key in the NICI server key", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb))) + { + MAKE_FLM_ERROR_STRING( "dbOpen failed", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = m_pDb->wrapKey())) + { + MAKE_FLM_ERROR_STRING( "wrapKey failed", m_szDetails, rc); + goto Exit; + } + + m_pDb->Release(); + m_pDb = NULL; + + endTest("PASS"); +#endif // FLM_USE_NICI + + // Rename the database + + beginTest( + "dbRename", + "rename the database", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbRename( DB_NAME_STR, NULL, NULL, + NEW_NAME_STR, TRUE, NULL))) + { + MAKE_FLM_ERROR_STRING( "dbRename failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + // Copy the database + + beginTest( + "dbCopy", + "copy the database", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbCopy( NEW_NAME_STR, NULL, NULL, + DB_NAME_STR, NULL, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING( "dbCopy failed", m_szDetails, rc); + goto Exit; + } + + // Remove both databases + + if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDbSystem->dbRemove( NEW_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + +Exit: + + if( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if( bStartedTrans) + { + m_pDb->transAbort(); + } + + if( pBackup) + { + pBackup->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + if( pRootElement) + { + pRootElement->Release(); + } + + if( pUniqueElement) + { + pUniqueElement->Release(); + } + + if( pNonUniqueElement) + { + pNonUniqueElement->Release(); + } + + if( pElement) + { + pElement->Release(); + } + + if( pData) + { + pData->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( pucLargeBuf) + { + free( pucLargeBuf); + } + + if( RC_BAD( rc)) + { + sprintf( szMsgBuf, "Error %04X -- %s\n", (unsigned)rc, + m_pDbSystem->errorString( rc)); + display( szMsgBuf); + log( szMsgBuf); + + return( rc); + } + else + { + // Shut down the FLAIM database engine. This call must be made + // even if the init call failed. No more FLAIM calls should be made + // by the application. + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( NE_XFLM_OK); + } +} diff --git a/version5/util/binarytest.cpp b/version5/util/binarytest.cpp new file mode 100644 index 0000000..7884af1 --- /dev/null +++ b/version5/util/binarytest.cpp @@ -0,0 +1,630 @@ +//------------------------------------------------------------------------------ +// Desc: Unit test for testing binary values +// +// 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: binarytest.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\BIN.DB" +#else + #define DB_NAME_STR "bin.db" +#endif + +#define BIN_FILE_SIZE 2000000 +#define BIG_FILE "BINARYFILE" +#define BIN_BUFFER_SIZE 6550 + +/**************************************************************************** +Desc: +****************************************************************************/ +class IBinaryTestImpl : public TestBase +{ +public: + + RCODE verifyData( + IF_FileHdl * pFileHdl, + FLMUINT64 ui64NodeId); + + RCODE encryptionTest( + IF_FileHdl * pFileHdl, + FLMUINT64 ui64NodeId); + + RCODE buildBinaryFile( void); + + const char * getName( void); + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if ( ( *ppTest = new IBinaryTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IBinaryTestImpl::getName( void) +{ + return( "Binary Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IBinaryTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDibCreated = FALSE; + IF_FileSystem * pFileSystem = NULL; + IF_FileHdl * pFileHdl = NULL; + FLMUINT uiBytesRead = 0; + FLMUINT uiTotalBytesRead = 0; + FLMUINT uiNameId = 0; + IF_DOMNode * pNode = NULL; + FLMBOOL bLast = FALSE; + char szBuffer[1024]; + FLMBOOL bTransActive = FALSE; + char * pszBuffer = NULL; + FLMUINT64 ui64NodeId; + + beginTest( + "Large Binary Value Test", + "Insert a large binary value into a DOM Node ", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_ERROR_STRING( "Failed to initialize test state", m_szDetails, rc); + goto Exit; + } + bDibCreated = TRUE; + + if ( RC_BAD( rc = buildBinaryFile())) + { + MAKE_ERROR_STRING( "Failed to create binary test file", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->createElementDef( + NULL, + "bin_data", + XFLM_BINARY_TYPE, + &uiNameId))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DATA_COLLECTION, uiNameId, &pNode, &ui64NodeId))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + m_pDbSystem->getFileSystem( &pFileSystem); + + if ( RC_BAD( rc = pFileSystem->Open( + BIG_FILE, XFLM_IO_RDONLY, &pFileHdl))) + { + MAKE_ERROR_STRING( "Failed to open file.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + // Set a small binary value so we can test to make sure that the code + // allows an element with an embedded value to be changed to one with + // a streaming value + + f_strcpy( szBuffer, "Hello, World!"); + if ( RC_BAD( rc = pNode->setBinary( m_pDb, szBuffer, + f_strlen( szBuffer) + 1, TRUE))) + { + MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); + goto Exit; + } + + for(;;) + { + if ( RC_BAD( rc = pFileHdl->Read( + uiTotalBytesRead, + sizeof(szBuffer), + szBuffer, + &uiBytesRead))) + { + if ( rc == NE_XFLM_IO_END_OF_FILE) + { + bLast = TRUE; + } + else + { + MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); + goto Exit; + } + } + + if ( RC_BAD( rc = pNode->setBinary( m_pDb, szBuffer, uiBytesRead, bLast))) + { + MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); + goto Exit; + } + uiTotalBytesRead += uiBytesRead; + + if ( bLast) + { + break; + } + } + + pNode->Release(); + pNode = NULL; + + bTransActive = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) + { + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Large Encrypted Binary Value Test", + "Insert a large encrypted binary value into a DOM Node ", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + if ( RC_BAD( rc = encryptionTest( pFileHdl, ui64NodeId))) + { + goto Exit; + } + + bTransActive = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "6550 Byte Binary Value Test", + "Insert a binary value of size 6550 into a DOM Node ", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + if ( RC_BAD( rc = f_alloc( BIN_BUFFER_SIZE, &pszBuffer))) + { + MAKE_ERROR_STRING( "f_alloc failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) + { + MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, BIN_BUFFER_SIZE, TRUE))) + { + MAKE_ERROR_STRING( "setBinary failed", m_szDetails, rc); + goto Exit; + } + + bTransActive = FALSE; + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + +Exit: + + if( bTransActive) + { + if ( RC_BAD( rc)) + { + m_pDb->transAbort(); + } + else + { + m_pDb->transCommit(); + } + } + + if( RC_BAD( rc)) + { + endTest("FAIL"); + } + else + { + endTest("PASS"); + } + + if ( pszBuffer) + { + f_free( &pszBuffer); + } + + if ( pNode) + { + pNode->Release(); + } + + pFileSystem->Delete( BIG_FILE); + if ( pFileSystem) + { + pFileSystem->Release(); + } + + if ( pFileHdl) + { + pFileHdl->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IBinaryTestImpl::buildBinaryFile( void) +{ + IF_FileSystem * pFileSystem = NULL; + IF_FileHdl * pFileHdl = NULL; + FLMUINT uiBytesToWrite; + FLMUINT uiBytesWritten; + FLMUINT uiSpaceLeft = BIN_FILE_SIZE; + char szChunk[1000]; + char c = 0; + RCODE rc = NE_XFLM_OK; + + m_pDbSystem->getFileSystem( &pFileSystem); + + pFileSystem->Delete( BIG_FILE); + if ( RC_BAD( rc = pFileSystem->Create( + BIG_FILE, XFLM_IO_RDWR, &pFileHdl))) + { + MAKE_ERROR_STRING( "File create failed", m_szDetails, rc); + goto Exit; + } + + for( ;;) + { + uiBytesToWrite = f_min( uiSpaceLeft, sizeof(szChunk)); + f_memset( szChunk, c++, uiBytesToWrite); + + if ( RC_BAD( rc = pFileHdl->Write( + XFLM_IO_CURRENT_POS, + uiBytesToWrite, + szChunk, + &uiBytesWritten))) + { + MAKE_ERROR_STRING( "Write failed", m_szDetails, rc); + goto Exit; + } + + uiSpaceLeft -= uiBytesToWrite; + + if ( !uiSpaceLeft) + { + break; + } + } + +Exit: + + if ( pFileHdl) + { + pFileHdl->Release(); + } + + if ( pFileSystem) + { + pFileSystem->Release(); + } + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IBinaryTestImpl::verifyData( + IF_FileHdl * pFileHdl, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + char szBuf1[ 10000]; + char szBuf2[ sizeof(szBuf1)]; + FLMUINT uiTotalBytesRead = 0; + FLMUINT uiBytesRead1 = 0; + FLMUINT uiBytesRead2 = 0; + FLMBOOL bLast = FALSE; + IF_DOMNode * pNode = NULL; + + m_pDbSystem->clearCache( m_pDb); + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) + { + goto Exit; + } + + for(;;) + { + if ( RC_BAD( rc = pFileHdl->Read( + uiTotalBytesRead, + sizeof(szBuf1), + szBuf1, + &uiBytesRead1))) + { + if ( rc == NE_XFLM_IO_END_OF_FILE) + { + bLast = TRUE; + } + else + { + MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); + goto Exit; + } + } + + if ( RC_BAD( rc = pNode->getBinary( + m_pDb, + szBuf2, + uiTotalBytesRead, + sizeof(szBuf2), + &uiBytesRead2))) + { + if ( !( rc == NE_XFLM_EOF_HIT && bLast)) + { + MAKE_ERROR_STRING( "getBinary failed.", m_szDetails, rc); + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + + if ( (uiBytesRead1 != uiBytesRead2) || + f_memcmp( szBuf1, szBuf2, uiBytesRead1) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "Values do not match", m_szDetails, rc); + goto Exit; + } + + uiTotalBytesRead += uiBytesRead1; + + if ( bLast) + { + break; + } + + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + return rc; +} + +#define START_SEED 123 + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IBinaryTestImpl::encryptionTest( + IF_FileHdl * pFileHdl, + FLMUINT64 ui64NodeId) +{ + F_RandomGenerator rand; + RCODE rc = NE_XFLM_OK; + FLMUINT uiEncDef = 0; + FLMUINT64 ui64TotalSize; + char * pszBuffer = NULL; + FLMBOOL bLast = FALSE; + FLMUINT uiTotalBytesRead = 0; + FLMUINT uiChunkSize; + FLMUINT uiBytesRead; + IF_DOMNode * pNode = NULL; + +#ifdef FLM_USE_NICI + if ( RC_BAD( rc = m_pDb->createEncDef( + "aes", + "aes_def", + 0, + &uiEncDef))) + { + MAKE_ERROR_STRING( "Failed to create encryption definition", + m_szDetails, rc); + goto Exit; + } +#endif + + rand.randomSetSeed( START_SEED); + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) + { + MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pFileHdl->Size( &ui64TotalSize))) + { + MAKE_ERROR_STRING( "Failed to get file size", m_szDetails, rc); + goto Exit; + } + + for(;;) + { + if ( uiTotalBytesRead < ui64TotalSize) + { + uiChunkSize = rand.randomChoice( 1, + (FLMUINT32)(ui64TotalSize - uiTotalBytesRead)); + + if ( pszBuffer) + { + f_free( &pszBuffer); + } + + if (RC_BAD( rc = f_alloc( uiChunkSize, &pszBuffer))) + { + MAKE_ERROR_STRING( "f_alloc failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pFileHdl->Read( + uiTotalBytesRead, + uiChunkSize, + pszBuffer, + &uiBytesRead))) + { + MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); + goto Exit; + } + } + else + { + bLast = TRUE; + uiBytesRead = 0; + } + + if( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, uiBytesRead, + bLast, uiEncDef))) + { + MAKE_ERROR_STRING( "setBinary failed.", m_szDetails, rc); + goto Exit; + } + uiTotalBytesRead += uiBytesRead; + + if ( bLast) + { + break; + } + } + + pNode->Release(); + pNode = NULL; + + if( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) + { + goto Exit; + } + + if (RC_BAD( rc = f_realloc( uiTotalBytesRead, &pszBuffer))) + { + MAKE_ERROR_STRING( "f_realloc failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, ui64NodeId, &pNode))) + { + MAKE_ERROR_STRING( "getNode failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pFileHdl->Read( 0, uiTotalBytesRead, + pszBuffer, &uiBytesRead))) + { + MAKE_ERROR_STRING( "Failed to read from file", m_szDetails, rc); + goto Exit; + } + + // Set the node to have a large non-streaming value + + if( RC_BAD( rc = pNode->setBinary( m_pDb, pszBuffer, + uiBytesRead, TRUE, uiEncDef))) + { + goto Exit; + } + + pNode->Release(); + pNode = NULL; + + if ( RC_BAD( rc = verifyData( pFileHdl, ui64NodeId))) + { + goto Exit; + } + +Exit: + + if( pszBuffer) + { + f_free( &pszBuffer); + } + + if( pNode) + { + pNode->Release(); + } + + return( rc); +} diff --git a/version5/util/checkdb.cpp b/version5/util/checkdb.cpp new file mode 100644 index 0000000..ac74061 --- /dev/null +++ b/version5/util/checkdb.cpp @@ -0,0 +1,2178 @@ +//------------------------------------------------------------------------------ +// Desc: Checks a database for corruptions +// +// Tabs: 3 +// +// Copyright (c) 1992-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: checkdb.cpp 3129 2006-01-25 11:46:17 -0700 (Wed, 25 Jan 2006) ahodgkinson $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "ftx.h" +#include "sharutil.h" + +#define UTIL_ID "CHECKDB" + +#define LABEL_COLUMN 5 +#define VALUE_COLUMN 30 + +#define LOG_FILE_ROW 1 +#define SOURCE_ROW 2 +#define DATA_DIR_ROW 3 +#define RFL_DIR_ROW 4 +#define CACHE_USED_ROW 5 +#define ECACHE_USED_ROW 6 +#define DOING_ROW 7 +#define FILE_SIZE_ROW 8 +#define AMOUNT_DONE_ROW 9 + +#define TOTAL_DOM_NODES_ROW 10 +#define DOM_LINKS_VERIFIED_ROW 11 +#define TOTAL_BROKEN_LINKS_ROW 12 + +#define TOTAL_KEYS_ROW 13 +#define TOTAL_DUPS_ROW 14 +#define TOTAL_KEYS_EXAM_ROW 15 +#define BAD_IXREF_ROW 16 +#define MISSING_IXREF_ROW 17 +#define CONFLICT_ROW 18 +#define CORRUPT_ROW 19 +#define TOTAL_CORRUPT_ROW 20 +#define REPAIR_ROW 21 +#define OLD_VIEW_ROW 22 +#define MISMATCH_ROW 23 + +#define MAX_LOG_BUFF 2048 + +#define MISMATCH_READ_ERR 0 +#define MISMATCH_ERROR_CODE 1 +#define MISMATCH_ERR_LOCALE 2 +#define MISMATCH_LF_NUMBER 3 +#define MISMATCH_LF_NAME 4 +#define MISMATCH_LF_TYPE 5 +#define MISMATCH_LF_LEVEL 6 +#define MISMATCH_BLK_ADDRESS 7 +#define MISMATCH_PARENT_ADDRESS 8 +#define MISMATCH_ELM_OFFSET 9 +#define MISMATCH_DRN 10 +#define MISMATCH_ELM_REC_OFFSET 11 +#define MISMATCH_FIELD_NUM 12 +#define MISMATCH_ERR_NOT_LOGGED 13 + +#define SCREEN_REFRESH_RATE 100 + +/******************************************************************** +Desc: +*********************************************************************/ +class F_LocalCheckStatus : public IF_DbCheckStatus +{ +public: + + F_LocalCheckStatus() + { + m_uiLastRefresh = 0; + } + + RCODE XFLMAPI reportProgress( + XFLM_PROGRESS_CHECK_INFO * pProgCheck); + + RCODE XFLMAPI reportCheckErr( + XFLM_CORRUPT_INFO * pCorruptInfo, + FLMBOOL * pbFix); + +private: + FLMUINT m_uiLastRefresh; +}; + +FSTATIC FLMBOOL CheckDatabase( void); + +FSTATIC FLMBOOL DoCheck( void); + +FSTATIC void CheckShowHelp( + FLMBOOL bShowFullUsage); + +FSTATIC FLMBOOL GetParams( + FLMINT iArgC, + char ** ppszArgV); + +FSTATIC void OutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pszLabel, + const char * pszValue, + FLMUINT64 ui64NumValue, + FLMBOOL bLogIt); + +FSTATIC void OutLine( + const char * pszString); + +FSTATIC void LogFlush( void); + +FSTATIC void LogString( + const char * pszString); + +FSTATIC void DisplayValue( + FLMUINT uiRow, + const char * pszValue); + +FSTATIC void DisplayNumValue( + FLMUINT uiRow, + FLMUINT64 ui64Number); + +FSTATIC void OutValue( + const char * pszLabel, + const char * pszValue); + +FSTATIC void OutUINT( + const char * pszLabel, + FLMUINT uiNum); + +FSTATIC void OutUINT64( + const char * pszLabel, + FLMUINT64 ui64Num); + +FSTATIC void OutBlkHeader( void); + +FSTATIC void OutOneBlockStat( + const char * pszLabel, + FLMUINT uiBlockSize, + FLMUINT64 ui64KeyCount, + FLMUINT64 ui64BytesUsed, + FLMUINT64 ui64ElementCount, + FLMUINT64 ui64ContElementCount, + FLMUINT64 ui64ContElmBytes, + FLMUINT uiBlockCount, + FLMINT iLastError, + FLMUINT uiNumErrors); + +FSTATIC void OutLogicalFile( + IF_DbInfo * pDbInfo, + FLMUINT uiIndex); + +FSTATIC void PrintInfo( + IF_DbInfo * pDbInfo); + +FSTATIC FLMUINT CheckShowError( + const char * pszMessage, + FLMBOOL bLogIt); + +FSTATIC RCODE GetUserInput( void); + +FSTATIC void LogStr( + FLMUINT uiIndent, + const char * pszStr); + +FSTATIC void LogCorruptError( + XFLM_CORRUPT_INFO * pCorrupt); + +FSTATIC void LogKeyError( + XFLM_CORRUPT_INFO * pCorrupt); + +FSTATIC FLMBOOL DisplayField( + IF_DataVector * ifpKey, + FLMUINT uiElementNumber, + FLMUINT uiStartCol, + FLMUINT uiLevelOffset); + +FSTATIC FLMBOOL NumToName( + FLMUINT uiNum, + FLMUINT uiType, + char * pszBuf); + +#ifdef FLM_NLM +FLMBOOL gv_bSynchronized = FALSE; +FSTATIC void chkCleanup( void); +#endif + +FLMBOOL gv_bShutdown = FALSE; +static IF_FileHdl * gv_pLogFile = NULL; +static IF_DbInfo * gv_pDbInfo = NULL; +static F_Db * gv_pDb = NULL; +static F_NameTable * gv_pNameTable = NULL; +static FLMUINT gv_uiMaxRow; +static char gv_szLogFileName[ F_PATH_MAX_SIZE]; +static char gv_szTmpDir[ F_PATH_MAX_SIZE]; +static char gv_szLastError[ 256]; +static FLMUINT gv_uiLineCount; +static char gv_szDbFileName[ F_PATH_MAX_SIZE]; +static char gv_szDataDir[ F_PATH_MAX_SIZE]; +static char gv_szRflDir[ F_PATH_MAX_SIZE]; +static char * gv_pszLogBuffer = NULL; +static FLMUINT64 gv_ui64FileSize; +static FLMUINT64 gv_ui64BytesDone; +static FLMUINT gv_uiCorruptCount; +static FLMUINT gv_uiRepairCount; +static FLMUINT gv_uiTotalCorruptions; +static FLMUINT gv_uiOldViewCount; +static FLMUINT gv_uiMismatchCount; +static FLMUINT gv_uiLogBufferCount = 0; +static FLMBOOL gv_bMultiplePasses = FALSE; +static FLMBOOL gv_bBatchMode; +static FLMBOOL gv_bContinue; +static FLMBOOL gv_bStartUpdate = FALSE; +static FLMBOOL gv_bRepairCorruptions = FALSE; +static FLMBOOL gv_bDoLogicalCheck = FALSE; +static FLMBOOL gv_bLoggingEnabled; +static FLMBOOL gv_bShowStats; +static FLMBOOL gv_bRunning; +static FLMBOOL gv_bPauseBeforeExiting = FALSE; +static FLMBOOL gv_bSkipDomLinkVerify = FALSE; +static char gv_szPassword[256]; + +#ifdef FLM_WATCOM_NLM + #define main nlm_main +#endif + +/******************************************************************** +Desc: +*********************************************************************/ +extern "C" int main( + int iArgC, + char ** ppszArgV) +{ + int iResCode = 0; + F_Pool LogPool; + F_DbSystem dbSystem; + + gv_bBatchMode = FALSE; + gv_bShutdown = FALSE; + gv_bRunning = TRUE; + gv_szLastError[ 0] = '\0'; + +#ifdef FLM_NLM + + /* Setup the routines to be called when the NLM exits itself */ + + atexit( chkCleanup); + + /* Register to see the DOWN server event. */ + +#endif + + if( RC_BAD( dbSystem.init())) + { + WpsStrOut( "\nCould not initialize FLAIM.\n"); + goto Exit; + } + + WpsInit( 0xFFFF, 0xFFFF, "FLAIM Database Check"); + WpsOptimize(); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsDrawBorder(); + WpsScrClr( 0, 0); + WpsScrSize( NULL, &gv_uiMaxRow); + + LogPool.poolInit( 1024); + if (RC_BAD( LogPool.poolAlloc( MAX_LOG_BUFF, (void **)&gv_pszLogBuffer))) + { + WpsStrOut( "\nFailed to allocatae memory pool\n"); + goto Exit; + } + + if( GetParams( iArgC, ppszArgV)) + { + if (!DoCheck()) + { + iResCode = 1; + } + } + + LogPool.poolFree(); + + if( (gv_bPauseBeforeExiting) && (!gv_bShutdown)) + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + if( gv_szLastError[ 0] != '\0') + { + WpsStrOut( gv_szLastError); + } + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsStrOut( "Press any character to exit CHECKDB: "); + for (;;) + { + if( gv_bShutdown) + { + break; + } + if (WpkTestKB()) + { + (void)WpkIncar(); + break; + } + } + } + +Exit: + + if (gv_pDbInfo) + { + gv_pDbInfo->Release(); + } + + WpsExit(); + dbSystem.exit(); +#ifdef FLM_NLM + if (!gv_bSynchronized) + { +// SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + gv_bRunning = FALSE; + return( iResCode); +} + +/******************************************************************** +Desc: Check the database... +*********************************************************************/ +FSTATIC FLMBOOL CheckDatabase( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiStatus; + char szTmpBuf[ 100]; + FLMUINT uiCheckFlags; + FLMBOOL bOk = TRUE; + IF_DbCheckStatus * pDbCheckStatus = NULL; + F_DbSystem dbSystem; + + // Open the database - so we can have access to its name table + + if (!gv_pDb) + { + if( RC_BAD( rc = dbSystem.dbOpen( + gv_szDbFileName, gv_szDataDir, + gv_szRflDir, gv_szPassword, XFLM_ALLOW_LIMITED_MODE, + (IF_Db **)&gv_pDb))) + { + f_strcpy( szTmpBuf, "Error opening database: "); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], + dbSystem.errorString( rc)); + CheckShowError( szTmpBuf, TRUE); + bOk = FALSE; + goto Exit; + } + } + + if (gv_pNameTable) + { + gv_pNameTable->Release(); + gv_pNameTable = NULL; + } + (void)gv_pDb->getNameTable( &gv_pNameTable); + + gv_uiCorruptCount = 0; + gv_ui64BytesDone = 0; + gv_ui64FileSize = 0; + gv_uiOldViewCount = 0; + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + + if (gv_bLoggingEnabled) + { + LogString( NULL); + LogString( NULL); + LogString( NULL); + LogString( +"=========================================================================="); + LogString( "CHECK PARAMETERS:"); + } + OutLabel( LABEL_COLUMN, + SOURCE_ROW, + "Database", + gv_szDbFileName, + 0, + TRUE); + + OutLabel( LABEL_COLUMN, + DATA_DIR_ROW, + "Data Files Dir.", + gv_szDataDir [0] + ? &gv_szDataDir [0] + : "", + 0, + TRUE); + + OutLabel( LABEL_COLUMN, + RFL_DIR_ROW, + "RFL Files Dir.", + gv_szRflDir [0] + ? &gv_szRflDir [0] + : "", + 0, + TRUE); + + OutLabel( LABEL_COLUMN, + LOG_FILE_ROW, + "Log File", + (gv_szLogFileName[ 0]) + ? &gv_szLogFileName[ 0] + : "", + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + CACHE_USED_ROW, + "Cache Bytes Used", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + ECACHE_USED_ROW, + "Ext. Cache Bytes Used", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + DOING_ROW, + "Doing", + "Opening Database File", + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + FILE_SIZE_ROW, + "File Size", + NULL, + (FLMUINT)gv_ui64FileSize, + FALSE); + + OutLabel( LABEL_COLUMN, + CORRUPT_ROW, + "File Corruptions", + NULL, + gv_uiCorruptCount, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_CORRUPT_ROW, + "Total Corruptions", + NULL, + gv_uiTotalCorruptions, + FALSE); + + OutLabel( LABEL_COLUMN, + OLD_VIEW_ROW, + "Old View Count", + NULL, + gv_uiOldViewCount, + FALSE); + + OutLabel( LABEL_COLUMN, + MISMATCH_ROW, + "Mismatch Count", + NULL, + gv_uiMismatchCount, + FALSE); + + OutLabel( LABEL_COLUMN, + REPAIR_ROW, + "Problems Repaired", + NULL, + gv_uiRepairCount, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_KEYS_ROW, + "Total Index Keys", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_DUPS_ROW, + "Total Duplicate Keys", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + CONFLICT_ROW, + "Key Conflicts", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_KEYS_EXAM_ROW, + "Num. Keys Checked", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + BAD_IXREF_ROW, + "Invalid Index Keys", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + MISSING_IXREF_ROW, + "Missing Index Keys", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_DOM_NODES_ROW, + "Total DOM Nodes", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + DOM_LINKS_VERIFIED_ROW, + "DOM Links Verified", + NULL, + 0, + FALSE); + + OutLabel( LABEL_COLUMN, + TOTAL_BROKEN_LINKS_ROW, + "DOM Links Broken", + NULL, + 0, + FALSE); + + if( gv_bLoggingEnabled) + { + LogString( NULL); + LogString( "CHECK DETAILED RESULTS:"); + LogString( NULL); + } + + uiCheckFlags = 0; + if( gv_bRepairCorruptions == TRUE) + { + uiCheckFlags |= XFLM_ONLINE; + } + + if( gv_bDoLogicalCheck == TRUE) + { + uiCheckFlags |= XFLM_DO_LOGICAL_CHECK; + } + + if (gv_bSkipDomLinkVerify) + { + uiCheckFlags |= XFLM_SKIP_DOM_LINK_CHECK; + } + + if (RC_OK( rc)) + { + F_LocalCheckStatus dbCheckStatus; + + rc = dbSystem.dbCheck( gv_szDbFileName, gv_szDataDir, gv_szRflDir, NULL, + uiCheckFlags, &gv_pDbInfo, &dbCheckStatus); + } + + if( rc == NE_XFLM_FAILURE) + { + f_sprintf( szTmpBuf, "User pressed ESCAPE, check halted"); + gv_bShutdown = TRUE; + } + else + { + f_strcpy( szTmpBuf, "RETURN CODE: "); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], dbSystem.errorString( rc)); + } + + uiStatus = CheckShowError( szTmpBuf, TRUE); + + if( ((uiStatus != WPK_ESCAPE) || (gv_bLoggingEnabled)) && + (gv_bShowStats) && + ((rc == NE_XFLM_OK) || + (rc == NE_XFLM_DATA_ERROR) || + (rc == NE_XFLM_TRANS_ACTIVE))) + { + PrintInfo( gv_pDbInfo); + } + + if( gv_bLoggingEnabled) + { + LogString( NULL); + LogFlush(); + } + +Exit: + + if( gv_pNameTable) + { + gv_pNameTable->Release(); + gv_pNameTable = NULL; + } + + if( gv_pDb) + { + gv_pDb->Release(); + } + + if (pDbCheckStatus) + { + pDbCheckStatus->Release(); + } + + return( bOk); +} + +/******************************************************************** +Desc: Function to coordinate check of the database. +*********************************************************************/ +FSTATIC FLMBOOL DoCheck( void) +{ + FLMBOOL bOk = TRUE; + char szTmpBuf[ 100]; + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + gv_bContinue = TRUE; + gv_uiLineCount = 0; + gv_bLoggingEnabled = FALSE; + gv_uiCorruptCount = 0; + gv_uiTotalCorruptions = 0; + gv_uiRepairCount = 0; + gv_uiMismatchCount = 0; + gv_uiLogBufferCount = 0; + + if( gv_szLogFileName[ 0]) + { + gv_pFileSystem->Delete( gv_szLogFileName); + if( RC_OK( rc = gv_pFileSystem->Create( gv_szLogFileName, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE, &gv_pLogFile))) + { + gv_bLoggingEnabled = TRUE; + } + else + { + f_strcpy( szTmpBuf, "Error creating log file: "); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], dbSystem.errorString( rc)); + CheckShowError( szTmpBuf, FALSE); + bOk = FALSE; + goto Exit; + } + } + + WpsCursorSetType( WPS_CURSOR_INVISIBLE); + for( ;;) + { + // Check the database... + if( !CheckDatabase()) + { + bOk = FALSE; + break; + } + + if( (!gv_bMultiplePasses) || (gv_bShutdown)) + { + break; + } + } + WpsCursorSetType( WPS_CURSOR_UNDERLINE); + + if( gv_bLoggingEnabled) + { + LogFlush(); + gv_pLogFile->Close(); + gv_pLogFile->Release(); + gv_pLogFile = NULL; + } + +Exit: + + return( bOk); +} + +/******************************************************************** +Desc: Show the help screen. +*********************************************************************/ +FSTATIC void CheckShowHelp( + FLMBOOL bShowFullUsage + ) +{ + WpsStrOut( "\n"); + if (bShowFullUsage) + { + WpsStrOut( "Usage: checkdb [Options]\n"); + } + else + { + WpsStrOut( "Parameters: [Options]\n\n"); + } + + WpsStrOut( +" FileName = Name of database to check.\n"); + WpsStrOut( +" Options\n"); + WpsStrOut( +" -b = Run in Batch Mode.\n"); + WpsStrOut( +" -c = Repair logical corruptions.\n"); + WpsStrOut( +" -d = Display/log detailed statistics.\n"); + WpsStrOut( +" -dr = RFL directory.\n"); + WpsStrOut( +" -dd = Data directory.\n"); + WpsStrOut( +" -i = Perform a logical (index) check.\n"); + WpsStrOut( +" -l = Log detailed information to .\n"); + WpsStrOut( +" -m = Multiple passes (continuous check).\n"); + WpsStrOut( +" -o = Output binary log information to .\n"); + WpsStrOut( +" -p = Pause before exiting.\n"); + WpsStrOut( +" -s = Skip DOM link verification.\n"); + WpsStrOut( +" -t = Temporary directory.\n"); + WpsStrOut( +" -u = Run check in an update transaction.\n"); + WpsStrOut( +" -v = Verify binary log information in . NOTE:\n"); + WpsStrOut( +" The -v and -o options cannot both be specified.\n"); + WpsStrOut( +" -a = Database password.\n"); +#ifdef FLM_NLM + WpsStrOut( +" -w = Wait to end to synchronize\n"); +#endif + WpsStrOut( +" -? = A '?' anywhere in the command line will cause this\n"); + WpsStrOut( +" screen to be displayed.\n"); + WpsStrOut( +"Options may be specified anywhere in the command line.\n"); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBOOL GetParams( + FLMINT iArgC, + char ** ppszArgV + ) +{ +#define MAX_ARGS 30 + FLMUINT uiLoop; + char szTmpBuf[ 100]; + char * pszTmp; + char * ppszArgs[ MAX_ARGS]; + char szCommandBuffer[ 300]; +#ifdef FLM_NLM + FLMBOOL bWaitToSync = FALSE; +#endif + + gv_szDbFileName [0] = '\0'; + gv_szDataDir [0] = '\0'; + gv_szRflDir [0] = '\0'; + gv_szLogFileName[ 0] = '\0'; + gv_szTmpDir[ 0] = '\0'; + gv_bShowStats = FALSE; + gv_szPassword[0] = '\0'; + + /* + Ask the user to enter parameters if none were entered on the command + line. + */ + + if( iArgC < 2) + { + for( ;;) + { + WpsStrOut( "CheckDB Params (enter ? for help): "); + szCommandBuffer[ 0] = '\0'; + WpsLineEd( szCommandBuffer, sizeof( szCommandBuffer) - 1, + &gv_bShutdown); + if( gv_bShutdown) + { + return( FALSE); + } + if( f_stricmp( szCommandBuffer, "?") == 0) + { + CheckShowHelp( FALSE); + } + else + { + break; + } + } + flmUtilParseParams( szCommandBuffer, MAX_ARGS, &iArgC, &ppszArgs [1]); + ppszArgs [0] = ppszArgV [0]; + iArgC++; + ppszArgV = &ppszArgs [0]; + } + + uiLoop = 1; + while( uiLoop < (FLMUINT)iArgC) + { + pszTmp = ppszArgV[ uiLoop]; + + /* See if they specified an option */ + +#ifdef FLM_UNIX + if( *pszTmp == '-') +#else + if( (*pszTmp == '-') || (*pszTmp == '/')) +#endif + { + pszTmp++; + if( (*pszTmp == 'l') || (*pszTmp == 'L')) + { + pszTmp++; + if( *pszTmp) + { + f_strcpy( gv_szLogFileName, pszTmp); + } + else + { + if( CheckShowError( + "Log file name not specified in parameter", + FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( (*pszTmp == 't') || (*pszTmp == 'T')) + { + pszTmp++; + if( *pszTmp) + { + f_strcpy( gv_szTmpDir, pszTmp); + } + else + { + if( CheckShowError( + "Temporary directory not specified in parameter", + FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( (*pszTmp == 'd') || (*pszTmp == 'D')) + { + pszTmp++; + if (!(*pszTmp)) + { + gv_bShowStats = TRUE; + } + else if (*pszTmp == 'r' || *pszTmp == 'R') + { + f_strcpy( gv_szRflDir, pszTmp + 1); + } + else if (*pszTmp == 'd' || *pszTmp == 'D') + { + f_strcpy( gv_szDataDir, pszTmp + 1); + } + else + { + f_sprintf( szTmpBuf, "Invalid option %s", pszTmp - 1); + if( CheckShowError( szTmpBuf, FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if (*pszTmp == 'a' || *pszTmp == 'A') + { + f_strcpy( gv_szPassword, pszTmp + 1); + } + else if (f_stricmp( pszTmp, "B") == 0) + { + gv_bBatchMode = TRUE; + } + else if (f_stricmp( pszTmp, "C") == 0) + { + gv_bRepairCorruptions = TRUE; + } + else if (f_stricmp( pszTmp, "I") == 0) + { + gv_bDoLogicalCheck = TRUE; + } + else if (f_stricmp( pszTmp, "M") == 0) + { + gv_bMultiplePasses = TRUE; + } + else if (f_stricmp( pszTmp, "P") == 0) + { + gv_bPauseBeforeExiting = TRUE; + } + else if (f_stricmp( pszTmp, "S") == 0) + { + gv_bSkipDomLinkVerify = TRUE; + } + else if (f_stricmp( pszTmp, "U") == 0) + { + gv_bStartUpdate = TRUE; + } +#ifdef FLM_NLM + else if (f_stricmp( pszTmp, "W") == 0) + { + bWaitToSync = TRUE; + } +#endif + else if (f_stricmp( pszTmp, "?") == 0 || + f_stricmp( pszTmp, "HELP") == 0) + { +#ifdef FLM_NLM + if (!gv_bSynchronized) + { +// SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + CheckShowHelp( TRUE); + gv_bPauseBeforeExiting = TRUE; + return( FALSE); + } + else + { + f_sprintf( szTmpBuf, "Invalid option %s", pszTmp); + if( CheckShowError( szTmpBuf, FALSE) == WPK_ESCAPE) + { + return( FALSE); + } + } + } + else if( f_stricmp( pszTmp, "?") == 0) + { +Show_Help: +#ifdef FLM_NLM + if (!gv_bSynchronized) + { +// SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + CheckShowHelp( TRUE); + gv_bPauseBeforeExiting = TRUE; + return( FALSE); + } + else if( !gv_szDbFileName[ 0]) + { + f_strcpy( gv_szDbFileName, pszTmp); + } + uiLoop++; + } +#ifdef FLM_NLM + if (!bWaitToSync && !gv_bSynchronized) + { +// SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + if( !gv_szDbFileName[ 0]) + { + goto Show_Help; + } + else + { + return( TRUE); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogFlush( + void + ) +{ + FLMUINT uiBytesWritten; + + if( gv_uiLogBufferCount) + { + gv_pLogFile->Write( XFLM_IO_CURRENT_POS, + gv_uiLogBufferCount, (FLMBYTE *)gv_pszLogBuffer, + &uiBytesWritten); + gv_uiLogBufferCount = 0; + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogString( + const char * pszString) +{ + FLMUINT uiLen; + FLMUINT uiLoop; + + if( (gv_bLoggingEnabled) && (gv_pszLogBuffer != NULL)) + { + uiLen = (FLMUINT)((pszString != NULL) + ? (FLMUINT)(f_strlen( pszString)) + : 0); + for( uiLoop = 0; uiLoop < uiLen; uiLoop++) + { + gv_pszLogBuffer[ gv_uiLogBufferCount++] = *pszString++; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } + gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\r'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + gv_pszLogBuffer[ gv_uiLogBufferCount++] = '\n'; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLine( + const char * pszBuf) +{ + FLMUINT uiChar; + + if( gv_bLoggingEnabled) + { + LogString( pszBuf); + } + + if( !gv_bBatchMode) + { + if( gv_bContinue) + { + if( gv_uiLineCount == 20) + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( + "Press: ESC to quit, anything else to continue"); + for( ;;) + { + if( gv_bShutdown) + { + uiChar = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiChar = WpkIncar(); + break; + } + } + + if( uiChar == WPK_ESCAPE) + { + gv_bContinue = FALSE; + } + else + { + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + } + + gv_uiLineCount = 0; + } + } + + if( gv_bContinue) + { + WpsStrOutXY( pszBuf, 0, gv_uiLineCount); + gv_uiLineCount++; + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutValue( + const char * pszLabel, + const char * pszValue) +{ + char szTmpBuf[ 100]; + + f_strcpy( szTmpBuf, "...................................... "); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], pszValue); + f_memcpy( szTmpBuf, pszLabel, (FLMSIZET)f_strlen( pszLabel)); + OutLine( szTmpBuf); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutUINT( + const char * pszLabel, + FLMUINT uiNum) +{ + char szValue [12]; + + if( uiNum == 0xFFFFFFFF) + { + f_strcpy( szValue, "0xFFFFFFFF"); + } + else + { + f_sprintf( szValue, "%u", (unsigned)uiNum); + } + + OutValue( pszLabel, szValue); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutUINT64( + const char * pszLabel, + FLMUINT64 ui64Num) +{ + char szValue [24]; + + if( ui64Num == (FLMUINT64)-1) + { + f_strcpy( szValue, "0xFFFFFFFFFFFFFFFF"); + } + else + { + f_sprintf( szValue, "%I64u", ui64Num); + } + + OutValue( pszLabel, szValue); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutBlkHeader( void) +{ + OutLine( +" Blk Type Blk Count Total Bytes Bytes Used Prcnt Element Cnt Avg Elem"); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutOneBlockStat( + const char * pszLabel, + FLMUINT uiBlockSize, + FLMUINT64 ui64KeyCount, + FLMUINT64 ui64BytesUsed, + FLMUINT64 ui64ElementCount, + FLMUINT64 ui64ContElementCount, + FLMUINT64 ui64ContElmBytes, + FLMUINT uiBlockCount, + FLMINT iLastError, + FLMUINT uiNumErrors) +{ + char szTmpBuf[ 100]; + FLMUINT64 ui64TotalBytes; + FLMUINT uiPercent; + FLMUINT uiAvgElementSize; + F_DbSystem dbSystem; + + ui64TotalBytes = (FLMUINT64)uiBlockCount * (FLMUINT64)uiBlockSize; + if( ui64ElementCount) + { + uiAvgElementSize = (FLMUINT)( ui64BytesUsed / ui64ElementCount); + } + else + { + uiAvgElementSize = 0; + } + + if( ui64BytesUsed > 40000000) + { + uiPercent = (FLMUINT)( ui64BytesUsed / (ui64TotalBytes / 100)); + } + else if( ui64TotalBytes) + { + uiPercent = (FLMUINT)((ui64BytesUsed * 100) / ui64TotalBytes); + } + else + { + uiPercent = 0; + } + + f_sprintf( szTmpBuf, "%-12s %10u %11u %10u %5u %11u %8u", + pszLabel, (unsigned)uiBlockCount, (unsigned)ui64TotalBytes, + (unsigned)ui64BytesUsed, + (unsigned)uiPercent, (unsigned)ui64ElementCount, + (unsigned)uiAvgElementSize); + OutLine( szTmpBuf); + + if( ui64ContElementCount) + { + uiAvgElementSize = (FLMUINT)( ui64ContElmBytes / ui64ContElementCount); + if( ui64ContElmBytes > 40000000) + { + uiPercent = + (FLMUINT)( ui64ContElmBytes / (ui64TotalBytes / 100)); + } + else if( ui64TotalBytes) + { + uiPercent = (FLMUINT)((ui64ContElmBytes * 100) / ui64TotalBytes); + } + else + { + uiPercent = 0; + } + + f_sprintf( szTmpBuf, "%-12s " + "%10u %5u %11u %8u", " ContElm", + (unsigned)ui64ContElmBytes, + (unsigned)uiPercent, (unsigned)ui64ContElementCount, + (unsigned)uiAvgElementSize); + OutLine( szTmpBuf); + } + + if( ui64KeyCount) + { + f_sprintf( szTmpBuf, "%-12s %10u", + " KeyCnt", (unsigned)ui64KeyCount); + OutLine( szTmpBuf); + } + + if( uiNumErrors) + { + f_strcpy( szTmpBuf, " LAST ERROR: "); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], + dbSystem.checkErrorToStr( iLastError)); + OutLine( szTmpBuf); + f_sprintf( szTmpBuf, + " TOTAL ERRORS: %u", (unsigned)uiNumErrors); + OutLine( szTmpBuf); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLogicalFile( + IF_DbInfo * pDbInfo, + FLMUINT uiIndex + ) +{ + char szTmpBuf[ 100]; + FLMUINT uiLoop; + char szLfName[ 30]; + FLMUINT uiLfNum; + eLFileType eLfType; + FLMUINT uiRootBlkAddress; + FLMUINT uiNumLevels; + FLMUINT64 ui64KeyCount; + FLMUINT64 ui64BytesUsed; + FLMUINT64 ui64ElementCount; + FLMUINT64 ui64ContElementCount; + FLMUINT64 ui64ContElmBytes; + FLMUINT uiBlockCount; + FLMINT iLastError; + FLMUINT uiNumErrors; + + pDbInfo->getBTreeInfo( uiIndex, &uiLfNum, &eLfType, + &uiRootBlkAddress, + &uiNumLevels); + + switch( eLfType) + { + case XFLM_LF_COLLECTION: /* Data collection */ + f_strcpy( szTmpBuf, "COLLECTION"); + break; + case XFLM_LF_INDEX: /* Index */ + f_strcpy( szTmpBuf, "INDEX"); + break; + default: + break; + } + (void)NumToName( uiLfNum, + eLfType == XFLM_LF_COLLECTION + ? ELM_COLLECTION_TAG + : ELM_INDEX_TAG, + szLfName); + OutValue( szTmpBuf, szLfName); + + OutUINT( " Logical File Number", uiLfNum); + OutUINT( " Root Block Address", uiRootBlkAddress); + + if (!uiNumLevels) + { + OutUINT( " Levels", uiNumLevels); + } + else + { + OutBlkHeader(); + for( uiLoop = 0; uiLoop < uiNumLevels; uiLoop++) + { + f_sprintf( szTmpBuf, " Level %u", (unsigned)uiLoop); + pDbInfo->getBTreeBlockStats( uiIndex, uiLoop, + &ui64KeyCount, &ui64BytesUsed, + &ui64ElementCount, &ui64ContElementCount, + &ui64ContElmBytes, &uiBlockCount, + &iLastError, &uiNumErrors); + OutOneBlockStat( szTmpBuf, + pDbInfo->getDbHdr()->ui16BlockSize, ui64KeyCount, + ui64BytesUsed, ui64ElementCount, ui64ContElementCount, + ui64ContElmBytes, uiBlockCount, iLastError, uiNumErrors); + } + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void PrintInfo( + IF_DbInfo * pDbInfo + ) +{ + FLMUINT uiLoop; + FLMUINT uiNumLogicalFiles; + FLMUINT64 ui64BytesUsed; + FLMUINT64 ui64ElementCount; + FLMUINT64 ui64ContElementCount; + FLMUINT64 ui64ContElmBytes; + FLMUINT uiBlockCount; + FLMINT iLastError; + FLMUINT uiNumErrors; + const XFLM_DB_HDR * pDbHdr = pDbInfo->getDbHdr(); + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, 0); + + OutUINT( "Default Language", + (FLMUINT)pDbHdr->ui8DefaultLanguage); + OutUINT64( "File Size", pDbInfo->getFileSize()); + OutUINT( "Index Count", pDbInfo->getNumIndexes()); + OutUINT( "Collection Count", + pDbInfo->getNumCollections()); + OutUINT( "Block Size", + (FLMUINT)pDbHdr->ui16BlockSize); + OutLine( "LOG HEADER"); + OutUINT( " First LFH Block Address", + (FLMUINT)pDbHdr->ui32FirstLFBlkAddr); + + OutLine( "MISCELLANEOUS BLOCK STATISTICS"); + OutBlkHeader(); + + ui64ElementCount = 0; + ui64ContElementCount = 0; + ui64ContElmBytes = 0; + pDbInfo->getAvailBlockStats( &ui64BytesUsed, &uiBlockCount, + &iLastError, &uiNumErrors); + if( uiBlockCount) + { + OutOneBlockStat( " Avail", + (FLMUINT)pDbHdr->ui16BlockSize, + 0, ui64BytesUsed, ui64ElementCount, ui64ContElementCount, + ui64ContElmBytes, uiBlockCount, iLastError, uiNumErrors); + } + + ui64ElementCount = 0; + ui64ContElementCount = 0; + ui64ContElmBytes = 0; + pDbInfo->getLFHBlockStats( &ui64BytesUsed, &uiBlockCount, + &iLastError, &uiNumErrors); + if( uiBlockCount) + { + OutOneBlockStat( " LFH", + (FLMUINT)pDbHdr->ui16BlockSize, + 0, ui64BytesUsed, ui64ElementCount, ui64ContElementCount, + ui64ContElmBytes, uiBlockCount, iLastError, uiNumErrors); + } + + uiNumLogicalFiles = pDbInfo->getNumLogicalFiles(); + for( uiLoop = 0; uiLoop < uiNumLogicalFiles; uiLoop++) + { + OutLogicalFile( pDbInfo, uiLoop); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMUINT CheckShowError( + const char * pszMessage, + FLMBOOL bLogIt) +{ + FLMUINT uiResKey; + + f_sprintf( gv_szLastError, "%s", pszMessage); + + if( bLogIt) + { + LogString( pszMessage); + } + + if( gv_bBatchMode) + { + uiResKey = 0; + } + else + { + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( pszMessage); + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsStrOut( "Press ENTER to continue, ESC to quit"); + for( ;;) + { + if( gv_bShutdown) + { + uiResKey = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiResKey = WpkIncar(); + if( (uiResKey == WPK_ENTER) || (uiResKey == WPK_ESCAPE)) + { + break; + } + } + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + } + + return( uiResKey); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void OutLabel( + FLMUINT uiCol, + FLMUINT uiRow, + const char * pszLabel, + const char * pszValue, + FLMUINT64 ui64NumValue, + FLMBOOL bLogIt) +{ + char szTmpBuf[ 100]; + FLMUINT uiLoop; + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsStrOutXY( pszLabel, uiCol, uiRow); + + for( uiLoop = WpsCurrCol(); uiLoop < VALUE_COLUMN - 1; uiLoop++) + { + WpsChrOut( '.'); + } + + + if( pszValue != NULL) + { + DisplayValue( uiRow, pszValue); + } + else + { + DisplayNumValue( uiRow, ui64NumValue); + } + + if( (bLogIt) && (gv_bLoggingEnabled)) + { + f_strcpy( szTmpBuf, pszLabel); + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], ": "); + if( pszValue != NULL) + { + f_strcpy( &szTmpBuf[ f_strlen( szTmpBuf)], pszValue); + } + else + { + f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], + "%I64u", ui64NumValue); + } + LogString( szTmpBuf); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void DisplayValue( + FLMUINT uiRow, + const char * pszValue) +{ + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsStrOutXY( pszValue, VALUE_COLUMN, uiRow); + WpsLineClr( 255, 255); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void DisplayNumValue( + FLMUINT uiRow, + FLMUINT64 ui64Number) +{ + char szTmpBuf[ 128]; + + f_sprintf( szTmpBuf, "%,23I64u 0x%016I64X", + ui64Number, ui64Number); + DisplayValue( uiRow, szTmpBuf); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC RCODE GetUserInput( + void + ) +{ + FLMUINT uiChar; + + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + + WpsStrOut( "Q,ESC=Quit, Other=Continue"); + for( ;;) + { + if( gv_bShutdown) + { + uiChar = WPK_ESCAPE; + break; + } + else if( WpkTestKB()) + { + uiChar = WpkIncar(); + break; + } + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 1)); + + switch( uiChar) + { + case 'q': + case 'Q': + case WPK_ESCAPE: + return( RC_SET( NE_XFLM_FAILURE)); + default: + break; + } + + return( NE_XFLM_OK); +} + +/******************************************************************** +Desc: +*********************************************************************/ +RCODE F_LocalCheckStatus::reportProgress( + XFLM_PROGRESS_CHECK_INFO * pProgCheck) +{ + RCODE rc = NE_XFLM_OK; + XFLM_CACHE_INFO cacheInfo; + char szWhat[ 256]; + char szLfName[ 128]; + F_DbSystem dbSystem; + FLMUINT uiCurrentTime; + + FLM_TIMER_UNITS_TO_MILLI( FLM_GET_TIMER(), uiCurrentTime); + + if( (uiCurrentTime - m_uiLastRefresh < SCREEN_REFRESH_RATE) && + !pProgCheck->bStartFlag) + { + goto Exit; + } + + // We have exceeded our refresh interval or we have changed check phases, + // therefore, we should refresh the screen. + + m_uiLastRefresh = uiCurrentTime; + + dbSystem.getCacheInfo( &cacheInfo); + DisplayNumValue( CACHE_USED_ROW, cacheInfo.BlockCache.uiByteCount); + DisplayNumValue( ECACHE_USED_ROW, cacheInfo.ECache.ui64TotalBytesAllocated); + + if( gv_bShutdown) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Update the display first + + gv_ui64BytesDone = pProgCheck->ui64BytesExamined; + DisplayNumValue( TOTAL_KEYS_ROW, pProgCheck->ui64NumKeys); + DisplayNumValue( TOTAL_DUPS_ROW, pProgCheck->ui64NumDuplicateKeys); + DisplayNumValue( TOTAL_KEYS_EXAM_ROW, pProgCheck->ui64NumKeysExamined); + DisplayNumValue( CONFLICT_ROW, pProgCheck->ui64NumConflicts); + DisplayNumValue( BAD_IXREF_ROW, pProgCheck->ui64NumKeysNotFound); + DisplayNumValue( MISSING_IXREF_ROW, pProgCheck->ui64NumDocKeysNotFound); + + DisplayNumValue( TOTAL_DOM_NODES_ROW, pProgCheck->ui64NumDomNodes); + DisplayNumValue( DOM_LINKS_VERIFIED_ROW, pProgCheck->ui64NumDomLinksVerified); + DisplayNumValue( TOTAL_BROKEN_LINKS_ROW, pProgCheck->ui64NumBrokenDomLinks); + + DisplayNumValue( REPAIR_ROW, pProgCheck->uiNumProblemsFixed); + gv_uiRepairCount = pProgCheck->uiNumProblemsFixed; + + if( pProgCheck->iCheckPhase != XFLM_CHECK_RS_SORT) + { + OutLabel( LABEL_COLUMN, AMOUNT_DONE_ROW, "Bytes Checked", + NULL, gv_ui64BytesDone, FALSE); + } + + if( pProgCheck->bStartFlag) + { + gv_ui64FileSize = pProgCheck->ui64FileSize; + DisplayNumValue( FILE_SIZE_ROW, gv_ui64FileSize); + + switch( pProgCheck->iCheckPhase) + { + case XFLM_CHECK_LFH_BLOCKS: + f_strcpy( szWhat, "LFH BLOCKS"); + break; + case XFLM_CHECK_B_TREE: + *szLfName = '\0'; + if( pProgCheck->uiLfType == XFLM_LF_INDEX) + { + f_strcpy( szWhat, "INDEX: "); + (void)NumToName( pProgCheck->uiLfNumber, + ELM_INDEX_TAG, szLfName); + } + else if( pProgCheck->uiLfType == XFLM_LF_COLLECTION) + { + f_strcpy( szWhat, "COLLECTION: "); + (void)NumToName( pProgCheck->uiLfNumber, + ELM_COLLECTION_TAG, szLfName); + } + else + { + f_strcpy( szWhat, "DICTIONARY: "); + (void)NumToName( pProgCheck->uiLfNumber, + ELM_INDEX_TAG, szLfName); + } + + f_strcpy( &szWhat[ f_strlen( szWhat)], szLfName); + + f_sprintf( &szWhat[ f_strlen( szWhat)], " (%u)", + (unsigned)pProgCheck->uiLfNumber); + szWhat[ 50] = '\0'; + break; + case XFLM_CHECK_AVAIL_BLOCKS: + f_strcpy( szWhat, "AVAIL BLOCKS"); + break; + case XFLM_CHECK_RS_SORT: + f_strcpy( szWhat, "SORTING INDEX KEYS"); + break; + case XFLM_CHECK_DOM_LINKS: + f_strcpy( szWhat, "COLLECTION: "); + (void)NumToName( pProgCheck->uiLfNumber, + ELM_COLLECTION_TAG, szLfName); + f_strcpy( &szWhat[ f_strlen( szWhat)], szLfName); + f_sprintf( &szWhat[ f_strlen( szWhat)], " (%u)", + (unsigned)pProgCheck->uiLfNumber); + szWhat[ 50] = '\0'; + break; + default: + break; + } + + szWhat[ 45] = '\0'; + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + DisplayValue( DOING_ROW, szWhat); + } + else if( (WpkTestKB()) && (WpkIncar() == WPK_ESCAPE)) + { + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + WpsScrPos( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_RED, WPS_WHITE); + WpsStrOut( "ESCAPE key pressed.\n"); + rc = GetUserInput(); + WpsScrClr( 0, (FLMUINT)(gv_uiMaxRow - 2)); + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + goto Exit; + } + +Exit: + + return rc; +} + +RCODE F_LocalCheckStatus::reportCheckErr( + XFLM_CORRUPT_INFO * pCorruptInfo, + FLMBOOL * pbFix) +{ + RCODE rc = NE_XFLM_OK; + XFLM_CACHE_INFO cacheInfo; + F_DbSystem dbSystem; + + dbSystem.getCacheInfo( &cacheInfo); + DisplayNumValue( CACHE_USED_ROW, cacheInfo.BlockCache.uiByteCount); + DisplayNumValue( ECACHE_USED_ROW, cacheInfo.ECache.ui64TotalBytesAllocated); + + if( (gv_bLoggingEnabled) && + ((gv_bShowStats) || + (pCorruptInfo->iErrCode != FLM_OLD_VIEW))) + { + LogCorruptError( pCorruptInfo); + } + + WpsScrBackFor( WPS_BLUE, WPS_WHITE); + if( pCorruptInfo->iErrCode == FLM_OLD_VIEW) + { + gv_uiOldViewCount++; + DisplayNumValue( OLD_VIEW_ROW, gv_uiOldViewCount); + } + else + { + gv_uiCorruptCount++; + gv_uiTotalCorruptions++; + DisplayNumValue( CORRUPT_ROW, gv_uiCorruptCount); + DisplayNumValue( TOTAL_CORRUPT_ROW, gv_uiTotalCorruptions); + } + if (pbFix) + { + *pbFix = gv_bRepairCorruptions; + } + + return( rc); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogStr( + FLMUINT uiIndent, + const char * pszStr) +{ + FLMUINT uiLoop; + + if( gv_bLoggingEnabled) + { + for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) + { + gv_pszLogBuffer[ gv_uiLogBufferCount++] = ' '; + if( gv_uiLogBufferCount == MAX_LOG_BUFF) + { + LogFlush(); + } + } + LogString( pszStr); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogCorruptError( + XFLM_CORRUPT_INFO * pCorrupt) +{ + char szWhat[ 20]; + char szTmpBuf[ 100]; + F_DbSystem dbSystem; + + switch( pCorrupt->uiErrLocale) + { + case XFLM_LOCALE_LFH_LIST: + { + LogStr( 0, "ERROR IN LFH LINKED LIST:"); + break; + } + + case XFLM_LOCALE_AVAIL_LIST: + { + LogStr( 0, "ERROR IN AVAIL LINKED LIST:"); + break; + } + + case XFLM_LOCALE_B_TREE: + { + if( pCorrupt->iErrCode == FLM_OLD_VIEW) + { + LogStr( 0, "OLD VIEW"); + } + else + { + if( pCorrupt->ui64ErrNodeId) + { + f_strcpy( szWhat, "NODE"); + } + else if( pCorrupt->uiErrElmOffset) + { + f_strcpy( szWhat, "ELEMENT"); + } + else if( pCorrupt->uiErrBlkAddress) + { + f_strcpy( szWhat, "BLOCK"); + } + else + { + f_strcpy( szWhat, "LAST BLOCK"); + } + f_sprintf( szTmpBuf, "BAD %s", szWhat); + LogStr( 0, szTmpBuf); + } + + // Log the logical file number, name, and type + + f_sprintf( szTmpBuf, "Logical File Number: %u", + (unsigned)pCorrupt->uiErrLfNumber); + LogStr( 2, szTmpBuf); + + switch( pCorrupt->uiErrLfType) + { + case XFLM_LF_COLLECTION: + { + f_strcpy( szWhat, "Collection"); + break; + } + + case XFLM_LF_INDEX: + { + f_strcpy( szWhat, "Index"); + break; + } + + default: + { + f_sprintf( szWhat, "?%u", + (unsigned)pCorrupt->uiErrLfType); + break; + } + } + + f_sprintf( szTmpBuf, "Logical File Type: %s", szWhat); + LogStr( 2, szTmpBuf); + + // Log the level in the B-Tree, if known + + if( pCorrupt->uiErrBTreeLevel != 0xFF) + { + f_sprintf( szTmpBuf, "Level in B-Tree: %u", + (unsigned)pCorrupt->uiErrBTreeLevel); + LogStr( 2, szTmpBuf); + } + + break; + } + + case XFLM_LOCALE_INDEX: + { + f_strcpy( szWhat, "Index"); + LogKeyError( pCorrupt); + break; + } + + default: + { + pCorrupt->uiErrLocale = 0; + break; + } + } + + // Log the block address, if known + + if( pCorrupt->uiErrBlkAddress) + { + f_sprintf( szTmpBuf, "Block Address: 0x%08X (%u)", + (unsigned)pCorrupt->uiErrBlkAddress, + (unsigned)pCorrupt->uiErrBlkAddress); + LogStr( 2, szTmpBuf); + } + + // Log the parent block address, if known + + if( pCorrupt->uiErrParentBlkAddress) + { + if( pCorrupt->uiErrParentBlkAddress != 0xFFFFFFFF) + { + f_sprintf( szTmpBuf, "Parent Block Address: 0x%08X (%u)", + (unsigned)pCorrupt->uiErrParentBlkAddress, + (unsigned)pCorrupt->uiErrParentBlkAddress); + } + else + { + f_sprintf( szTmpBuf, + "Parent Block Address: NONE, Root Block"); + } + LogStr( 2, szTmpBuf); + } + + // Log the element offset, if known + + if( pCorrupt->uiErrElmOffset != (FLMUINT)~(0)) + { + f_sprintf( szTmpBuf, "Element Offset: %u", + (unsigned)pCorrupt->uiErrElmOffset); + LogStr( 2, szTmpBuf); + } + + // Log the NodeId, if known + + if( pCorrupt->ui64ErrNodeId) + { + f_sprintf( szTmpBuf, + "NodeId: %u", (unsigned)pCorrupt->ui64ErrNodeId); + LogStr( 2, szTmpBuf); + } + + f_strcpy( szTmpBuf, dbSystem.checkErrorToStr( pCorrupt->iErrCode)); + f_sprintf( &szTmpBuf[ f_strlen( szTmpBuf)], " (%d)", + (int)pCorrupt->iErrCode); + LogStr( 2, szTmpBuf); + LogStr( 0, NULL); + + if( gv_bLoggingEnabled) + { + gv_pLogFile->Flush(); + } +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC void LogKeyError( + XFLM_CORRUPT_INFO * pCorrupt) +{ + FLMUINT uiLogItem; + IF_DataVector * ifpKey = NULL; + FLMUINT uiIndent; + FLMUINT uiLevelOffset; + char szNameBuf[ 200]; + char szTmpBuf[ 200]; + FLMUINT uiElementNumber; + + (void)NumToName( pCorrupt->uiErrLfNumber, ELM_INDEX_TAG, szNameBuf); + LogString( NULL); + LogString( NULL); + + f_sprintf( szTmpBuf, "ERROR IN INDEX: %s", szNameBuf); + LogString( szTmpBuf); + + uiLogItem = 'K'; + uiLevelOffset = 0; + for( ;;) + { + uiIndent = 2; + if( uiLogItem == 'K') + { + if( (ifpKey = pCorrupt->ifpErrIxKey) == NULL) + { + uiLogItem = 'L'; + continue; + } + LogString( NULL); + LogString( " PROBLEM KEY"); + } + + uiElementNumber = 0; + while( ifpKey->getNameId( uiElementNumber)) + { + DisplayField( ifpKey, uiElementNumber, uiIndent, uiLevelOffset); + uiElementNumber++; + } + + if( uiLogItem == 'L') + { + break; + } + else + { + uiLogItem = 'L'; + } + } +} + +/*************************************************************************** +Name: DisplayField +Desc: This routine displays a field to the screen. +*****************************************************************************/ +FSTATIC FLMBOOL DisplayField( + IF_DataVector * ifpKey, + FLMUINT uiElementNumber, + FLMUINT uiStartCol, + FLMUINT uiLevelOffset + ) +{ + char szTmpBuf[ 220]; + FLMUINT uiLoop; + FLMUINT uiLen; + FLMUINT uiBinLen; + FLMUINT uiTmpLen; + char * pszTmp; + FLMBYTE * pucTmp; + FLMBYTE ucTmpBin [80]; + FLMUINT uiNum; + FLMUINT uiIndent = (uiLevelOffset * 2) + uiStartCol; + FLMUINT64 ui64NodeId; + + // Insert leading spaces to indent for level + + for( uiLoop = 0; uiLoop < uiIndent; uiLoop++) + { + szTmpBuf[ uiLoop] = ' '; + } + + // Output level and tag + if (ifpKey->isKeyComponent( uiElementNumber)) + { + f_sprintf( &szTmpBuf[ uiIndent], "K) "); + } + else + { + f_sprintf( &szTmpBuf[ uiIndent], "D) "); + } + + (void)NumToName( ifpKey->getNameId( uiElementNumber), + ifpKey->isAttr( uiElementNumber) + ? ELM_ATTRIBUTE_TAG + : ELM_ELEMENT_TAG, + &szTmpBuf[ f_strlen( szTmpBuf)]); + + // Output what will fit of the value on the rest of the line + + uiLen = f_strlen( szTmpBuf); + szTmpBuf[ uiLen++] = ' '; + szTmpBuf[ uiLen] = 0; + if (!ifpKey->getDataLength( uiElementNumber)) + { + goto Exit; + } + switch( ifpKey->getDataType( uiElementNumber)) + { + case XFLM_TEXT_TYPE: + pszTmp = &szTmpBuf[ uiLen]; + uiLen = 80 - uiLen; + ifpKey->getUTF8( uiElementNumber, (FLMBYTE *)pszTmp, &uiLen); + break; + case XFLM_NUMBER_TYPE: + ifpKey->getUINT( uiElementNumber, &uiNum); + f_sprintf( &szTmpBuf [uiLen], "%u", (unsigned)uiNum); + break; + case XFLM_BINARY_TYPE: + ifpKey->getBinary( uiElementNumber, NULL, &uiBinLen); + uiTmpLen = sizeof( ucTmpBin); + ifpKey->getBinary( uiElementNumber, ucTmpBin, &uiTmpLen); + pucTmp = &ucTmpBin [0]; + while (uiBinLen && uiLen < 77) + { + f_sprintf( &szTmpBuf [uiLen], "%02X ", (unsigned)*pucTmp); + uiBinLen--; + pucTmp++; + uiLen += 3; + } + szTmpBuf [uiLen - 1] = 0; + break; + default: + break; + } + + // Get the Id to display + if ((ui64NodeId = ifpKey->getID( uiElementNumber)) != 0) + { + uiLen = f_strlen( szTmpBuf); + f_sprintf( &szTmpBuf[ uiLen], " %I64u", ui64NodeId); + } + + // Output the line + +Exit: + LogString( szTmpBuf); + return( TRUE); +} + +/******************************************************************** +Desc: +*********************************************************************/ +FSTATIC FLMBOOL NumToName( + FLMUINT uiNum, + FLMUINT uiType, + char * pszBuf + ) +{ + FLMUINT uiLen = 128; + + if (gv_pNameTable && + RC_OK( gv_pNameTable->getFromTagTypeAndNum( + gv_pDb, uiType, uiNum, + NULL, pszBuf, &uiLen))) + { + return( TRUE); + } + + switch (uiType) + { + case ELM_INDEX_TAG: + { + if (uiNum == XFLM_DICT_NUMBER_INDEX) + { + f_strcpy( pszBuf, "Dictionary Number Index"); + return( TRUE); + } + else if (uiNum == XFLM_DICT_NAME_INDEX) + { + f_strcpy( pszBuf, "Dictionary Name Index"); + return( TRUE); + } + } + case ELM_COLLECTION_TAG: + { + if (uiNum == XFLM_DATA_COLLECTION) + { + f_strcpy( pszBuf, "Data Collection"); + return( TRUE); + } + else if (uiNum == XFLM_DICT_COLLECTION) + { + f_strcpy( pszBuf, "Dictionary Collection"); + return( TRUE); + } + } + } + + f_sprintf( pszBuf, "#%u", (unsigned)uiNum); + return( FALSE); + +} + +#ifdef FLM_NLM +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +FSTATIC void chkCleanup( + void + ) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + f_yieldCPU(); + } +} +#endif diff --git a/version5/util/colldeftestsrv.cpp b/version5/util/colldeftestsrv.cpp new file mode 100644 index 0000000..7676420 --- /dev/null +++ b/version5/util/colldeftestsrv.cpp @@ -0,0 +1,1016 @@ +//------------------------------------------------------------------------------ +// Desc: Collection definition unit tests +// +// 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: colldeftestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\TST.DB" +#else + #define DB_NAME_STR "tst.db" +#endif + +static FLMBYTE gv_ucLargeBuffer[8192]; + +static void buildLargeBuffer( void); + +/**************************************************************************** +Desc: +****************************************************************************/ +class ICollDefTestImpl : public TestBase +{ +public: + + const char * getName(); + + RCODE execute( void); + +private: + + RCODE importEncDefs( void); + + RCODE importToCollection( + FLMUINT uiDictNum); + + RCODE verifyDocument( + FLMUINT uiDictNum); + + RCODE createDODocument( + FLMUINT uiCollNum, + FLMUINT64 * pui64NodeId); + + RCODE verifyDODocument( + FLMUINT uiCollNum, + FLMUINT64 ui64NodeId); + + FLMUINT m_uiAESDef; + FLMUINT m_uiDES3Def; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if ( ( *ppTest = new ICollDefTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * ICollDefTestImpl::getName( void) +{ + return( "Collection Definition Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::importEncDefs( void) +{ + RCODE rc = NE_XFLM_OK; + char * ppszEncDefs[] = {XFLM_ENC_AES_OPTION_STR, XFLM_ENC_DES3_OPTION_STR}; + FLMUINT puiEncDef[ 2]; + char szEncDef[ 200]; + FLMUINT uiLoop = 0; + + for( + uiLoop = 0; + uiLoop < sizeof(ppszEncDefs)/sizeof(ppszEncDefs[0]); + uiLoop++) + { + f_sprintf( + szEncDef, + "", + ppszEncDefs[uiLoop], + ppszEncDefs[uiLoop]); + + if ( RC_BAD( rc = importBuffer( szEncDef, XFLM_DICT_COLLECTION))) + { + MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); + goto Exit; + } + + f_sprintf( szEncDef, "%s definition", ppszEncDefs[uiLoop]); + if (RC_BAD( rc = m_pDb->getEncDefId( (char *)szEncDef, + (FLMUINT *)&puiEncDef[ uiLoop]))) + { + goto Exit; + } + } + + m_uiAESDef = puiEncDef[ 0]; + m_uiDES3Def = puiEncDef[ 1]; + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::importToCollection( + FLMUINT uiDictNum) +{ + RCODE rc = NE_XFLM_OK; + char szBuffer[ 200]; + FLMBOOL bTransBegun = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + f_sprintf( + szBuffer, + " " + "
" + "\"2999 Birches Lane\"" + "\"Anytown\"" + "\"MyState\"" + "\"86507\"" + "
" + "
"); + + if ( RC_BAD( rc = importBuffer( szBuffer, uiDictNum))) + { + MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + +Exit: + + if (bTransBegun) + { + m_pDb->transAbort(); + } + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::verifyDocument( + FLMUINT uiDictNum) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + char szValue[ 20]; + char szLocalName[ 20]; + FLMUINT uiBufSize = sizeof(szValue); + FLMUINT uiCharsReturned; + + // Get the first ( and only) document + + if (RC_BAD( rc = m_pDb->getFirstDocument( uiDictNum, &pNode))) + { + goto Exit; + } + + // Verify the root node - s/b + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("sample")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szLocalName, "sample")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the child node - s/b
+ + if (RC_BAD( rc = pNode->getFirstChild( m_pDb, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("address")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szLocalName, "address")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the child node - s/b + + if (RC_BAD( rc = pNode->getFirstChild( m_pDb, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("street")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szLocalName, "street")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the value. + + if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, + 0, uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("\"2999 Birches Lane\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szValue, "\"2999 Birches Lane\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the sibling node - s/b + + if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if( uiCharsReturned != f_strlen("city")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if( f_strcmp( szLocalName, "city")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the value. + + if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, + 0, uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("\"AnyTown\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szValue, "\"Anytown\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the sibling node - s/b + + if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("state")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szLocalName, "state")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the value. + + if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, + 0, uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("\"MyState\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szValue, "\"MyState\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the sibling node - s/b + + if (RC_BAD( rc = pNode->getNextSibling( m_pDb, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, uiBufSize, + &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("zip")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szLocalName, "zip")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + // Verify the value. + + if (RC_BAD( rc = pNode->getUTF8( m_pDb, (FLMBYTE *)szValue, uiBufSize, + 0, uiBufSize, &uiCharsReturned))) + { + goto Exit; + } + + if (uiCharsReturned != f_strlen("\"86507\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (f_strcmp( szValue, "\"86507\"")) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + +Exit: + + if (pNode) + { + pNode->Release(); + } + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::createDODocument( + FLMUINT uiCollNum, + FLMUINT64 * pui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + IF_DOMNode * pAttr = NULL; + FLMUINT uiBuffSize = sizeof(gv_ucLargeBuffer); + FLMBOOL bTransBegun = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + // Create a new document + + if (RC_BAD( rc = m_pDb->createRootElement( uiCollNum, ELM_ELEMENT_TAG, + &pNode, pui64NodeId))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->createAttribute( m_pDb, + ATTR_ENCRYPTION_KEY_TAG, &pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->setBinary( m_pDb, gv_ucLargeBuffer, + uiBuffSize, TRUE, 0))) + { + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + +Exit: + + if (pAttr) + { + pAttr->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + if (bTransBegun) + { + m_pDb->transAbort(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::verifyDODocument( + FLMUINT uiCollNum, + FLMUINT64 ui64NodeId) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pNode = NULL; + IF_DOMNode * pAttr = NULL; + FLMBYTE * pucBuff = NULL; + FLMUINT uiLength; + FLMUINT uiLengthRV; + FLMUINT uiLoop; + + // Create a new document + + if (RC_BAD( rc = m_pDb->getDocument( uiCollNum, XFLM_EXACT, + ui64NodeId, &pNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pNode->getAttribute( m_pDb, + ATTR_ENCRYPTION_KEY_TAG, &pAttr))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getDataLength(m_pDb, &uiLength))) + { + goto Exit; + } + + if (uiLength != sizeof( gv_ucLargeBuffer)) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if (RC_BAD( rc = f_alloc( sizeof( gv_ucLargeBuffer), &pucBuff))) + { + goto Exit; + } + + if (RC_BAD( rc = pAttr->getBinary( m_pDb, pucBuff, 0, uiLength, &uiLengthRV))) + { + goto Exit; + } + + if (uiLength != uiLengthRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + for (uiLoop = 0; uiLoop < sizeof( gv_ucLargeBuffer); uiLoop++) + { + if (gv_ucLargeBuffer[ uiLoop] != pucBuff[ uiLoop]) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + } + +Exit: + + if (pucBuff) + { + f_free( &pucBuff); + } + + if (pAttr) + { + pAttr->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ICollDefTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDibCreated = FALSE; + FLMUINT uiAESDictNum = 0; + FLMUINT uiDES3DictNum = 0; + FLMUINT64 ui64AESDoc; + FLMUINT64 ui64DES3Doc; + IF_DOMNode * pNode = NULL; + IF_DOMNode * pAttr = NULL; + IF_DOMNode * pCollDef = NULL; + IF_DOMNode * pTmpNode = NULL; + FLMBOOL bTransBegun = FALSE; + + beginTest( + "Encrypted Collection Definition Test", + "Define encrypted collections tests.", + "1) create a database 2) create required encryption definitions " + "3) create some encrypted collection definitions.", + "none"); + + strcpy (m_szDetails, "No Additional Info."); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, rc); + goto Exit; + } + +#ifdef FLM_USE_NICI + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = importEncDefs())) + { + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; +#endif + + // Start an update transaction + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + bDibCreated = TRUE; + + // Create a collection def + + /* + + */ + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_COLLECTION_TAG, + &pCollDef))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pCollDef->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, + (FLMBYTE *)"Encrypted Collection AES"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + +#ifdef FLM_USE_NICI + if ( RC_BAD( rc = pCollDef->createAttribute( + m_pDb, + ATTR_ENCRYPTION_ID_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUINT( + m_pDb, + m_uiAESDef))) + { + MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } +#endif + + if ( RC_BAD( rc = m_pDb->documentDone( pCollDef))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + + if ( RC_BAD( rc = pCollDef->getAttribute( + m_pDb, + ATTR_DICT_NUMBER_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + // Get the dictionary number (used by later tests) + + if ( RC_BAD( rc = pAttr->getUINT( + m_pDb, + &uiAESDictNum))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + // Create an encrypted collection definition for DES3 encryption + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + /* + + */ + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_COLLECTION_TAG, + &pCollDef))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pCollDef->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( + m_pDb, + (FLMBYTE *)"Encrypted Collection DES3"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + +#ifdef FLM_USE_NICI + if ( RC_BAD( rc = pCollDef->createAttribute( + m_pDb, + ATTR_ENCRYPTION_ID_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUINT( + m_pDb, + m_uiDES3Def))) + { + MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } +#endif + + if ( RC_BAD( rc = m_pDb->documentDone( pCollDef))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pCollDef->getAttribute( + m_pDb, + ATTR_DICT_NUMBER_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + // Get the dictionary number (used by later tests) + + if ( RC_BAD( rc = pAttr->getUINT( + m_pDb, + &uiDES3DictNum))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + endTest("PASS"); + + beginTest( "Import to Encrypted Collections Tests", + "Verify that we can import data into an encrypted collection", + "Import a document into each of the previously defined collections." + " Retrieve them and verify them against the original document.", + "none"); + + if (RC_BAD( rc = importToCollection( uiAESDictNum))) + { + goto Exit; + } + + if (RC_BAD( rc = importToCollection( uiDES3DictNum))) + { + goto Exit; + } + + // Close the database, reopen it and verify the documents. + + if( pNode) + { + pNode->Release(); + pNode = NULL; + } + + if( pAttr) + { + pAttr->Release(); + pAttr = NULL; + } + + if( pCollDef) + { + pCollDef->Release(); + pCollDef = NULL; + } + + if( pTmpNode) + { + pTmpNode->Release(); + pTmpNode = NULL; + } + + if( m_pDb->Release()) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + m_pDb = NULL; + + // Open the database. + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, NULL, + FALSE, &m_pDb))) + { + goto Exit; + } + + // Verify the documents at a time and compare them. + + if (RC_BAD( rc = verifyDocument( uiAESDictNum))) + { + goto Exit; + } + + if (RC_BAD( rc = verifyDocument( uiDES3DictNum))) + { + goto Exit; + } + + endTest( "PASS"); + + beginTest( "Encrypted Data Only Block Test", + "Verify that we can encrypt the data only blocks that are part of an encrypted collection", + "Create a document with a very large value, then retrieve and verify it.", + "none"); + + buildLargeBuffer(); + + if (RC_BAD( rc = createDODocument( uiAESDictNum, &ui64AESDoc))) + { + goto Exit; + } + + if (RC_BAD( rc = createDODocument( uiDES3DictNum, &ui64DES3Doc))) + { + goto Exit; + } + + // Close the database, reopen it and verify the documents. + + if ( m_pDb->Release()) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + m_pDb = NULL; + + // Open the database. + + if( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + goto Exit; + } + + if( RC_BAD( rc = verifyDODocument( uiAESDictNum, ui64AESDoc))) + { + goto Exit; + } + + if( RC_BAD( rc = verifyDODocument( uiDES3DictNum, ui64DES3Doc))) + { + goto Exit; + } + + endTest( "PASS"); + +Exit: + + if ( RC_BAD( rc)) + { + endTest( "FAIL"); + } + + if( pNode) + { + pNode->Release(); + } + + if( pAttr) + { + pAttr->Release(); + } + + if( pCollDef) + { + pCollDef->Release(); + } + + if( pTmpNode) + { + pTmpNode->Release(); + } + + if( bTransBegun) + { + if( RC_OK( rc)) + { + m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +static void buildLargeBuffer( void) +{ + FLMUINT uiLoop; + + for (uiLoop = 0; uiLoop < sizeof(gv_ucLargeBuffer); uiLoop++) + { + gv_ucLargeBuffer[ uiLoop] = (FLMBYTE)(uiLoop & 0xFF); + } +} diff --git a/version5/util/dbdiff.cpp b/version5/util/dbdiff.cpp new file mode 100644 index 0000000..86ce855 --- /dev/null +++ b/version5/util/dbdiff.cpp @@ -0,0 +1,775 @@ +//------------------------------------------------------------------------------ +// Desc: Utility to compare two databases for equivalence. +// +// 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: dbdiff.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "sharutil.h" +#include "dbdiff.h" + +/**************************************************************************** +Name: compareNodes +Desc: method to compare two F_DOMNodes. +****************************************************************************/ +RCODE F_DbDiff::compareNodes( + FLMBYTE * pszCompareInfo, //info about the records for output + F_DOMNode * pNode1, + F_DOMNode * pNode2, + DBDIFF_CALLBACK outputCallback, + void * pvData) +{ + RCODE rc = NE_XFLM_OK; + FlmStringAcc acc; + char szErrBuff[ 100]; + + if (pNode1->compareNode( + pNode2, + m_pDb1, + m_pDb2, + &szErrBuff[0], + sizeof( szErrBuff)) != 0) + { + acc.printf( + "ERROR: {%s} %s\n", pszCompareInfo, szErrBuff); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + +Exit: + + return rc; +} + +/**************************************************************************** +Name: compareCollections +Desc: compare two databases' Collection +****************************************************************************/ +RCODE F_DbDiff::compareCollections( + FLMUINT uiCollection, + DBDIFF_CALLBACK outputCallback, + void * pvData) +{ + FLMUINT64 ui64NodeId1 = 0; + FLMUINT64 ui64NodeId2 = 0; + F_DOMNode * pNode1 = NULL; + F_DOMNode * pNode2 = NULL; + FLMBOOL bScanFinished1 = FALSE; + FLMBOOL bScanFinished2 = FALSE; + FlmStringAcc acc; + RCODE rc = NE_XFLM_OK; + + if ( uiCollection == XFLM_MAINT_COLLECTION) + { + // Ignore any differences in this collection + goto Exit; + } + + while ( (!bScanFinished1) && (!bScanFinished2)) + { + if ( RC_BAD( rc = m_pDb1->getNextNode( uiCollection, + ui64NodeId1, + &pNode1))) + { + if ( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + bScanFinished1 = TRUE; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode1->getNodeId( m_pDb1, &ui64NodeId1))) + { + goto Exit; + } + } + + if ( RC_BAD( rc = m_pDb2->getNextNode( uiCollection, + ui64NodeId2, + &pNode2))) + { + if ( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + bScanFinished2 = TRUE; + } + else + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode1->getNodeId( m_pDb2, &ui64NodeId2))) + { + goto Exit; + } + } + + if ( bScanFinished1 && bScanFinished2) + { + break; + } + + //if one of them is finished and not the other one + if (bScanFinished1 != bScanFinished2) + { + FlmStringAcc acc; + acc.appendf( + "ERROR: database1 and database2 have a different # of nodes in " + "Collection %u\n", uiCollection); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + //if the nodeId's are different, then there's a problem + if ( ui64NodeId1 != ui64NodeId2) + { + FlmStringAcc acc; + acc.appendf( "ERROR: database1's nodeId %u ", ui64NodeId1); + acc.appendf( " != database2's nodeId %u!\n", ui64NodeId2); + acc.appendf( "ERROR: nodeId mismatch in Collection %u\n", uiCollection); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if ( !bScanFinished1) //bScanFinished1==bScanFinished2 at this point + { + acc.printf( "NodeId %u of Collection %u", ui64NodeId1, uiCollection); + //compare the two records for accuracy + if ( RC_BAD( rc = compareNodes( (FLMBYTE *)acc.getTEXT(), + pNode1, + pNode2, + outputCallback, + pvData))) + { + goto Exit; + } + } +#ifdef FLM_NLM + f_yieldCPU(); +#else + f_sleep( 0); +#endif + } + +Exit: + + if ( pNode1) + { + pNode1->Release(); + } + if ( pNode2) + { + pNode2->Release(); + } + return rc; +} + + +/**************************************************************************** +Name: flmDbDiff +Desc: compare two databases for logical equivalence. +****************************************************************************/ +RCODE F_DbDiff::diff( + char * pszDb1, + char * pszDb1Password, + char * pszDb2, + char * pszDb2Password, + DBDIFF_CALLBACK outputCallback, + void * pvData) +{ + RCODE rc = NE_XFLM_OK; + RCODE tmpRc = NE_XFLM_OK; + FLMBOOL bTransActive1 = FALSE; + FLMBOOL bTransActive2 = FALSE; + F_DOMNode * pNode1 = NULL; + F_DOMNode * pNode2 = NULL; + FlmVector CollectionVec1; + FlmVector CollectionVec2; + FLMUINT uiCollectionLen1 = 0; + FLMUINT uiCollectionLen2 = 0; + FLMUINT uiIndexLen1 = 0; + FLMUINT uiIndexLen2 = 0; + FLMUINT uiLoop; + F_COLLECTION * pCollection1; + F_COLLECTION * pCollection2; + F_Dict * pDict1; + F_Dict * pDict2; + FLMUINT uiCollectionNum; + FLMUINT uiIndexNum; + IXD * pIxd; + FlmStringAcc acc; + FlmVector IndexVec1; + FlmVector IndexVec2; + F_DbSystem dbSystem; + + //try to unlock any files currently open + if ( RC_BAD( rc = dbSystem.closeUnusedFiles(0))) + { + goto Exit; + } + + //can't compare a db with itself + if ( f_strcmp( pszDb1, pszDb2) == 0) + { + outputCallback( "ERROR: cannot compare a dib with itself!\n", pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + //open up the database, and all that it entails + if( RC_BAD( rc = dbSystem.openDb( pszDb1, + NULL, + NULL, + pszDb1Password, + XFLM_DONT_RESUME_THREADS, + (IF_Db **)&m_pDb1))) + { + acc.printf( "ERROR: cannot open dib '"); + acc.appendTEXT( pszDb1); + acc.appendTEXT( "'!\n"); + outputCallback((char*)acc.getTEXT(), pvData); + goto Exit; + } + if( RC_BAD( rc = dbSystem.openDb( pszDb2, + NULL, + NULL, + pszDb2Password, + XFLM_DONT_RESUME_THREADS, + (IF_Db **)&m_pDb2))) + { + acc.printf( "ERROR: cannot open dib '"); + acc.appendTEXT( pszDb2); + acc.appendTEXT( "'!\n"); + outputCallback((char*)acc.getTEXT(), pvData); + goto Exit; + } + if( RC_BAD( rc = m_pDb1->transBegin( XFLM_READ_TRANS))) + { + outputCallback( "ERROR: cannot start trans #1\n", pvData); + goto Exit; + } + bTransActive1 = TRUE; + if( RC_BAD( rc = m_pDb2->transBegin( XFLM_READ_TRANS))) + { + outputCallback( "ERROR: cannot start trans #2\n", pvData); + goto Exit; + } + bTransActive2 = TRUE; + + if (RC_BAD( rc = m_pDb1->getDictionary( &pDict1))) + { + outputCallback( "ERROR: retrieving dictionary #1\n", pvData); + goto Exit; + } + + if (RC_BAD( rc = m_pDb2->getDictionary( &pDict2))) + { + outputCallback( "ERROR: retrieving dictionary #2\n", pvData); + goto Exit; + } + + //prepare to read Collections from the dictionary + if (RC_BAD( rc = pDict1->getCollection( XFLM_DICT_COLLECTION, + &pCollection1))) + { + outputCallback( "ERROR: retrieving dictionary collection #1\n", pvData); + goto Exit; + } + + if (RC_BAD( rc = pDict2->getCollection( XFLM_DICT_COLLECTION, + &pCollection2))) + { + outputCallback( "ERROR: retrieving dictionary collection #2\n", pvData); + goto Exit; + } + + outputCallback( "Starting dbdiff with databases '", pvData); + outputCallback( (char*)pszDb1, pvData); + outputCallback( "' and '", pvData); + outputCallback( (char*)pszDb2, pvData); + outputCallback( "'\n", pvData); + outputCallback( "Building index and Collection lists...", pvData); + + //build up a list of each Collection from the first database + for (uiCollectionNum = 0;;) + { + if ((pCollection1 = pDict1->getNextCollection( uiCollectionNum, + TRUE)) == NULL) + { + break; + } + uiCollectionNum = pCollection1->lfInfo.uiLfNum; + if ( RC_BAD( rc = CollectionVec1.setElementAt( (void*)uiCollectionNum, + uiCollectionLen1++))) + { + goto Exit; + } + } + + //build up a list of each Collection from the second database + for (uiCollectionNum = 0;;) + { + if ((pCollection2 = pDict2->getNextCollection( uiCollectionNum, + TRUE)) == NULL) + { + break; + } + uiCollectionNum = pCollection2->lfInfo.uiLfNum; + if ( RC_BAD( rc = CollectionVec2.setElementAt( (void*)uiCollectionNum, + uiCollectionLen2++))) + { + goto Exit; + } + } + + //must have same # of Collections to be the same + if ( uiCollectionLen1 != uiCollectionLen2) + { + acc.printf( + "ERROR: the two databases have a different number of Collections(" + "%u,%u)!\n", uiCollectionLen1, uiCollectionLen2); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + //must have same # of indexes to be the same + //build up a list of each Collection from the first database + for (uiIndexNum = 0;;) + { + if ((pIxd = pDict1->getNextIndex( uiIndexNum, + TRUE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + IndexVec1.setElementAt( (void*)uiIndexNum, uiIndexLen1++); + } + + for (uiIndexNum = 0;;) + { + if ((pIxd = pDict2->getNextIndex( uiIndexNum, + TRUE)) == NULL) + { + break; + } + uiIndexNum = pIxd->uiIndexNum; + IndexVec2.setElementAt( (void*)uiIndexNum, uiIndexLen2++); + } + + if ( uiIndexLen1 != uiIndexLen2) + { + acc.printf( + "ERROR: the two databases have a different number of indexes(" + "%u,%u)!\n", uiIndexLen1, uiIndexLen2); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + outputCallback( "done\n", pvData); + outputCallback( "comparing collections\n", pvData); + + //loop through each Collection and compare the two + for ( uiLoop = 0; uiLoop < uiCollectionLen1; uiLoop++) + { + FLMUINT uiCollection1 = (FLMUINT)CollectionVec1.getElementAt( uiLoop); + FLMUINT uiCollection2 = (FLMUINT)CollectionVec2.getElementAt( uiLoop); + + if ( uiCollection1 != uiCollection2) + { + acc.printf( "ERROR: database1's Collection (%u) != " + "database2's Collection (%u)!\n", uiCollection1, uiCollection2); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + else + { + acc.printf( "(%4u of %4u) processing Collection #%u\n", + uiLoop+1, uiCollectionLen1, uiCollection1); + outputCallback( (char*)acc.getTEXT(), pvData); + if ( RC_BAD( rc = compareCollections( uiCollection1, + //uiCollection1==uiCollection2 at this point + outputCallback, + pvData))) + { + goto Exit; + } + } + } + + // Compare index keys + + outputCallback( "done\n", pvData); + outputCallback( "comparing indexes\n", pvData); + + for( uiLoop = 0; uiLoop < uiIndexLen1; uiLoop++) + { + uiIndexNum = (FLMUINT)IndexVec1.getElementAt( uiLoop); + + if ( uiIndexNum != (FLMUINT)IndexVec2.getElementAt( uiLoop)) + { + acc.printf( "ERROR: database1's Index (%u) != " + "database2's Index (%u)!\n", uiIndexNum, IndexVec2.getElementAt( uiLoop)); + outputCallback( (char*)acc.getTEXT(), pvData); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + acc.printf( "(%5u of %5u) processing Index #%u\n", + uiLoop + 1, uiIndexLen1, uiIndexNum); + outputCallback( acc.getTEXT(), pvData); + + if ( RC_BAD( rc = compareIndexes( uiIndexNum, outputCallback, pvData))) + { + goto Exit; + } + } + +Exit: + + if (pNode1) + { + pNode1->Release(); + } + if (pNode2) + { + pNode2->Release(); + } + + if ( bTransActive1) + { + if ( RC_OK( rc)) + { + rc = m_pDb1->transCommit(); + } + else + { + (void)m_pDb1->transAbort(); + } + } + if ( bTransActive2) + { + if ( RC_OK( rc)) + { + rc = m_pDb2->transCommit(); + } + else + { + (void)m_pDb2->transAbort(); + } + } + //close any files currently open. This is so any smi open can be + //done following this in the same process. + tmpRc = dbSystem.closeUnusedFiles(0); + if ( RC_OK( rc)) + { + rc = tmpRc; + } + return rc; +} + +RCODE F_DbDiff::compareIndexes( + FLMUINT uiIndexNum, + DBDIFF_CALLBACK outputCallback, + void * pvData) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector searchKey1; + F_DataVector searchKey2; + FLMBOOL bDataComp; + FLMBOOL bKeyComp; + FLMUINT uiLoop; + FLMUINT uiLoop2; + FLMUINT uiDataType; + FLMUINT uiDataLen1; + FLMUINT uiDataLen2; + FLMBYTE * pucVal1 = NULL; + FLMBYTE * pucVal2 = NULL; + FLMUINT64 ui64Val1 = 0; + FLMUINT64 ui64Val2 = 0; + + for( uiLoop = 0;; uiLoop++) + { + if ( uiLoop == 0) + { + if (RC_BAD( rc = m_pDb1->keyRetrieve( + uiIndexNum, NULL, XFLM_FIRST, &searchKey1))) + { + if ( rc == NE_XFLM_EOF_HIT) + { + // empty index. Make sure the other index is empty too. + + if (( rc = m_pDb2->keyRetrieve( + uiIndexNum, NULL, XFLM_FIRST, &searchKey2)) != NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"key mismatch found.", pvData); + goto Exit; + } + } + rc = NE_XFLM_OK; + goto Exit; + } + + if (RC_BAD( rc = m_pDb2->keyRetrieve( + uiIndexNum, NULL, XFLM_FIRST, &searchKey2))) + { + outputCallback( (char*)"keyRetrieve failed.", pvData); + goto Exit; + } + + if ( searchKey1.getDocumentID() != searchKey2.getDocumentID()) + { + // Error + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"index document id mismatch.", pvData); + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDb1->keyRetrieve( + uiIndexNum, &searchKey1, XFLM_EXCL, &searchKey1))) + { + if ( rc == NE_XFLM_EOF_HIT) + { + // No more keys. Make sure the other index is at the end too. + + if (( rc = m_pDb2->keyRetrieve( + uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2)) != NE_XFLM_EOF_HIT) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"key mismatch found.", pvData); + goto Exit; + } + } + rc = NE_XFLM_OK; + goto Exit; + } + + if (RC_BAD( rc = m_pDb2->keyRetrieve( + uiIndexNum, &searchKey2, XFLM_EXCL, &searchKey2))) + { + outputCallback( (char*)"keyRetrieve failed.", pvData); + goto Exit; + } + } + + for( uiLoop2 = 0;; uiLoop2++) + { + if ( ( bDataComp = searchKey1.isDataComponent(uiLoop2)) != + searchKey2.isDataComponent(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"isDataComponent mismatch.", pvData); + goto Exit; + } + + if ( ( bKeyComp = searchKey1.isKeyComponent(uiLoop2)) != + searchKey2.isKeyComponent(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"isKeyComponent mismatch.", pvData); + goto Exit; + } + + if ( !bDataComp && !bKeyComp) + { + // No more components to check + break; + } + + if ( searchKey1.isRightTruncated( uiLoop2) != + searchKey2.isRightTruncated( uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"isRightTruncated mismatch.", pvData); + goto Exit; + } + + if ( searchKey1.isLeftTruncated( uiLoop2) != + searchKey2.isLeftTruncated( uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"isLeftTruncated mismatch.", pvData); + goto Exit; + } + + if ( searchKey1.getID(uiLoop2) != searchKey2.getID(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"node id mismatch.", pvData); + goto Exit; + } + + if ( searchKey1.getNameId(uiLoop2) != searchKey2.getNameId(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"name id mismatch.", pvData); + goto Exit; + } + + if ( searchKey1.isAttr(uiLoop2) != searchKey2.isAttr(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"isAttr mismatch.", pvData); + goto Exit; + } + + if ( ( uiDataType = searchKey1.getDataType(uiLoop2)) != + searchKey2.getDataType(uiLoop2)) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"data type mismatch.", pvData); + goto Exit; + } + + if ( ( uiDataLen1 = searchKey1.getDataLength(uiLoop2)) != + ( uiDataLen2 = searchKey2.getDataLength(uiLoop2))) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"data length mismatch.", pvData); + goto Exit; + } + + if ( uiDataType == XFLM_TEXT_TYPE || uiDataType == XFLM_BINARY_TYPE) + { + if ( pucVal1) + { + f_free( &pucVal1); + } + + if ( RC_BAD( rc = f_alloc( uiDataLen1, &pucVal1))) + { + goto Exit; + } + + if ( pucVal2) + { + f_free( &pucVal2); + } + + if ( RC_BAD( rc = f_alloc( uiDataLen2, &pucVal2))) + { + goto Exit; + } + } + + switch( uiDataType) + { + case XFLM_NUMBER_TYPE: + { + if ( RC_BAD( rc = searchKey1.getUINT64( uiLoop2, &ui64Val1))) + { + outputCallback( (char*)"getUINT64 failed.", pvData); + goto Exit; + } + + if ( RC_BAD( rc = searchKey2.getUINT64( uiLoop2, &ui64Val2))) + { + outputCallback( (char*)"getUINT64 failed.", pvData); + goto Exit; + } + + if ( ui64Val1 != ui64Val2) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"UINT64 val mismatch.", pvData); + goto Exit; + } + break; + } + case XFLM_BINARY_TYPE: + { + if ( RC_BAD( rc = searchKey1.getBinary( uiLoop2, pucVal1, &uiDataLen1))) + { + outputCallback( (char*)"getBinary failed.", pvData); + goto Exit; + } + + if ( RC_BAD( rc = searchKey2.getBinary( uiLoop2, pucVal2, &uiDataLen2))) + { + outputCallback( (char*)"getBinary failed.", pvData); + goto Exit; + } + + if ( f_memcmp( pucVal1, pucVal2, uiDataLen2) != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"binary val mismatch.", pvData); + goto Exit; + } + break; + } + case XFLM_TEXT_TYPE: + { + if ( RC_BAD( rc = searchKey1.getUTF8( uiLoop2, pucVal1, &uiDataLen1))) + { + outputCallback( (char*)"getUTF8 failed.", pvData); + goto Exit; + } + + if ( RC_BAD( rc = searchKey2.getUTF8( uiLoop2, pucVal2, &uiDataLen2))) + { + outputCallback( (char*)"getUTF8 failed.", pvData); + goto Exit; + } + + if ( f_strcmp( pucVal1, pucVal2) != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + outputCallback( (char*)"text val mismatch.", pvData); + goto Exit; + } + break; + } + default: + break; + } + } + } +Exit: + + if ( pucVal1) + { + f_free( &pucVal1); + } + + if ( pucVal2) + { + f_free( &pucVal2); + } + + return rc; +} diff --git a/version5/util/dbdiff.h b/version5/util/dbdiff.h new file mode 100644 index 0000000..ce0d987 --- /dev/null +++ b/version5/util/dbdiff.h @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// Desc: Utility to compare two databases for equivalence. +// +// 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: dbdiff.h 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +/*============================================================================= +Desc: Define the F_DbDiff class - also note the need for the call back + definition. +*============================================================================*/ +typedef void (* DBDIFF_CALLBACK) ( + char * pszOutput, + void * pvData); + + +class F_DbDiff +{ +public: + + F_DbDiff() + { + m_pDb1 = m_pDb2 = NULL; + } + + ~F_DbDiff() + { + // Close the databases if they are open. + if ( m_pDb1 != NULL) + { + m_pDb1->Release(); + } + if ( m_pDb2 != NULL) + { + m_pDb2->Release(); + } + } + + RCODE diff( + char * pszDb1, + char * pszDb1Password, + char * pszDb2, + char * pszDb2Password, + DBDIFF_CALLBACK outputCallback, + void * pvData); + +private: + + RCODE compareCollections( + FLMUINT uiCollection, + DBDIFF_CALLBACK outputCallback, + void * pvData); + + RCODE compareIndexes( + FLMUINT uiIndexNum, + DBDIFF_CALLBACK outputCallback, + void * pvData); + + RCODE compareNodes( + FLMBYTE * pszCompareInfo, + F_DOMNode * pNode1, + F_DOMNode * pNode2, + DBDIFF_CALLBACK outputCallback, + void * pvData); + + F_Db * m_pDb1; + F_Db * m_pDb2; +}; diff --git a/version5/util/dictchangetest.cpp b/version5/util/dictchangetest.cpp new file mode 100644 index 0000000..2599ee5 --- /dev/null +++ b/version5/util/dictchangetest.cpp @@ -0,0 +1,363 @@ +//------------------------------------------------------------------------------ +// Desc: Dictionary change tests +// +// 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: dictchangetest.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\TST.DB" +#else + #define DB_NAME_STR "tst.db" +#endif + +#define NAMESPACE "http://www.bogusnamespace.com" +#define MAX_POLL_COUNT 100 + +/**************************************************************************** +Desc: +****************************************************************************/ +class IDictChangeTestImpl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IDictChangeTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IDictChangeTestImpl::getName( void) +{ + return( "Dict Change Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDictChangeTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDibCreated = FALSE; + FLMUINT uiElmId = 0; + IF_DOMNode * pNode = NULL; + IF_DOMNode * pDictDef = NULL; + IF_DOMNode * pDoc = NULL; + IF_DOMNode * pMyAttrNode = NULL; + IF_DOMNode * pMyEntryNode = NULL; + char szLocalName[100]; + FLMUINT uiNumCharsReturned = 0; + FLMBOOL bTransStarted = FALSE; + FLMUINT uiMyEntryId = 0; + FLMUINT uiMyAttrId = 0x80000001; + FLMUINT64 ui64EntryId; + + beginTest( + "Dictionary Item Change Test", + "Ensure we can change the name of an element definition then reuse the old name", + "Create the database/create an element definition/" + "create root element/rename element/set element to \"purge\"/" + "reuse the old element name", + ""); + + if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_ERROR_STRING( "Failed to init test state.", m_szDetails, rc); + goto Exit; + } + bDibCreated = TRUE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if( RC_BAD( rc = m_pDb->createElementDef( NAMESPACE, "foo", XFLM_TEXT_TYPE, + &uiElmId))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, uiElmId, + &pNode))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pNode->setUTF8( m_pDb, (FLMBYTE *)"value1"))) + { + MAKE_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); + goto Exit; + } + + // change the "foo" element's name then set its state to "purge" + + if( RC_BAD( rc = m_pDb->getDictionaryDef( ELM_ELEMENT_TAG, + uiElmId, &pDictDef))) + { + MAKE_ERROR_STRING( "getDictionaryDef changed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pDictDef->setAttributeValueUTF8( + m_pDb, ATTR_NAME_TAG, (FLMBYTE *)"deleted_foo"))) + { + MAKE_ERROR_STRING( "setAttributeValueUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->documentDone( pDictDef))) + { + MAKE_ERROR_STRING( "documentDone failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->changeItemState( ELM_ELEMENT_TAG, + uiElmId, XFLM_PURGE_OPTION_STR))) + { + MAKE_ERROR_STRING( "changeItemState failed", m_szDetails, rc); + goto Exit; + } + + uiElmId = 0; + if ( RC_BAD( rc = m_pDb->createElementDef( NAMESPACE, "foo", + XFLM_NUMBER_TYPE, &uiElmId))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, + uiElmId, &pNode))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->getLocalName( m_pDb, szLocalName, + sizeof( szLocalName), &uiNumCharsReturned))) + { + MAKE_ERROR_STRING( "getLocalName failed.", m_szDetails, rc); + goto Exit; + } + + if( bTransStarted) + { + if( RC_BAD( rc)) + { + m_pDb->transAbort(); + } + else + { + m_pDb->transCommit(); + } + } + + endTest("PASS"); + + beginTest( + "Dictionary Attribute Purge Test", + "Verify that setting a definition to \"purge\" works correctly", + "Create the database/create an attribute definition/" + "Create element/add an attribute/set attribute def to \"purge\"/" + "Test for presence of attribute definition", + ""); + + if( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if( RC_BAD( rc = m_pDb->createElementDef( NULL, "MyElement", + XFLM_NODATA_TYPE, &uiMyEntryId))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "MyAttribute", + XFLM_NUMBER_TYPE, &uiMyAttrId))) + { + MAKE_FLM_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createDocument( XFLM_DATA_COLLECTION, &pDoc))) + { + MAKE_FLM_ERROR_STRING( "createDocument failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDoc->createNode( m_pDb, ELEMENT_NODE, uiMyEntryId, + XFLM_LAST_CHILD, &pMyEntryNode, &ui64EntryId))) + { + MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pMyEntryNode->createAttribute( m_pDb, uiMyAttrId, + &pMyAttrNode))) + { + MAKE_FLM_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pMyAttrNode->setUINT( m_pDb, 0x18332))) + { + MAKE_FLM_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pMyEntryNode->setUINT64( m_pDb, 34))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDb->documentDone( pDoc))) + { + MAKE_FLM_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + m_pDb->transCommit(); + bTransStarted = FALSE; + + // Start a transaction and set the state of the uiMyAttrId to "purge" + + if( RC_BAD( rc = m_pDb->transBegin(XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if( RC_BAD( rc = m_pDb->changeItemState( ELM_ATTRIBUTE_TAG, + uiMyAttrId, XFLM_PURGE_OPTION_STR))) + { + MAKE_ERROR_STRING( "changeItemState failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( m_pDb->transCommit())) + { + goto Exit; + } + + bTransStarted = FALSE; + + for( FLMINT iCount = 0; iCount < MAX_POLL_COUNT; iCount++) + { + if( RC_BAD( rc = m_pDb->getDictionaryDef( ELM_ATTRIBUTE_TAG, + uiMyAttrId, &pMyAttrNode))) + { + break; + } + f_sleep( 100); + } + + if( rc != NE_XFLM_NOT_FOUND) + { + MAKE_ERROR_STRING( "purge attribute failed", m_szDetails, rc); + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + + endTest("PASS"); + m_pDb->doCheckpoint( 0); + +Exit: + + if( bTransStarted) + { + if( RC_BAD( rc)) + { + m_pDb->transAbort(); + } + else + { + m_pDb->transCommit(); + } + } + + if( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if( pNode) + { + pNode->Release(); + } + + if( pMyEntryNode) + { + pMyEntryNode->Release(); + } + + if( pMyAttrNode) + { + pMyAttrNode->Release(); + } + + if( pDoc) + { + pDoc->Release(); + } + + if ( pDictDef) + { + pDictDef->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return rc; +} diff --git a/version5/util/dictdeftestsrv.cpp b/version5/util/dictdeftestsrv.cpp new file mode 100644 index 0000000..a67ff92 --- /dev/null +++ b/version5/util/dictdeftestsrv.cpp @@ -0,0 +1,1348 @@ +//------------------------------------------------------------------------------ +// Desc: Dictionary definition tests +// +// 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: dictdeftestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\TST.DB" +#else + #define DB_NAME_STR "tst.db" +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +class IDictDefTestImpl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IDictDefTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IDictDefTestImpl::getName( void) +{ + return( "Dictionary Definition Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDictDefTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + int ps = 0; + FLMUINT uiDefNum = 0; + IF_DOMNode * pDocument = NULL; + IF_DOMNode * pNode = NULL; + IF_DOMNode * pAttr = NULL; + IF_DOMNode * pAttrDef = NULL; + IF_DOMNode * pElemDef = NULL; + IF_DOMNode * pIndex = NULL; + IF_DOMNode * pCollection = NULL; + FLMUINT uiAttrDictNum = 0; + FLMUINT uiElemDictNum = 0; + FLMUINT uiIndexDictNum = 0; + FLMBYTE szBuf[ 128]; + FLMBOOL bTransStarted = FALSE; + FLMBOOL bDibCreated = FALSE; + FLMUINT64 ui64Tmp; + + beginTest( + "Initialize FLAIM Test", + "Perform initializations so we can test the dictionary definition", + "(1)Get DbSystem class factory (2)call init() (3)create XFLAIM db", + "No additional info."); + + if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, ps); + goto Exit; + } + bDibCreated = TRUE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "Type Attribute Test", + "Verify that deleting or modifying of the \"type\"" + "attribute (ATTR_TYPE_TAG) of an element or attribute definition is " + "denied with a proper error code", + "(1) Create an element definition (2)Verify the node has a " + "\"type\" attribute (3)Try to set it to an arbitrary value (4)Try " + "to delete it.", + ""); + + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, + "element1", + XFLM_TEXT_TYPE, + &uiDefNum))) + { + MAKE_ERROR_STRING( "createElemDef failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->getDictionaryDef( + ELM_ELEMENT_TAG, + uiDefNum, + &pNode))) + { + MAKE_ERROR_STRING( "getDictionaryDef failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->hasAttribute( + m_pDb, + ATTR_TYPE_TAG, + &pAttr))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "\"Type\" attribute not automatically created.", + m_szDetails, rc); + } + else + { + MAKE_ERROR_STRING( "hasAttribute failed.", m_szDetails, rc); + } + goto Exit; + } + + rc = pAttr->setUINT( + m_pDb, + 8); + if ( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "Succeeded in modifying \"type\" attribute.", + m_szDetails, rc); + + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + + goto Exit; + } + + // need to begin a new transaction since attempting to delete + // the Attribute node will probably require the transaction + // to be aborted and we don't want to lose our modifications + + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + rc = pAttr->deleteNode(m_pDb); + if( rc != NE_XFLM_DELETE_NOT_ALLOWED) + { + MAKE_ERROR_STRING( "Incorrect rc when trying to delete \"type\" attribute.", + m_szDetails, rc); + + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + + goto Exit; + } + + // The failed delete requires us to abort the trans + // NOTE: This may no longer be necessary in the future + + if ( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "Name Attribute Test", + "Verify that deleting of the \"name\" " + "attribute (ATTR_NAME_TAG) of any definition is denied with a proper " + "error code. Verify that modifying the name is allowed but deleting " + "it should be denied.", + "(1) Verify the node has a \"name\" attribute" + "(2) Set it to an arbitrary value (3)Try to delete it.", + ""); + + if( RC_BAD( rc = pNode->hasAttribute( m_pDb, ATTR_NAME_TAG))) + { + MAKE_ERROR_STRING( "hasAttribute failed. \"name\" attribute not created" + " automatically.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pNode->getAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + // This attribute can be modified... + + if( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"This should be okay"))) + { + MAKE_ERROR_STRING( "failed to modify \"name\" attribute.", m_szDetails, rc); + goto Exit; + } + + // Need to begin a new transaction since attempting to delete + // the Attribute node will probably require the transaction + // to be aborted and we don't want to lose our modifications + + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + rc = pAttr->deleteNode(m_pDb); + if( rc != NE_XFLM_DELETE_NOT_ALLOWED) + { + MAKE_ERROR_STRING( "Succeeded in deleting \"name\" attribute.", + m_szDetails, rc); + + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + + goto Exit; + } + + // The failed delete requires us to abort the trans + // NOTE: This may no longer be necessary in the future + + if( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "State Attribute Test 1", + " Verify that deleting or modifying the \"state\" attribute " + "(ATTR_STATE_TAG) on an element attribute or index definition is denied " + "if it is attemped using the \"regular\" DOM methods for updating nodes. ", + "(1) Verify the node has a \"state\" attribute" + "(2) Try to set it to an arbitrary value (3) Try to delete it.", + ""); + + if( RC_BAD( rc = pNode->hasAttribute( m_pDb, ATTR_STATE_TAG, &pAttr))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "\"state\" attribute not automatically created.", + m_szDetails, rc); + rc = NE_XFLM_FAILURE; + } + else + { + MAKE_ERROR_STRING( "hasAttribute failed.", m_szDetails, rc); + } + + goto Exit; + } + + rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"This should not work"); + if( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "attempt to modify \"state\" attribute returned " + "incorrect rc.", m_szDetails, rc); + + if( RC_OK( rc )) + { + rc = NE_XFLM_FAILURE; + } + + goto Exit; + } + + // Commit the trans here since we may have to abort the trans + // in which we attempt to delete the pAttr node + + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + rc = pAttr->deleteNode(m_pDb); + if( rc != NE_XFLM_DELETE_NOT_ALLOWED) + { + MAKE_ERROR_STRING( "Attempt to delete \"state\" attribute " + "returned incorrect rc.", m_szDetails, rc); + goto Exit; + } + + // The failed delete requires us to abort the trans + // NOTE: This may no longer be necessary in the future + + if( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "State Attribute Test 2 / Dict Num Test", + + "Test creating an element or attribute definition without " + "a \"state\" attribute (ATTR_STATE_TAG). In this case FLAIM should " + "automatically create the \"state\" attribute and set its value to " + "\"active\". Same should happen for indexes if the user did not create " + "a state attribute for the index definition and value should be set " + "to \"online\". Make sure that the \"DictNumber\" attribute " + "(ATTR_DICT_NUMBER) is automatically created and assigned if the user " + "omits it or sets it to zero. This applies to all types of definitions " + "(elements attributes indexes collections and prefixes). Once set " + "to a non-zero value make sure that the user cannot ever modify or " + "delete the attribute.", + + "(1) Create an attribute definition (2) create an element " + "definition (3) create an index definition (3) make sure attribute", + ""); + + + //create an attribute definition + /* + + */ + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_ATTRIBUTE_TAG, + &pAttrDef))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttrDef->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"foo"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->documentDone( pAttrDef))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttrDef->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, + sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( strcmp( (char *)szBuf, "active")) + { + MAKE_ERROR_STRING( "unexpected \"state\" value. Expected: \"active\"", + m_szDetails, rc); + goto Exit; + } + + //create an element definition + /* + + */ + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_ELEMENT_TAG, + &pElemDef))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pElemDef->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD ( rc = pAttrDef->getAttribute( + m_pDb, ATTR_DICT_NUMBER_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiAttrDictNum))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + rc = pAttr->setUINT( m_pDb, 123); + if ( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "Incorrect error code returned while trying to " + "delete dict number attribute.", m_szDetails, rc); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + //should have been set up automatically + + if ( uiAttrDictNum == 0) + { + MAKE_ERROR_STRING( "Invalid dictionary number.", m_szDetails, rc); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + if ( RC_BAD( rc = pElemDef->createAttribute( + m_pDb, + uiAttrDictNum, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->documentDone( pElemDef))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + //make sure state automatically set to "active" + + if ( RC_BAD( rc = pElemDef->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, + sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( strcmp( (char *)szBuf, "active")) + { + MAKE_ERROR_STRING( "Invalid state value. Should be \"active\".", + m_szDetails, rc); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + //make sure the dict num was set up automatically + + if ( RC_BAD ( rc = pElemDef->getAttribute( + m_pDb, ATTR_DICT_NUMBER_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiElemDictNum))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiElemDictNum == 0) + { + MAKE_ERROR_STRING( "Invalid dictionary number.", m_szDetails, rc); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + rc = pAttr->setUINT( m_pDb, 123); + if ( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "Incorrect rc when trying to modify dict num.", + m_szDetails, rc); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + //create an index definition that references the elem and attr defs we made + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_INDEX_TAG, + &pIndex))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pIndex->createAttribute( m_pDb, ATTR_NAME_TAG, &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar+foo"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + //xflaim:ElementPath must have one or more xflaim:ElementComponent + //or one or more xflaim:AttributeComponent sub-elements + + if ( RC_BAD( rc = pIndex->createNode( + m_pDb, + ELEMENT_NODE, + ELM_ELEMENT_COMPONENT_TAG, + XFLM_FIRST_CHILD, + &pNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"bar"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createAttribute( + m_pDb, + ATTR_KEY_COMPONENT_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 1))) + { + MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD ( rc = pNode->createAttribute( + m_pDb, + ATTR_INDEX_ON_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"value"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createNode( + m_pDb, + ELEMENT_NODE, + ELM_ATTRIBUTE_COMPONENT_TAG, + XFLM_FIRST_CHILD, + &pNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8(m_pDb, (FLMBYTE *)"foo"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createAttribute( + m_pDb, + ATTR_INDEX_ON_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"value"))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->createAttribute( + m_pDb, + ATTR_KEY_COMPONENT_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUINT( m_pDb, 2))) + { + MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = m_pDb->documentDone( pIndex))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD ( rc = pIndex->getAttribute( + m_pDb, ATTR_DICT_NUMBER_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUINT( m_pDb, &uiIndexDictNum))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiIndexDictNum == 0) + { + MAKE_GENERIC_ERROR_STRING( "Invalid dict num", m_szDetails, uiIndexDictNum); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + rc = pAttr->setUINT( m_pDb, 123); + if ( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "Invalid rc when trying to change dict num.", + m_szDetails, rc); + if ( RC_OK( rc )) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Referenced Definitions Test", + "Test to make sure that element and attribute definitions " + "cannot be deleted if they are referenced from an index definition.", + "Try to delete the Element and Attribute Definitions we created.", + ""); + + //need to begin a new transaction since attempting to delete + //the Element Definition node will probably require the transaction + //to be aborted and we don't want to lose our modifications + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + rc = pElemDef->deleteNode( m_pDb); + if ( rc != NE_XFLM_CANNOT_DEL_ELEMENT) + { + MAKE_ERROR_STRING( "Expected rc == NE_XFLM_CANNOT_DEL_ELEMENT", + m_szDetails, rc); + goto Exit; + } + + //The failed delete requires us to abort the trans + //NOTE: This may no longer be necessary in the future + + if ( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + //try to delete attribute + rc = pAttrDef->deleteNode( m_pDb); + if ( rc != NE_XFLM_CANNOT_DEL_ATTRIBUTE) + { + MAKE_ERROR_STRING( "Exected rc == NE_XFLM_CANNOT_DEL_ATTRIBUTE.", + m_szDetails, rc); + if ( RC_OK( rc )) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + //The failed delete requires us to abort the trans + //NOTE: This may no longer be necessary in the future + if ( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "State Attribute Test 3", + + "Verify that the state attribute can be modified using the " + "special methods that are made for that purpose. For index definitions " + "the indexSuspend and indexResume methods may be used to change the state " + "of the index. For attribute and element definitions the changeItemState " + "method may be used to change the state of the element or attribute. For " + "changeItemState we need to verify that the state change is legal. Certain " + "state transitions are illegal for a user to make even using changeItemState." + "Test to make sure that element and attribute definitions cannot be deleted " + "directly using the DOM APIs if the state attribute is not set to \"unused\"." + "Verify that state is only settable to \"unused\" by the sweep method - users " + "should not be able to do this. The sweep method should be tested to make sure " + "it does not set the state to \"unused\" if the element or attribute definition " + "is actually in use somewhere.", + + "(1) Call indexSuspend and indexResume on the index definition " + "(2) Verify the state is changed correctly (3) Set state attribute of " + "the index element and attribute definitions to \"checking\" (4) Call " + "sweep (5) Verify all states are correct (6) Verify user cannot set state " + "to \"unused\" directly (7) for each definition in reverse order of creation: " + "(a) call sweep to set state to \"unused\" (b) delete definition.", + ""); + + //suspend the index + if ( RC_BAD( rc = m_pDb->indexSuspend( uiIndexDictNum))) + { + MAKE_ERROR_STRING( "indexSuspend failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pIndex->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof( szBuf), 0, + sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + //NOTE: There is a define FLM_INDEX_SUSPENDED_STR in flaim.h + //should I have access to it in flaimpub.h? + if ( strcmp( (char *)szBuf, "suspended")) + { + //MAKE_ERROR_STRING macro won't work here. + sprintf( + (char*)m_szDetails, + "index state: %s. expected \"suspended\". " + "rc == %X. File: %s. Line# %u. ", + szBuf, + (unsigned)rc, + __FILE__, + __LINE__); + goto Exit; + } + + //resume the index + if ( RC_BAD( rc = m_pDb->indexResume( uiIndexDictNum))) + { + MAKE_ERROR_STRING( "indexResume failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pIndex->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, + sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + if ( strcmp( (char *)szBuf, "online") && + strcmp( (char *)szBuf, "offline")) //VISIT - may still be offline ? + { + //MAKE_ERROR_STRING macro won't work here. + sprintf( + (char*)m_szDetails, + "index state: %s. expected \"online\" or \"offline\". " + "rc == %X. File: %s. Line# %u. ", + szBuf, + (unsigned)rc, + __FILE__, + __LINE__); + goto Exit; + } + + //now set it to unused the right way + if ( RC_BAD( rc = m_pDb->changeItemState( + ELM_ATTRIBUTE_TAG, + uiAttrDictNum, + "checking"))) + { + MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + for( ;;) + { + if ( RC_BAD( rc = pAttrDef->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), 0, + sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if( !strcmp( (char *)szBuf, "active")) + { + break; + } + + f_sleep( 250); + } + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if ( RC_BAD( rc = m_pDb->changeItemState( + ELM_ELEMENT_TAG, + uiElemDictNum, + "checking"))) + { + MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + for( ;;) + { + if ( RC_BAD( rc = pElemDef->getAttribute( + m_pDb, + ATTR_STATE_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUTF8( m_pDb, szBuf, sizeof(szBuf), + 0, sizeof(szBuf) - 1))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if( !strcmp( (char *)szBuf, "active")) + { + break; + } + + f_sleep( 250); + } + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + //shouldn't be able to set to "unused" this way + + rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"unused"); + if ( rc != NE_XFLM_READ_ONLY) + { + MAKE_ERROR_STRING( "Invalid return code when trying to modify " + "state attribute.", m_szDetails, rc); + goto Exit; + } + + //delete the IndexDef + if ( RC_BAD( rc = pIndex->deleteNode( m_pDb))) + { + sprintf( + (char*)m_szDetails, + "failed to delete index definition." + "Line# %u. rc == %X.", + __LINE__, + (unsigned)rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->changeItemState( + ELM_ELEMENT_TAG, + uiElemDictNum, + "checking"))) + { + MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + for( ;;) + { + if( RC_BAD( rc = pElemDef->getNodeId( m_pDb, &ui64Tmp))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + flmAssert( 0); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + f_sleep( 250); + } + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if ( RC_BAD( rc = m_pDb->changeItemState( + ELM_ATTRIBUTE_TAG, + uiAttrDictNum, + "checking"))) + { + MAKE_ERROR_STRING( "changeItemState failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + for( ;;) + { + if( RC_BAD( rc = pAttrDef->getNodeId( m_pDb, &ui64Tmp))) + { + if( rc != NE_XFLM_DOM_NODE_DELETED) + { + flmAssert( 0); + goto Exit; + } + + rc = NE_XFLM_OK; + break; + } + + f_sleep( 250); + } + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + m_pszTestName = "Dictionary Doc #1 Test"; + m_pszTestDesc = "Verify that no nodes in document #1 of the dictionary collection " + "can be modified or deleted by an application. This is a special document " + "used internally by FLAIM so it should not be changeable or deletable by " + "an application. It is readable but not changeable or deletable."; + + m_pszSteps = "(1) Get Doc #1 (2) Try to delete it. "; + + beginTest( + "Dictionary Doc #1 Test", + "Verify that no nodes in document #1 of the dictionary collection " + "can be modified or deleted by an application. This is a special document " + "used internally by FLAIM so it should not be changeable or deletable by " + "an application. It is readable but not changeable or deletable.", + "(1) Get Doc #1 (2) Try to delete it. ", + ""); + + //get document #1 + if ( RC_BAD( rc = m_pDb->getNode( + XFLM_DICT_COLLECTION, + 1, + &pDocument))) + { + MAKE_ERROR_STRING( "getNode failed.", m_szDetails, rc); + goto Exit; + } + + //should not be deleteable + rc = pDocument->deleteNode( m_pDb); + if ( rc != NE_XFLM_DELETE_NOT_ALLOWED) + { + MAKE_ERROR_STRING( "Unexpected rc from attempt to delete dict. doc #1." + , m_szDetails, rc); + goto Exit; + } + + //this failed delete forces us to abort the trans + if ( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + endTest("PASS"); + + m_pszTestName = "Collection Definition Test"; + m_pszTestDesc = "Verify rules for collection definitions are enforced"; + m_pszSteps = "(1) Create a collection definition (2) Try to delete its name"; + + beginTest( + "Collection Definition Test", + "Verify rules for collection definitions are enforced", + "(1) Create a collection definition (2) Try to delete its name", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DICT_COLLECTION, + ELM_COLLECTION_TAG, + &pCollection))) + { + MAKE_ERROR_STRING( "createRootElement failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pCollection->createAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)"My Collection"))) + { + MAKE_ERROR_STRING( "setUTF8 failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->documentDone( pCollection))) + { + MAKE_ERROR_STRING( "documentDone failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pCollection->getAttribute( + m_pDb, + ATTR_NAME_TAG, + &pAttr))) + { + MAKE_ERROR_STRING( "getAttribute failed", m_szDetails, rc); + goto Exit; + } + + rc = pAttr->deleteNode( m_pDb); + if ( rc != NE_XFLM_DELETE_NOT_ALLOWED) + { + MAKE_ERROR_STRING( "Invalid rc returned when trying to delete " + "\"name\" attribute. Expected: NE_XFLM_DELETE_NOT_ALLOWED", + m_szDetails, rc); + if( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_ERROR_STRING( "transAbort failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + endTest("PASS"); + +Exit: + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if (bTransStarted) + { + m_pDb->transCommit(); + } + + if ( pNode) + { + pNode->Release(); + } + + if ( pAttr) + { + pAttr->Release(); + } + + if ( pAttrDef) + { + pAttrDef->Release(); + } + + if ( pElemDef) + { + pElemDef->Release(); + } + + if ( pDocument) + { + pDocument->Release(); + } + + if ( pIndex ) + { + pIndex->Release(); + } + + if( pCollection) + { + pCollection->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( rc); +} diff --git a/version5/util/diffbackups.cpp b/version5/util/diffbackups.cpp new file mode 100644 index 0000000..9d2aaeb --- /dev/null +++ b/version5/util/diffbackups.cpp @@ -0,0 +1,321 @@ +//------------------------------------------------------------------------------ +// Desc: Check differences between backups +// +// 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: diffbackups.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "dart_backup.h" +#include "ftx.h" +#include "flmarg.h" + +FLMBOOL gv_bShutdown = FALSE; +FSTATIC FLMBOOL gv_bRunning = FALSE; +FSTATIC FTX_INFO * gv_pFtxInfo = NULL; +FSTATIC FTX_WINDOW * gv_pMainWindow = NULL; + +FINLINE FLMBOOL breakCallback( + void * pvData) +{ + F_UNREFERENCED_PARM( pvData); + return FALSE; +} + +FSTATIC RCODE utilMain( FLMUINT uiArgc, char ** ppszArgv); + +#ifdef FLM_NLM + +// Prototypes + +FSTATIC void utilCleanup( void); + +// End prototypes + +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +FSTATIC void utilCleanup( + void + ) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + f_yieldCPU(); + } +} +#endif + +#ifdef FLM_NLM +FLMBOOL gv_bSynchronized = FALSE; +#endif + +#ifdef FLM_WATCOM_NLM + #define main nlm_main +#endif + +/******************************************************************** +Desc: main +*********************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +int main( + int iArgC, + char ** ppucArgV + ) +#else +int __cdecl main( + int iArgC, + char ** ppucArgV + ) +#endif +{ + RCODE rc = NE_XFLM_OK; + int iResCode = 0; + F_DbSystem dbSystem; + + + gv_bShutdown = FALSE; + gv_bRunning = TRUE; + +#ifdef FLM_NLM + /* Setup the routine to be called when the NLM exits itself */ + atexit( utilCleanup); +#endif + + if ( RC_BAD( rc = dbSystem.init())) + { + goto Exit; + } + + //main code which varies from util to util goes here + rc = utilMain( iArgC, ppucArgV); + +Exit: + + dbSystem.exit(); + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } + gv_bRunning = FALSE; +#endif + + if ( iResCode == 0) + { + iResCode = (int)rc; + } + return( iResCode); +} + +/**************************************************************************** +NOTE: UTILITY-SPECIFIC CODE STARTS HERE +****************************************************************************/ + +/**************************************************************************** +Name: utilMain +Desc: the 'main'-type method which varies per utility +****************************************************************************/ +FSTATIC RCODE utilMain( FLMUINT uiArgc, char ** ppszArgv) +{ + RCODE rc = NE_XFLM_OK; + FlmArgSet * pArgSet = NULL; + FLMBOOL bPrintedUsage; + FLMBOOL bBatchMode = FALSE; + FLMUINT uiScreenRows; + + char szBackupRoot[128]; + char szRflRoot[128]; + char szDestDib1[16]; + char szDestDib2[16]; + + FLMUINT uiSet1 = 0; + FLMUINT uiSet2 = 0; + + FLMUINT64 ui64LastTrans = 0; + + F_RandomGenerator randomGen; + + randomGen.randomSetSeed( 123); + + //initialize the windowing + TEST_RC( rc = utilInitWindow( "ezutil", &uiScreenRows, + &gv_pFtxInfo, &gv_pMainWindow, &gv_bShutdown)); + + if ( (pArgSet = new FlmArgSet( + "diff backups test", //description of utility + utilOutputLine, gv_pMainWindow, //output callback and data + utilPressAnyKey, gv_pMainWindow, //pager callback and data + uiScreenRows)) //rows per screen + == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + + + TEST_RC( rc = pArgSet->addArg( + "root", "root directory where backups are", + TRUE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); + + TEST_RC( rc = pArgSet->addArg( + "rflroot", "root directory where rfl backups are", + TRUE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); + + TEST_RC( rc = pArgSet->addArg( + "firstDb", "name of first database", + FALSE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); + + TEST_RC( rc = pArgSet->addArg( + "secondDb", "name of second database", + FALSE, FLMARG_OPTION, FLMARG_CONTENT_STRING)); + +#ifdef FLM_LINUX + TEST_RC( rc = pArgSet->addArg( + "trans", "last trans to replay", + FALSE, FLMARG_OPTION, FLMARG_CONTENT_UNSIGNED_INT_64, (FLMUINT64)0, 0xFFFFFFFFFFFFFFFFLL)); +#else + TEST_RC( rc = pArgSet->addArg( + "trans", "last trans to replay", + FALSE, FLMARG_OPTION, FLMARG_CONTENT_UNSIGNED_INT_64, (FLMUINT64)0, 0xFFFFFFFFFFFFFFFF)); +#endif + + //options +#ifdef FLM_NLM + TEST_RC( rc = pArgSet->addArg( + "waitToSync", "wait to sync on Netware", + TRUE, FLMARG_OPTION, FLMARG_CONTENT_NONE)); +#endif + + //required args + TEST_RC( rc = pArgSet->addArg( + "firstSet", "first backup set number", + FALSE, FLMARG_REQUIRED_ARG, FLMARG_CONTENT_UNSIGNED_INT, + 0, 0xFFFFFFFF)); + + TEST_RC( rc = pArgSet->addArg( + "secondSet", "second backup set number", + FALSE, FLMARG_REQUIRED_ARG, FLMARG_CONTENT_UNSIGNED_INT, + 0, 0xFFFFFFFF)); + + //feed in the true command line + TEST_RC( rc = pArgSet->parseCommandLine( uiArgc, ppszArgv, &bPrintedUsage)); + +#ifdef FLM_NLM + if (!gv_bSynchronized && !(pArgSet->argIsPresent( "waitToSync"))) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + if( pArgSet->argIsPresent("root")) + { + f_strcpy( szBackupRoot, pArgSet->getString("root")); + } + else + { + f_strcpy( szBackupRoot, "hcbstage\\backups"); + } + + if( pArgSet->argIsPresent("rflroot")) + { + f_strcpy( szRflRoot, pArgSet->getString("rflroot")); + } + else + { + f_strcpy( szRflRoot, "hcbstage\\rfls"); + } + + if( pArgSet->argIsPresent("firstDb")) + { + f_strcpy( szDestDib1, pArgSet->getString("firstDb")); + } + else + { + f_strcpy( szDestDib1, "db1.db"); + } + + if( pArgSet->argIsPresent("secondDb")) + { + f_strcpy( szDestDib2, pArgSet->getString("secondDb")); + } + else + { + f_strcpy( szDestDib2, "db2.db"); + } + + if ( pArgSet->argIsPresent("firstSet")) + { + uiSet1 = pArgSet->getUINT( "firstSet"); + } + else + { + flmAssert(0); + } + + if ( pArgSet->argIsPresent("secondSet")) + { + uiSet2 = pArgSet->getUINT( "secondSet"); + } + else + { + flmAssert(0); + } + + if( pArgSet->argIsPresent("trans")) + { + ui64LastTrans = pArgSet->getUINT64( "trans"); + } + + if ( RC_BAD( rc = utilDiffBackupSets( + szBackupRoot, + szRflRoot, + szDestDib1, + szDestDib2, + &randomGen, + utilOutputLine, + gv_pMainWindow, + breakCallback, + gv_pMainWindow, + uiSet1, + uiSet2, + ui64LastTrans))) + { + goto Exit; + } + +Exit: + + if ( !bBatchMode) + { + utilPressAnyKey( "press any key to exit...", gv_pMainWindow); + } + if ( pArgSet) + { + pArgSet->Release(); + } + utilShutdownWindow( gv_pFtxInfo); + return rc; +} diff --git a/version5/util/dirtyexittest1srv.cpp b/version5/util/dirtyexittest1srv.cpp new file mode 100644 index 0000000..1c98993 --- /dev/null +++ b/version5/util/dirtyexittest1srv.cpp @@ -0,0 +1,286 @@ +//------------------------------------------------------------------------------ +// Desc: Dirty exit test 1 +// +// 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: dirtyexittest1srv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined(NLM) + #define DB_NAME_STR "SYS:\\BAD.DB" +#else + #define DB_NAME_STR "bad.db" +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +class IDirtyExitTest1Impl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IDirtyExitTest1Impl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IDirtyExitTest1Impl::getName( void) +{ + return( "Dirty Exit Test Part 1"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDirtyExitTest1Impl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bTransStarted = FALSE; + IF_PosIStream * pBufferIStream = NULL; + const char * pszDocument = + "" + " " + " 01 Pull Me Under.mp3" + " Pull Me Under" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 1" + " " + " " + " " + " 02 Another Day.mp3" + " Another Day" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 2" + " " + " " + " " + " 03 Take the Time.mp3" + " Take the Time" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 3" + " " + " " + " " + " 04 Surrounded.mp3" + " Surrounded" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 4" + " " + " " + " " + " 05 Metropolis, Pt. 1.mp3" + " Metropolis, Pt. 1" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 5" + " " + " " + " " + " 06 Under a Glass Moon.mp3" + " Under a Glass Moon" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 6" + " " + " " + " " + " 07 Wait for Sleep.mp3" + " Wait for Sleep" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 7" + " " + " " + " " + " 08 Learning to Live.mp3" + " Learning to Live" + " Dream Theater" + " Images & Words" + " 1992" + " Rock" + " 8" + " " + " " + ""; + + beginTest( + "Init Test State", + "Setup the necessary state for our test ", + "(1) Get the DbSystem (2) Create a database", + "No Additional Details."); + + if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_FLM_ERROR_STRING("Failed to open test state", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Import document test", + "Import a document", + "Import a document before the dirty shutdown", + ""); + + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING("Failed to begin trans.", m_szDetails, rc); + goto Exit; + } + bTransStarted = TRUE; + + if ( RC_BAD( rc = m_pDbSystem->openBufferIStream( + pszDocument, + strlen( pszDocument), + &pBufferIStream))) + { + MAKE_FLM_ERROR_STRING("Failed to open file istream", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->import( pBufferIStream, XFLM_DATA_COLLECTION))) + { + MAKE_FLM_ERROR_STRING("Failed to import document", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + +#ifdef FLM_USE_NICI + { + FLMUINT uiDes3Def = 0; + IF_DOMNode * pRoot = NULL; + IF_DOMNode * pNode = NULL; + + beginTest( + "Recover Encryption Test", + "Make sure operations with encrypted values are being recovered properly", + "(1) Create an encryption definition (2) Add an encrypted value " + "(3) commit the trans (4) quit the program without calling exit()", + ""); + + if ( RC_BAD( rc = m_pDb->createEncDef( + "des3", + "des3 definition", + 0, + &uiDes3Def))) + { + MAKE_FLM_ERROR_STRING("createEncDef failed", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->getFirstDocument( XFLM_DATA_COLLECTION, &pRoot))) + { + MAKE_FLM_ERROR_STRING("getFirstDocument", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pRoot->createNode( m_pDb, ELEMENT_NODE, + ELM_DOCUMENT_TITLE_TAG, XFLM_FIRST_CHILD, &pNode))) + { + MAKE_FLM_ERROR_STRING("createNode", m_szDetails, rc); + pRoot->Release(); + goto Exit; + } + + rc = pNode->setUTF8( + m_pDb, (FLMBYTE *)"My big ol' native text value to be encrypted", + 0, TRUE, uiDes3Def); + + pRoot->Release(); + pNode->Release(); + + if ( RC_BAD( rc)) + { + MAKE_FLM_ERROR_STRING("setNative failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + } +#endif + +Exit: + if ( bTransStarted) + { + RCODE tmpRc = m_pDb->transCommit(); + if ( RC_OK( rc)) + { + rc = tmpRc; + } + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + // superficial cleanup. Purposely not calling exit on the dbsystem + + if ( pBufferIStream) + { + pBufferIStream->Release(); + } + return rc; +} diff --git a/version5/util/dirtyexittest2srv.cpp b/version5/util/dirtyexittest2srv.cpp new file mode 100644 index 0000000..52f9a3b --- /dev/null +++ b/version5/util/dirtyexittest2srv.cpp @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// Desc: Dirty exit test 2 +// +// 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: dirtyexittest2srv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\BAD.DB" + #define REBUILD_DEST_NAME_STR "SYS:\\BLD.DB" +#else + #define DB_NAME_STR "bad.db" + #define REBUILD_DEST_NAME_STR "bld.db" +#endif + +/***************************************************************************** +Desc: +******************************************************************************/ +class IDirtyExitTest2Impl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); +}; + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if ( ( *ppTest = new IDirtyExitTest2Impl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +const char * IDirtyExitTest2Impl::getName( void) +{ + return( "Dirty Exit Test 2"); +} + +/***************************************************************************** +Desc: +******************************************************************************/ +RCODE IDirtyExitTest2Impl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bDibCreated = FALSE; + FLMUINT64 ui64TotalNodes; + FLMUINT64 ui64NodesRecovered; + + // Open the test state created by Dirty Exit Test 1. + + if ( RC_BAD( rc = openTestState( DB_NAME_STR))) + { + goto Exit; + } + + bDibCreated = TRUE; + + beginTest( + "Database check test", + "Make sure dib was not corrupted by dirty shutdown", + "Self explanatory", + "No Additional Details."); + + // Make sure the database is still consistent + + if( RC_BAD( rc = m_pDbSystem->dbCheck( DB_NAME_STR, NULL, NULL, NULL, + XFLM_DO_LOGICAL_CHECK, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING("dbCheck failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Database rebuild test", + "Make sure the rebuild code works", + "Self explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->dbRebuild( DB_NAME_STR, NULL, + REBUILD_DEST_NAME_STR, NULL, NULL, + NULL, NULL, NULL, &ui64TotalNodes, &ui64NodesRecovered, + NULL, NULL))) + { + MAKE_FLM_ERROR_STRING("dbRebuild failed", m_szDetails, rc); + goto Exit; + } + + if( ui64TotalNodes != ui64NodesRecovered) + { + MAKE_FLM_ERROR_STRING("dbRebuild failed", m_szDetails, NE_XFLM_FAILURE); + goto Exit; + } + + endTest("PASS"); + +Exit: + + if( RC_BAD( rc)) + { + endTest("FAIL"); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( rc); +} diff --git a/version5/util/domedit.cpp b/version5/util/domedit.cpp new file mode 100644 index 0000000..e60a5e0 --- /dev/null +++ b/version5/util/domedit.cpp @@ -0,0 +1,374 @@ +//------------------------------------------------------------------------------ +// Desc: Standalone DOM editor utility +// +// 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: domedit.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "ftx.h" +#include "domedit.h" + +#if defined( FLM_UNIX) + #include +#endif + +// NetWare hooks + +#ifdef FLM_NLM + FLMBOOL gv_bSynchronized = FALSE; + void domEditCleanup( void); +#endif + +/* +DOMEdit prototypes +*/ + +void UIMain( void * pData); + +RCODE _domEditBackgroundThread( + F_Thread * pThread); + +RCODE domEditVerifyRun( void); + +/* +Imported global data +*/ + +#ifdef FLM_DEBUG + extern RCODE gv_CriticalFSError; +#endif + +/*-------------------------------------------------------- +** Local (to this file only) global variables. +**-------------------------------------------------------*/ + +FTX_INFO * gv_pFtxInfo = NULL; +FLMBOOL gv_bShutdown = FALSE; +static F_Thread * gv_pBackgroundThrd = NULL; +char gv_szDbPath[ F_PATH_MAX_SIZE]; +char gv_szRflDir[ F_PATH_MAX_SIZE]; +char gv_szPassword[ 128]; +FLMBOOL gv_bAllowLimited; +static FLMBOOL gv_bRunning = TRUE; + +#ifdef FLM_WATCOM_NLM + #define main nlm_main +#endif + +/**************************************************************************** +Desc: +****************************************************************************/ +#if defined( FLM_UNIX) || defined( FLM_NLM) +extern "C" int main( + int ArgC, + char ** ArgV) +#else +int __cdecl main( + int ArgC, + char ** ArgV) +#endif +{ + int iResCode = 0; + +#if defined( FLM_UNIX) + struct rlimit l; + + if (getrlimit(RLIMIT_NOFILE, &l) < 0) + { + fprintf(stderr, "Could not get the maximum number of open files: %s", + strerror(errno)); + exit(1); + } + + if (geteuid() == 0) + l.rlim_max = 65536; // big enough for our needs + + l.rlim_cur = l.rlim_max; + + // increase the fd table + if (setrlimit(RLIMIT_NOFILE, &l) < 0) + fprintf(stderr, "Could not increase the number of open files to %ld", + l.rlim_max); +#endif + +#ifdef FLM_NLM + + /* Setup the routines to be called when the NLM exits itself */ + + atexit( domEditCleanup); + + /* Register to see the DOWN server event. */ + + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + + gv_szDbPath[ 0] = 0; + gv_szRflDir[ 0] = 0; + gv_szPassword[0] = 0; + gv_bAllowLimited = FALSE; + + if( ArgC >= 2) + { + f_strcpy( gv_szDbPath, ArgV[ 1]); + } + if( ArgC >= 3) + { + f_strcpy( gv_szRflDir, ArgV[ 2]); + } + + if (ArgC >= 4) + { + f_strcpy( gv_szPassword, ArgV[ 3]); + } + + if (ArgC >=5) + { + if (f_strnicmp( ArgV[ 4], "TRUE", 4) == 0) + { + gv_bAllowLimited = TRUE; + } + } + + UIMain( NULL); + +//Exit: + +#ifdef FLM_NLM + if (!gv_bSynchronized) + { + SynchronizeStart(); + gv_bSynchronized = TRUE; + } +#endif + gv_bRunning = FALSE; + + + return( iResCode); +} + +/**************************************************************************** +Name: UIMain +****************************************************************************/ +void UIMain( void * pData) +{ + F_Db * pDb = NULL; + FTX_SCREEN * pScreen = NULL; + FTX_WINDOW * pTitleWin = NULL; + F_DomEditor * pDomEditor = NULL; + char szTitle[ 80]; + FLMUINT uiDummy; + char szDbPath [F_PATH_MAX_SIZE]; + FLMUINT Cols; + FLMUINT Rows; + RCODE rc; + int iResCode = 0; + F_DbSystem dbSystem; + + F_UNREFERENCED_PARM( pData); + + if( RC_BAD( dbSystem.init())) + { + iResCode = -1; + goto Exit; + } + + f_sprintf( szTitle, + "DOMEdit for XFLAIM [DB=%s/BUILD=%s]", + XFLM_CURRENT_VER_STR, __DATE__); + + if( FTXInit( szTitle, 80, 50, + WPS_BLUE, WPS_WHITE, NULL, NULL, &gv_pFtxInfo) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + FTXSetShutdownFlag( gv_pFtxInfo, &gv_bShutdown); + + + if( FTXScreenInit( gv_pFtxInfo, szTitle, &pScreen) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + if( FTXWinInit( pScreen, 0, 1, &pTitleWin) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + if( FTXWinPaintBackground( pTitleWin, WPS_RED) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + if( FTXWinPrintStr( pTitleWin, szTitle) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + + if( FTXWinOpen( pTitleWin) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + + + if( RC_BAD( f_threadCreate( &gv_pBackgroundThrd, + _domEditBackgroundThread, "domedit_refresh"))) + { + iResCode = 1; + goto Exit; + } + + /* + Check expiration date + */ + + if( RC_BAD( rc = domEditVerifyRun())) + { + FTXDisplayMessage( pScreen, WPS_RED, WPS_WHITE, + "This Utility Has Expired", + F_DbSystem::_errorString( RC_SET( NE_XFLM_ILLEGAL_OP)), &uiDummy); + f_sleep( 5000); + iResCode = 1; + goto Exit; + } + + /* + Open the database + */ + + if( gv_szDbPath[ 0]) + { + + if( RC_BAD( rc = dbSystem.dbOpen( gv_szDbPath, NULL, gv_szRflDir, + (IF_Db **)&pDb, gv_szPassword, gv_bAllowLimited))) + { + FTXDisplayMessage( pScreen, WPS_RED, WPS_WHITE, + "Unable to open the database", + dbSystem.errorString( rc), &uiDummy); + iResCode = 1; + goto Exit; + } + } + else + { + if( RC_BAD( rc = dbSystem.dbOpen( szDbPath, NULL, gv_szRflDir, + (IF_Db **)&pDb, gv_szPassword, gv_bAllowLimited))) + { + FTXDisplayMessage( pScreen, WPS_RED, WPS_WHITE, + "Unable to open the database", + F_DbSystem::_errorString( rc), &uiDummy); + iResCode = 1; + goto Exit; + } + else + { + FTXWinClear( pTitleWin); + if( FTXWinPrintf( pTitleWin, "%s (Direct)", szTitle) != FTXRC_SUCCESS) + { + iResCode = 1; + goto Exit; + } + } + } + + if( (pDomEditor = f_new F_DomEditor) == NULL) + { + iResCode = 1; + goto Exit; + } + + if( RC_BAD( pDomEditor->Setup( pScreen))) + { + iResCode = 1; + goto Exit; + } + + pDomEditor->setSource( pDb, XFLM_DATA_COLLECTION); + pDomEditor->setShutdown( &gv_bShutdown); + + /* + Fire up the editor + */ + + FTXScreenGetSize( pScreen, &Cols, &Rows); + pDomEditor->interactiveEdit( 0, 1, Cols - 1, Rows - 1); + + + + + +Exit: + + if( pDomEditor) + { + pDomEditor->Release(); + pDomEditor = NULL; + } + + gv_bShutdown = TRUE; + if (pDb) + { + pDb->Release(); + } + + /* + Shut down the display and keyboard + */ + + f_threadDestroy( &gv_pBackgroundThrd); + + /* + Free FTX + */ + + FTXFree( &gv_pFtxInfo); + + dbSystem.exit(); +} + + +#ifdef FLM_NLM +/**************************************************************************** +Desc: This routine shuts down all threads in the NLM. +****************************************************************************/ +void domEditCleanup( void) +{ + gv_bShutdown = TRUE; + while( gv_bRunning) + { + f_sleep( 10); + } +} +#endif + diff --git a/version5/util/domedit.h b/version5/util/domedit.h new file mode 100644 index 0000000..2f700a7 --- /dev/null +++ b/version5/util/domedit.h @@ -0,0 +1,860 @@ +//------------------------------------------------------------------------------ +// Desc: DOM editor +// +// 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: domedit.h 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef DOMEDIT_HPP +#define DOMEDIT_HPP + +class F_DomEditor; +typedef F_DomEditor * F_DomEditor_p; + +/* +Constants +*/ + +#define F_DOMEDIT_BUF_SIZE 0x0000FFFF +#define F_DOMEDIT_MAX_TITLE_SIZE 64 + +/* +System flags +*/ + +#define F_DOMEDIT_FLAG_NOPARENT 0x00000001 +#define F_DOMEDIT_FLAG_NOCHILD 0x00000002 +#define F_DOMEDIT_FLAG_LIST_ITEM 0x00000004 +#define F_DOMEDIT_FLAG_HIDE_TAG 0x00000008 +#define F_DOMEDIT_FLAG_HIDE_LEVEL 0x00000010 +#define F_DOMEDIT_FLAG_HIDE_SOURCE 0x00000020 +#define F_DOMEDIT_FLAG_READ_ONLY 0x00000040 +#define F_DOMEDIT_FLAG_SELECTED 0x00000080 +#define F_DOMEDIT_FLAG_ENDTAG 0x00000100 +#define F_DOMEDIT_FLAG_NO_DELETE 0x00000200 +#define F_DOMEDIT_FLAG_COLLAPSED 0x00000400 +#define F_DOMEDIT_FLAG_HIDE_EXPAND 0x00000800 +#define F_DOMEDIT_FLAG_COMMENT 0x00001000 +#define F_DOMEDIT_FLAG_KEY_FROM 0x00002000 +#define F_DOMEDIT_FLAG_KEY_UNTIL 0x00004000 +#define F_DOMEDIT_FLAG_NODOM 0x00008000 // Don't fetch a dom object. +#define F_DOMEDIT_FLAG_ELEMENT_DATA 0x00010000 // Display the data embedded in the element node (acting as the child) + +/* +Index selection flags +*/ + +#define F_DOMEDIT_ISEL_NOIX 0x0001 // Show "no index" as an option + +/* +Configuration options +*/ + +#define F_DOMEDIT_CONFIG_STATS_START 0x0001 +#define F_DOMEDIT_CONFIG_STATS_STOP 0x0002 +#define F_DOMEDIT_CONFIG_STATS_RESET 0x0003 + + +/* +Types, enums, etc. +*/ + +typedef enum +{ + F_DOMEDIT_EVENT_RECREAD, + F_DOMEDIT_EVENT_RECINSERT, + F_DOMEDIT_EVENT_GETDISPVAL, + F_DOMEDIT_EVENT_GETNEXTNODE, + F_DOMEDIT_EVENT_GETPREVNODE, + F_DOMEDIT_EVENT_IEDIT, // Interactive editor invoked + F_DOMEDIT_EVENT_REFRESH, // Called prior to refresh + F_DOMEDIT_EVENT_NAME_TABLE +} eDomEventType; + +#define MAX_DISPLAY_SEGMENT 100 +typedef struct +{ + char szString[ MAX_DISPLAY_SEGMENT]; + FLMUINT uiCol; + FLMUINT uiForeground; + FLMUINT uiBackground; +} DME_DISP_COLUMN; + +// Structure to hold an entire row +typedef struct rowInfo +{ + FLMUINT uiLevel; + FLMUINT64 ui64NodeId; + FLMUINT64 ui64DocId; + FLMUINT uiNameId; + FLMBOOL bExpanded; + FLMBOOL bHasChildren; + FLMBOOL bHasAttributes; + FLMBOOL bHasElementData; // When this flag is set, the node is acting as the parent. + F_DOMNode * pDomNode; + F_DataVector * pVector; + eDomNodeType eType; + FLMUNICODE * puzValue; + FLMUINT uiLength; + FLMUINT uiIndex; + FLMUINT uiElementNumber; + FLMBOOL bUseValue; + FLMUINT uiFlags; + struct rowInfo * pNext; + struct rowInfo * pPrev; +} DME_ROW_INFO; + +typedef struct +{ + FLMBOOL bSingleLevel; + FLMUINT uiAnchorLevel; + FLMUINT64 ui64NodeId; +} DME_ROW_ANCHOR; + +typedef struct +{ + F_NameTable * pNameTable; + FLMBOOL bInitialized; +} DME_NAME_TABLE_INFO; + +/* +Callbacks +*/ + +typedef RCODE (* F_DOMEDIT_DISP_HOOK)( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals); + +typedef RCODE (* F_DOMEDIT_KEY_HOOK)( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +typedef RCODE (* F_DOMEDIT_HELP_HOOK)( + F_DomEditor * pDomEditor, + F_DomEditor * pHelpEditor); + +typedef RCODE (* F_DOMEDIT_EVENT_HOOK)( + F_DomEditor * pDomEditor, + eDomEventType eEventType, + void * EventData, + void * UserData); + +/* +Class definitions +*/ +class F_DomEditor : public XF_RefCount, public XF_Base +{ +public: + + F_DomEditor( void); + ~F_DomEditor( void); + + void reset( void); + + RCODE Setup( + FTX_SCREEN * pScreen); + + void setParent( + F_DomEditor * pParent); + + F_DomEditor * getParentEditor( void); + + RCODE setSource( + F_Db * pDb, + FLMUINT uiCollection); + + F_Db * getSource( void ) + { + return m_pDb; + } + + RCODE insertRow( + DME_ROW_INFO * pRow, + DME_ROW_INFO * pStartRow = NULL); + + RCODE setTitle( + char * pszTitle); + + void setCurrentAtTop( void); + + void setCurrentAtBottom( void); + + void setReadOnly( + FLMBOOL bReadOnly); + + RCODE interactiveEdit( + FLMUINT uiULX = 0, + FLMUINT uiULY = 0, + FLMUINT uiLRX = 0, + FLMUINT uiLRY = 0, + FLMBOOL bBorder = TRUE, + FLMUINT uiStatusLines = 1, + FLMUINT uiStartChar = 0); + + FLMBOOL isMonochrome( void); + + RCODE getPrevRow( + DME_ROW_INFO * pCurRow, + DME_ROW_INFO ** ppPrevRow, + FLMBOOL bFetchPrevRow = FALSE); + + RCODE getNextRow( + DME_ROW_INFO * pCurRow, + DME_ROW_INFO ** ppNextRow, + FLMBOOL bFetchNextRow = FALSE, + FLMBOOL bIgnoreAnchor = FALSE); + + RCODE getNextDocument( + DME_ROW_INFO * pCurRow, + DME_ROW_INFO ** ppNextRow, + FLMBOOL bFetchNextRow = FALSE, + FLMBOOL bIgnoreAnchor = FALSE); + + void setScrFirstRow( + DME_ROW_INFO * pScrFirstRow); + + DME_ROW_INFO * getScrFirstRow( void); + + DME_ROW_INFO * getScrLastRow( void); + + FLMUINT getCursorRow( void); + + FLMUINT getNumRows( void); + + FLMUINT getDispRows( void) + { + return m_uiNumRows; + } + + void setNumDispRows( + FLMUINT uiNumDispRows) + { + m_uiNumRows = uiNumDispRows; + } + + RCODE getDisplayValue( + DME_ROW_INFO * pRow, + char * pszBuf, + FLMUINT uiBufSize); + + DME_ROW_INFO * findRecord( + FLMUINT uiCollection, + FLMUINT uiDrn, + DME_ROW_INFO * pStartRow = NULL); + + void setShutdown( + FLMBOOL * pbShutdown); + + FLMBOOL * getShutdown( void); + + RCODE setCurrentRow( + DME_ROW_INFO * pCurRow, + FLMUINT uiCurRow); + + DME_ROW_INFO * getCurrentRow( + FLMUINT * puiCurRow); + + RCODE setFirstRow( + DME_ROW_INFO * pRow); + + RCODE setControlFlags( + DME_ROW_INFO * pCurRow, + FLMUINT uiFlags); + + RCODE getControlFlags( + DME_ROW_INFO * pCurRow, + FLMUINT * puiFlags); + + void setDisplayHook( + F_DOMEDIT_DISP_HOOK pDispHook, + void * DispData); + + void setKeyHook( + F_DOMEDIT_KEY_HOOK pKeyHook, + void * KeyData); + + void setHelpHook( + F_DOMEDIT_HELP_HOOK pHelpHook, + void * HelpData); + + void setEventHook( + F_DOMEDIT_EVENT_HOOK pEventHook, + void * EventData); + + F_Db * getDb( void); + + FLMUINT getCollection( void); + + FLMUINT getLastKey( void); + + RCODE getNumber( + char * pszBuf, + FLMUINT64 * pui64Value, + FLMINT64 * pi64Value); + + RCODE getCollectionNumber( + char * pszCollectionName, + FLMUINT * puiCollectionNum); + + RCODE getIndexNumber( + char * pszIndexName, + FLMUINT * puiIndexNum); + + RCODE retrieveNodeFromDb( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId); + + RCODE requestInput( + char * pszMessage, + char * pszResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar); + + RCODE displayMessage( + char * pszMessage, + RCODE rcOfMessage, + FLMUINT * puiTermChar, + FLMUINT uiBackground, + FLMUINT uiForeground); + + RCODE globalConfig( + FLMUINT uiOption); + + RCODE createStatusWindow( + char * pszTitle, + FLMUINT uiBack, + FLMUINT uiFore, + FLMUINT * puiCols, + FLMUINT * puiRows, + FTX_WINDOW ** ppWindow); + + + FLMUINT getULX( void); + FLMUINT getULY( void); + + RCODE openNewDb(); + + RCODE refreshNameTable( void); + + RCODE retrieveDocumentList( + FLMUINT uiCollection, + FLMUINT64 * pui64NodeId, + FLMUINT * puiTermChar); + + RCODE getDomNode( + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + F_DOMNode ** ppDomNode); + + void setDocList( + FLMBOOL bDocList + ) + { + m_bDocList = bDocList; + } + + DME_DISP_COLUMN * getDispColumns( void) + { + return &m_dispColumns[0]; + } + + FINLINE FLMUINT getEditCanvasCols( void); + + RCODE getNodeName( + F_DOMNode * pDOMNode, + DME_ROW_INFO * pRow, + FLMUNICODE ** ppuzName, + FLMUINT * puiBufSize = NULL); + + RCODE getNodeValue( + F_DOMNode * pDOMNode, + FLMUNICODE ** ppuzValue, + FLMUINT * puiBufSize = NULL, + FLMBOOL bStartTrans = TRUE); + + RCODE beginTransaction( + eDbTransType eTransType); + + RCODE commitTransaction( void); + + RCODE abortTransaction( void); + + void doQuery( + char * pszQuery, + FLMUINT uiQueryBufSize); + + RCODE indexList( void); + + RCODE editIndexRow( + DME_ROW_INFO * pCurRow); + + RCODE editIndexNode( + DME_ROW_INFO * pCurRow); + + FLMBOOL canEditRow( + DME_ROW_INFO * pCurRow); + + void releaseAllRows( void); + + FINLINE FLMBOOL isDocList( void) + { + return( m_bDocList); + } + + FINLINE FLMUINT getCanvasCols( void) + { + return m_uiEditCanvasCols; + } + + RCODE viewOnlyDeleteIxKey( void); + + FINLINE FTX_WINDOW * getStatusWindow( void) + { + return( m_pEditStatusWin); + } + +private: + + /* + Methods + */ + + void checkDocument( + DME_ROW_INFO ** ppDocRow, + DME_ROW_INFO * pRow); + + RCODE asciiUCMixToUC( + char * pszAscii, + FLMUNICODE * puzUnicode, + FLMUINT uiMaxUniChars); + + RCODE UCToAsciiUCMix( + FLMUNICODE * puzUnicode, + char * pszAscii, + FLMUINT uiMaxAsciiChars); + + FINLINE DME_ROW_ANCHOR * getRowAnchor( void); + + FINLINE void setRowAnchor( + DME_ROW_ANCHOR * pRowAnchor); + + RCODE editTextBuffer( + char ** ppszBuffer, + FLMUINT uiBufSize, + FLMUINT * puiTermChar); + + RCODE addDocumentToList( + FLMUINT uiCollection, + FLMUINT64 ui64DocumentId); + + void releaseLastRow( void); + + RCODE expandRow( + DME_ROW_INFO * pRow, + FLMBOOL bOneLevel, + DME_ROW_INFO ** ppLastRow); + + RCODE collapseRow( + DME_ROW_INFO ** ppRow); + + RCODE refreshEditWindow( + DME_ROW_INFO ** ppFirstRow, + DME_ROW_INFO * pCursorRow, + FLMUINT * puiCurRow); + + RCODE refreshRow( + FLMUINT uiRow, + DME_ROW_INFO * pNd, + FLMBOOL bSelected); + + RCODE clearSelections( void); + + + RCODE editRow( + FLMUINT uiRow, + DME_ROW_INFO * pRow, + FLMBOOL bReadOnly = FALSE); + + + FLMBOOL canDeleteRow( + DME_ROW_INFO * pCurRow); + + RCODE addRowToDb( + DME_ROW_INFO * pCurRow, + FLMUINT uiCollection, + FLMBOOL bAddInBackground, + FLMBOOL bStartThread, + FLMUINT * pudDrn); + + RCODE modifyRowInDb( + DME_ROW_INFO * pCurRow, + FLMBOOL bAddInBackground, + FLMBOOL bStartThread); + + FLMBOOL isExiting( void); + + RCODE _insertRow( + DME_ROW_INFO * pRecord, + DME_ROW_INFO * pStartNd = NULL); + + RCODE selectCollection( + FLMUINT * puiCollection, + FLMUINT * puiTermChar); + + RCODE selectIndex( + FLMUINT uiFlags, + FLMUINT * puiIndex, + FLMUINT * puiTermChar); + + RCODE displayAttributes( + DME_ROW_INFO * pRow); + + RCODE displayNodeInfo( + DME_ROW_INFO * pRow); + + RCODE exportNode( + DME_ROW_INFO * pRow); + + RCODE showHelp( + FLMUINT * puiKeyRV = NULL); + + RCODE getNextTitle( + DME_ROW_INFO * pRow, + DME_ROW_INFO ** ppNewRow); + + RCODE getPrevTitle( + DME_ROW_INFO * pRow, + DME_ROW_INFO ** ppNewRow); + + RCODE selectElementAttribute( + DME_ROW_INFO * pRow, + FLMUINT * puiTermChar); + + RCODE deleteRow( + DME_ROW_INFO ** ppCurRow); + + RCODE addSomething( + DME_ROW_INFO ** ppCurRow); + + RCODE createDocumentNode( + DME_ROW_INFO ** ppCurRow); + + RCODE createRootElementNode( + DME_ROW_INFO ** ppCurRow); + + RCODE createElementNode( + DME_ROW_INFO ** ppCurRow); + + RCODE createTextNode( + DME_ROW_INFO ** ppRow, + eDomNodeType eNodeType); + + RCODE createAttributeNode( + DME_ROW_INFO ** ppCurRow); + + RCODE selectNodeType( + eDomNodeType * peNodeType, + FLMUINT * puiTermChar); + + RCODE buildNewRow( + FLMINT iLevel, + F_DOMNode * pDomNode, + DME_ROW_INFO ** ppNewRow); + + RCODE selectTag( + FLMUINT uiTagType, + FLMUINT * puiTag, + FLMUINT * puiTermChar); + + RCODE selectEncDef( + FLMUINT uiTagType, + FLMUINT * puiEncDefId, + FLMUINT * puiTermChar); + + RCODE addComment( + DME_ROW_INFO ** ppCurRow, + const char * pucFormat, ...); + + /* + Data + */ + + DME_ROW_INFO * m_pScrFirstRow; + DME_ROW_INFO * m_pScrLastRow; + DME_ROW_INFO * m_pCurRow; + DME_ROW_ANCHOR * m_pRowAnchor; + DME_ROW_INFO * m_pDocList; + DME_ROW_INFO * m_pCurDoc; + F_Db * m_pDb; + FLMBOOL m_bOpenedDb; + FLMUINT m_uiCollection; + FLMUINT m_uiLastKey; + char m_szTitle[ F_DOMEDIT_MAX_TITLE_SIZE + 1]; + FLMUINT m_uiCurRow; + FLMUINT m_uiEditCanvasRows; + FLMUINT m_uiEditCanvasCols; + FLMUINT m_uiNumRows; + FLMUINT m_uiULX; + FLMUINT m_uiULY; + FLMUINT m_uiLRX; + FLMUINT m_uiLRY; + FLMBOOL m_bReadOnly; + FLMBOOL m_bSetupCalled; + FLMBOOL * m_pbShutdown; + FLMBOOL m_bMonochrome; + FTX_SCREEN * m_pScreen; + FTX_WINDOW * m_pEditWindow; + FTX_WINDOW * m_pEditStatusWin; + F_DomEditor * m_pParent; + F_DOMEDIT_DISP_HOOK m_pDisplayHook; + void * m_DisplayData; + F_DOMEDIT_KEY_HOOK m_pKeyHook; + void * m_KeyData; + F_DOMEDIT_HELP_HOOK m_pHelpHook; + void * m_HelpData; + F_DOMEDIT_EVENT_HOOK m_pEventHook; + void * m_EventData; + FLMBOOL m_bDocList; + F_NameTable * m_pNameTable; + DME_DISP_COLUMN m_dispColumns[ 16]; +}; + + +FINLINE FLMUINT F_DomEditor::getEditCanvasCols( void) +{ + return m_uiEditCanvasCols; +} + +FINLINE void F_DomEditor::setShutdown( + FLMBOOL * pbShutdown) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pbShutdown = pbShutdown; +} + +FINLINE FLMBOOL * F_DomEditor::getShutdown( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pbShutdown); +} + +FINLINE FLMBOOL F_DomEditor::isExiting( void) +{ + flmAssert( m_bSetupCalled == TRUE); + if( m_pbShutdown && *m_pbShutdown) + { + return( TRUE); + } + + return( FALSE); +} + +FINLINE FLMBOOL F_DomEditor::isMonochrome( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_bMonochrome); +} + +FINLINE void F_DomEditor::setParent( + F_DomEditor * pParent) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pParent = pParent; +} + +FINLINE F_DomEditor * F_DomEditor::getParentEditor( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pParent); +} + +FINLINE void F_DomEditor::setReadOnly( + FLMBOOL bReadOnly) +{ + flmAssert( m_bSetupCalled == TRUE); + m_bReadOnly = bReadOnly; +} + +FINLINE DME_ROW_INFO * F_DomEditor::getScrFirstRow( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pScrFirstRow); +} + +FINLINE DME_ROW_INFO * F_DomEditor::getScrLastRow( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pScrLastRow); +} + +FINLINE FLMUINT F_DomEditor::getCursorRow( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiCurRow); +} + +FINLINE DME_ROW_ANCHOR * F_DomEditor::getRowAnchor( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pRowAnchor); +} + +FINLINE void F_DomEditor::setRowAnchor( + DME_ROW_ANCHOR * pRowAnchor) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pRowAnchor = pRowAnchor; +} + +FINLINE FLMUINT F_DomEditor::getNumRows( void) +{ + flmAssert( m_bSetupCalled == TRUE && m_pEditWindow != NULL); + return( m_uiEditCanvasRows); +} + + +FINLINE F_Db * F_DomEditor::getDb( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_pDb); +} + +FINLINE FLMUINT F_DomEditor::getCollection( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiCollection); +} + +FINLINE FLMUINT F_DomEditor::getLastKey( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiLastKey); +} + +FINLINE FLMUINT F_DomEditor::getULX( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiULX); +} + +FINLINE FLMUINT F_DomEditor::getULY( void) +{ + flmAssert( m_bSetupCalled == TRUE); + return( m_uiULY); +} + +FINLINE void F_DomEditor::setDisplayHook( + F_DOMEDIT_DISP_HOOK pDispHook, + void * DispData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pDisplayHook = pDispHook; + m_DisplayData = DispData; +} + +FINLINE void F_DomEditor::setKeyHook( + F_DOMEDIT_KEY_HOOK pKeyHook, + void * KeyData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pKeyHook = pKeyHook; + m_KeyData = KeyData; +} + +FINLINE void F_DomEditor::setHelpHook( + F_DOMEDIT_HELP_HOOK pHelpHook, + void * HelpData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pHelpHook = pHelpHook; + m_HelpData = HelpData; +} + +FINLINE void F_DomEditor::setEventHook( + F_DOMEDIT_EVENT_HOOK pEventHook, + void * EventData) +{ + flmAssert( m_bSetupCalled == TRUE); + m_pEventHook = pEventHook; + m_EventData = EventData; +} + + +/* +Prototypes +*/ +RCODE F_DomEditorDefaultDispHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals); + +RCODE F_DomEditorViewOnlyKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +RCODE F_DomEditorSelectionKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +RCODE F_DomEditorFileKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +FLMBOOL domDisplayNodeInfo( + FTX_WINDOW * pWindow, + char * pszOutputFileName, + IF_Db * pDb, + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMBOOL bDoSubTree, + FLMBOOL bWaitForKeystroke); + +FLMBOOL domDisplayEntryInfo( + FTX_WINDOW * pWindow, + char * pszOutputFileName, + IF_Db * pDb, + FLMUINT64 ui64NodeId, + FLMBOOL bWaitForKeystroke); + +FLMBOOL domDisplayBTreeInfo( + FTX_WINDOW * pWindow, + char * pszOutputFileName, + IF_Db * pDb, + FLMUINT uiLfNum, + FLMBOOL bDoCollection, + FLMBOOL bDoIndex, + FLMBOOL bWaitForKeystroke); + +#endif diff --git a/version5/util/domnodetestsrv.cpp b/version5/util/domnodetestsrv.cpp new file mode 100644 index 0000000..620dce1 --- /dev/null +++ b/version5/util/domnodetestsrv.cpp @@ -0,0 +1,1618 @@ +//------------------------------------------------------------------------------ +// Desc: DOM Node tests +// +// 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: domnodetestsrv.cpp 3116 2006-01-19 13:31:53 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\TST.DB" +#else + #define DB_NAME_STR "tst.db" +#endif + +#define BUILTIN_ATTRIBUTES (XFLM_LAST_RESERVED_ATTRIBUTE_TAG - \ + XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + 1) + +#define NUM_CHILD_NODES 500 + +struct UTF8LenPair +{ + FLMBYTE * pucUTF8Str; + FLMUINT uiNumChars; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class IDOMNodeTestImpl : public TestBase +{ +public: + + const char * getName( void); + + RCODE execute( void); + + RCODE domNodeWorkout( void); + + RCODE pendingNodesTest( void); + + RCODE strValLengthTests( void); + + RCODE uniqueChildTests( void); + + RCODE concurrentValueModifyTests( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( + IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IDOMNodeTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IDOMNodeTestImpl::getName( void) +{ + return( "DOM Node Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = domNodeWorkout())) + { + goto Exit; + } + + if( RC_BAD( rc = pendingNodesTest())) + { + goto Exit; + } + + if( RC_BAD( rc = strValLengthTests())) + { + goto Exit; + } + + if( RC_BAD( rc = uniqueChildTests())) + { + goto Exit; + } + + /* VISIT - enable when concurrent streaming fails with correct error codes + if ( RC_BAD( rc = concurrentValueModifyTests())) + { + goto Exit; + } + */ + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::domNodeWorkout( void) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pDocument = NULL; + IF_DOMNode * pDocRoot = NULL; + IF_DOMNode * pDOMNode1 = NULL; + IF_DOMNode * pDOMNode2 = NULL; + IF_DOMNode * pAttr = NULL; + int ps = 0; + FLMUINT uiTag = 0; + FLMBOOL b = FALSE; + FLMUINT uiDataType = 0; + FLMUINT64 ui64ParentID = 0; + FLMUINT uiLoop = 0; + FLMBOOL bTransStarted = FALSE; + FLMBOOL bDibCreated = FALSE; + FLMUINT64 ui64NodeId; + eDomNodeType eNodeType; + FLMUINT uiValue = 12345; + FLMUINT64 ui64Value = 123456; + FLMINT iValue = -12345; + FLMINT64 i64Value = -12345; + const char * pszValue = "Native\\UTF8 Value"; + FLMUNICODE puzValue[] = {85,110,105,99,111,100,101,0}; + FLMBYTE pucBinValue[] = {0x01, 0x02, 0x03, 0x04, 0x05}; + FLMUINT uiValueRV = 0; + FLMUINT64 ui64ValueRV = 0; + FLMINT iValueRV = 0; + FLMINT64 i64ValueRV = 0; + char pszValueRV[ 128]; + FLMUNICODE puzValueRV[] = {0,0,0,0,0,0,0,0}; + FLMBYTE pucBinValueRV[ 64]; + FLMUINT uiBufferBytes = sizeof( pszValueRV); + FLMUINT64 ui64Tmp; + char szTemp[ 128]; + + //PART #1 - Initialize XFLAIM + + beginTest( + "Init", + "Perform initializations so we can exercise the DOMNode", + "(1)Get DbSystem class factory (2)call init() (3)create XFLAIM db", + "No Additional Info."); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + MAKE_ERROR_STRING( "Failed to initialize test state.", m_szDetails, ps); + goto Exit; + } + bDibCreated = TRUE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.",m_szDetails, ps); + goto Exit; + } + bTransStarted = TRUE; + + endTest("PASS"); + + beginTest( + "DOMNode Workout", + "Exercise the DOMNode interface", + "(1)Create a document (2)Create child nodes (3)Create attributes " + "(4)Set and verify attribute values (5)Iterate through nodes", + ""); + + if ( RC_BAD( rc = m_pDb->createDocument( + XFLM_DATA_COLLECTION, + &pDocument))) + { + MAKE_ERROR_STRING( "createDocument failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pDocument->createNode( + m_pDb, + ELEMENT_NODE, + ELM_ELEMENT_TAG, + XFLM_FIRST_CHILD, + &pDocRoot))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + // Create the first child + + if ( RC_BAD( rc = pDocRoot->createNode( + m_pDb, + ELEMENT_NODE, + ELM_ELEMENT_TAG, + XFLM_FIRST_CHILD, + &pDOMNode1))) + { + MAKE_ERROR_STRING( "createDocument failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDocRoot->getDocumentId( m_pDb, &ui64NodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = pDocument->getNodeId( m_pDb, &ui64Tmp))) + { + goto Exit; + } + + if( ui64Tmp != ui64NodeId) + { + MAKE_GENERIC_ERROR_STRING64( "Incorrect root Id", m_szDetails, + ui64NodeId); + + goto Exit; + } + + // Create a bunch of siblings and add attributes to them + + for ( uiLoop = 0; uiLoop < NUM_CHILD_NODES - 1; uiLoop++) + { + // Record the iteration number for output purposes. + + sprintf( szTemp, " Iteration #%lu.", uiLoop); + + // Make a bunch of sibling nodes + + if ( RC_BAD( rc = pDOMNode1->createNode( + m_pDb, + ELEMENT_NODE, + ELM_ELEMENT_TAG, + (uiLoop % 2) ? XFLM_NEXT_SIB : XFLM_PREV_SIB, + &pDOMNode2))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + eNodeType = pDOMNode2->getNodeType(); + + if( eNodeType != ELEMENT_NODE) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Illegal node type.", + m_szDetails, eNodeType); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( RC_BAD( rc = pDOMNode2->hasChildren(m_pDb, &b))) + { + MAKE_ERROR_STRING( "hasChildren failed.", m_szDetails, rc); + goto Exit; + } + + if (b) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Node erroneously claims to have children.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + uiTag = XFLM_FIRST_RESERVED_ATTRIBUTE_TAG + (uiLoop % BUILTIN_ATTRIBUTES); + + if ( RC_BAD( rc = pDOMNode2->createAttribute( + m_pDb, + uiTag, + &pAttr))) + { + MAKE_ERROR_STRING( "createAttribute failed.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( RC_BAD( rc = pDOMNode2->hasAttribute( + m_pDb, + uiTag))) + { + if( rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "Node is missing an attribute", + m_szDetails, rc); + } + else + { + MAKE_ERROR_STRING( "hasAttribute failed.", m_szDetails, rc); + } + strcat( m_szDetails, szTemp); + goto Exit; + } + + // Look up the tag's data type and set an appropriate value + // Then retrieve it again for verification + + if ( RC_BAD( rc = pAttr->getDataType( + m_pDb, + &uiDataType))) + { + MAKE_ERROR_STRING( "getDataType failed.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + switch( uiDataType) + { + case XFLM_NUMBER_TYPE: + switch( uiLoop % 4) + { + case 0: + + if ( RC_BAD( rc = pAttr->setUINT( + m_pDb, + uiValue))) + { + MAKE_ERROR_STRING( "setUINT failed.", + m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUINT( m_pDb, &uiValueRV))) + { + MAKE_ERROR_STRING( "getUINT failed.", + m_szDetails, rc); + goto Exit; + } + + if ( uiValue != uiValueRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "UINT Data corruption detected.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + case 1: + + if ( RC_BAD( rc = pAttr->setUINT64( + m_pDb, + ui64Value + ))) + { + MAKE_ERROR_STRING( "setUINT64 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getUINT64( m_pDb, &ui64ValueRV))) + { + MAKE_ERROR_STRING( "getUINT64 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( ui64Value != ui64ValueRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "UINT64 Data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + case 2: + + if ( RC_BAD( rc = pAttr->setINT( + m_pDb, + iValue + ))) + { + MAKE_ERROR_STRING( "setINT failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pAttr->getINT( m_pDb, &iValueRV))) + { + MAKE_ERROR_STRING( "getINT failed.", + m_szDetails, rc); + goto Exit; + } + + if ( iValue != iValueRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "INT Data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + case 3: + + if ( RC_BAD( rc = pAttr->setINT64( + m_pDb, + i64Value + ))) + { + MAKE_ERROR_STRING( "setINT64 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD ( rc = pAttr->getINT64( m_pDb, &i64ValueRV))) + { + MAKE_ERROR_STRING( "getINT64 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( i64Value != i64ValueRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "INT64 Data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + } + break; + case XFLM_TEXT_TYPE: + switch( uiLoop % 3) + { + case 0: + if ( RC_BAD( rc = pAttr->setUTF8( m_pDb, (FLMBYTE *)pszValue))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUTF8( + m_pDb, (FLMBYTE *)pszValueRV, + sizeof(pszValueRV), 0, ~((FLMUINT)0)))) + { + MAKE_ERROR_STRING( "getUTF8 error", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( strcmp( pszValue, pszValueRV) != 0) + { + rc = NE_XFLM_FAILURE; + MAKE_ERROR_STRING( "Native Data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + case 1: + + if (RC_BAD( rc = pAttr->setUnicode( + m_pDb, + puzValue + ))) + { + MAKE_ERROR_STRING( "setUnicode failed.", + m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = pAttr->getUnicode( + m_pDb, + puzValueRV, + sizeof(puzValueRV), + 0, + ~((FLMUINT)0), + NULL))) + { + MAKE_ERROR_STRING( "getUnicode error", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + { + FLMUNICODE * puzTmp1 = puzValue; + FLMUNICODE * puzTmp2 = puzValueRV; + + while (*puzTmp1 && *puzTmp2 && *puzTmp1 == *puzTmp2) + { + puzTmp1++; + puzTmp2++; + } + if ( *puzTmp1 || *puzTmp2) + { + rc = NE_XFLM_FAILURE; + MAKE_ERROR_STRING( "Unicode data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + break; + case 2: + + if (RC_BAD( rc = pAttr->setUTF8( + m_pDb, + (FLMBYTE *)pszValue + ))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + uiBufferBytes = sizeof( pszValueRV); + + if (RC_BAD( rc = pAttr->getUTF8( + m_pDb, + (FLMBYTE *)pszValueRV, + uiBufferBytes, + 0, + ~((FLMUINT)0), + NULL))) + { + MAKE_ERROR_STRING( "getUTF8 error", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( strcmp( pszValue, pszValueRV) != 0) + { + rc = NE_XFLM_FAILURE; + MAKE_ERROR_STRING( "UTF8 data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + + } + break; + + case XFLM_BINARY_TYPE: + + if (RC_BAD( rc = pAttr->setBinary( + m_pDb, + pucBinValue, + 10 + ))) + { + MAKE_ERROR_STRING( "setBinary failed.", + m_szDetails, rc); + goto Exit; + } + + if RC_BAD( rc = pAttr->getBinary( m_pDb, pucBinValueRV, 0, 10, &uiValueRV)) + { + MAKE_ERROR_STRING( "getBinary failed.", + m_szDetails, rc); + goto Exit; + } + + if ( memcmp( pucBinValue, pucBinValueRV, 10)) + { + rc = NE_XFLM_FAILURE; + MAKE_ERROR_STRING( "Binary data corruption detected", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + break; + + default: + break; + } + break; + } + + // Since there's only one attribute, either one of these functions will do + if ( (uiLoop % 2) == 0) + { + if ( RC_BAD( rc = pDOMNode2->getFirstAttribute( + m_pDb, + &pAttr))) + { + MAKE_ERROR_STRING( "getFirstAttribute failed", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + else + { + if ( RC_BAD( rc = pDOMNode2->getAttribute( + m_pDb, + uiTag, + &pAttr + ))) + { + MAKE_ERROR_STRING( "getAttribute failed", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + + // We gave these nodes one and only one attribute + // The attributes should have no siblings + + if ( ( rc = pAttr->getPreviousSibling( + m_pDb, + &pDOMNode2)) != NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "getPreviousSibling returned invalid rc. ", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + if ( ( rc = pAttr->getNextSibling( + m_pDb, + &pDOMNode2)) != NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "getNextSibling returned invalid rc. ", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + } + + if( RC_BAD( rc = pDocRoot->getNodeId( m_pDb, &ui64ParentID))) + { + goto Exit; + } + + if( RC_BAD( rc = pDocRoot->hasChildren(m_pDb, &b))) + { + MAKE_ERROR_STRING( "hasChildren failed. ", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if( !b) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Document root erroneously claims to have no children. ", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + // Reposition to the first child under the document root, + // iterate through its children (first->last) and perform + // various DOMNode ops + + for ( uiLoop = 0; uiLoop < NUM_CHILD_NODES; uiLoop++) + { + sprintf( szTemp, " Iteration #%lu.", uiLoop); + + if ( uiLoop == 0) + { + // Initialization + + if ( RC_BAD( rc = pDocRoot->getFirstChild( + m_pDb, + &pDOMNode1))) + { + MAKE_ERROR_STRING( "getFirstChild failed.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + else + { + // Move to the next sibling + + if ( RC_BAD( rc = pDOMNode1->getNextSibling( + m_pDb, + &pDOMNode1))) + { + MAKE_ERROR_STRING( "getNextSibling failed.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + + if ( RC_BAD( rc = pDOMNode1->getParentId(m_pDb, &ui64ValueRV))) + { + MAKE_ERROR_STRING( "getParentId failed.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( ui64ParentID != ui64ValueRV) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Incorrect parent ID.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + rc = NE_XFLM_FAILURE; + goto Exit; + } + } + + // There should be no more siblings + + if ( (rc = pDOMNode1->getNextSibling( + m_pDb, + &pDOMNode2)) != NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "Invalid rc returned from getNextSibling.", + m_szDetails, rc); + strcat( m_szDetails, szTemp); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + endTest("PASS"); + + beginTest( + "DOMNode Iteration/Deletion", + "Iterate and delete the DOMNodes", + "Move backwards through the child nodes and delete them", + ""); + + // Move backwards through the siblings deleting them (except the last one) + + for( uiLoop = 0; uiLoop < NUM_CHILD_NODES; uiLoop++) + { + if ( uiLoop == 0) + { + // Initialization + + if ( RC_BAD( rc = pDocRoot->getLastChild( + m_pDb, + &pDOMNode1))) + { + MAKE_ERROR_STRING( "getLastChild failed.", m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + } + else + { + // Move to the prev sibling + + if ( RC_BAD( rc = pDOMNode1->getPreviousSibling( + m_pDb, + &pDOMNode2))) + { + MAKE_ERROR_STRING( "getPreviousSibling failed.", m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + if ( RC_BAD( rc = pDOMNode1->deleteNode( m_pDb))) + { + MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); + strcat( m_szDetails, szTemp); + goto Exit; + } + + pDOMNode1->Release(); + pDOMNode1 = pDOMNode2; + pDOMNode2 = NULL; + } + } + + // Test error condition + + if ( ( rc = pDOMNode1->getPreviousSibling( + m_pDb, + &pDOMNode2)) != NE_XFLM_DOM_NODE_NOT_FOUND) + { + MAKE_ERROR_STRING( "getPreviousSibling returned invalid rc.", m_szDetails, rc); + if ( RC_OK( rc)) + { + rc = NE_XFLM_FAILURE; + } + goto Exit; + } + + //Delete the last DOM node + + if ( RC_BAD( rc = pDOMNode1->deleteNode(m_pDb))) + { + MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); + goto Exit; + } + + pDOMNode1->Release(); + pDOMNode1 = NULL; + + // Test document iteration + // pDocument - 1st doc, pDOMNode1 - next doc + + if ( RC_BAD( rc = m_pDb->createDocument( + XFLM_DATA_COLLECTION, + &pDOMNode1))) + { + MAKE_ERROR_STRING( "createDocument failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pDocument->getNextDocument( + m_pDb, + &pDOMNode2))) + { + MAKE_ERROR_STRING( "getNextDocument failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pDOMNode1->getPreviousDocument( + m_pDb, + &pDOMNode2))) + { + MAKE_ERROR_STRING( "getPreviousDocument failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransStarted = FALSE; + + endTest("PASS"); + +Exit: + + if ( RC_BAD( rc )) + { + endTest("FAIL"); + } + + if ( bTransStarted) + { + m_pDb->transCommit(); + } + if ( pDOMNode1) + { + pDOMNode1->Release(); + } + if ( pDOMNode2) + { + pDOMNode2->Release(); + } + if ( pDocument) + { + pDocument->Release(); + } + if (pDocRoot) + { + pDocRoot->Release(); + } + if ( pAttr) + { + pAttr->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + + return rc; +} + +#define NUM_DOCS 2000 + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::pendingNodesTest( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bTransActive = FALSE; + FLMUINT64 * pui64Docs = NULL; + FLMUINT uiLoop; + IF_DOMNode * pDoc = NULL; + FLMBOOL bDibCreated = FALSE; + + beginTest( + "Pending Nodes Error Test", + "Delete 2000 documents within a single transaction " + "and ensure NE_XFLM_TOO_MANY_PENDING_NODES is not returned " + "to verify DEFECT000400386 has been fixed", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + if ( RC_BAD( rc = f_alloc( + NUM_DOCS * sizeof(FLMUINT64), &pui64Docs))) + { + MAKE_ERROR_STRING( "Failed to allocate docId array", m_szDetails, rc); + goto Exit; + } + + for( uiLoop = 0; uiLoop < NUM_DOCS; uiLoop++) + { + if (RC_BAD( rc = m_pDb->createDocument( + XFLM_DATA_COLLECTION, + &pDoc))) + { + MAKE_ERROR_STRING( "createDocument failed.", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = m_pDb->documentDone( pDoc))) + { + MAKE_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pDoc->getNodeId( m_pDb, &pui64Docs[ uiLoop]))) + { + MAKE_ERROR_STRING( "getNodeId failed.", m_szDetails, rc); + goto Exit; + } + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = FALSE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + for ( uiLoop = 0; uiLoop < NUM_DOCS; uiLoop++) + { + if ( RC_BAD( rc = m_pDb->getNode( + XFLM_DATA_COLLECTION, + pui64Docs[uiLoop], + &pDoc))) + { + MAKE_ERROR_STRING( "getNode failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pDoc->deleteNode( m_pDb))) + { + MAKE_ERROR_STRING( "deleteNode failed.", m_szDetails, rc); + goto Exit; + } + } + + if ( RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = FALSE; + endTest("PASS"); + +Exit: + + if ( bTransActive) + { + if ( RC_OK( rc)) + { + rc = m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if ( pDoc) + { + pDoc->Release(); + } + + if ( pui64Docs) + { + f_free( &pui64Docs); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::strValLengthTests( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBYTE pucUTF8Odysseus[] = { + 0xEF, 0xBB, 0xBF, 0xCE, 0xA4, 0xCE, 0xB7, 0x20, 0xCE, 0xB3, 0xCE, 0xBB, 0xCF, + 0x8E, 0xCF, 0x83, 0xCF, 0x83, 0xCE, 0xB1, 0x20, 0xCE, 0xBC, 0xCE, 0xBF, 0xCF, + 0x85, 0x20, 0xCE, 0xAD, 0xCE, 0xB4, 0xCF, 0x89, 0xCF, 0x83, 0xCE, 0xB1, 0xCE, + 0xBD, 0x20, 0xCE, 0xB5, 0xCE, 0xBB, 0xCE, 0xBB, 0xCE, 0xB7, 0xCE, 0xBD, 0xCE, + 0xB9, 0xCE, 0xBA, 0xCE, 0xAE, 0x00}; + // First few words from a Greek poem (29 actual chars + 1 null) + FLMBYTE pucUTF8BronzeHorse[] = { + 0xEF, 0xBB, 0xBF, 0xD0, 0x9D, 0xD0, 0xB0, 0x20, 0xD0, 0xB1, 0xD0, 0xB5, 0xD1, 0x80, 0xD0, 0xB5, + 0xD0, 0xB3, 0xD1, 0x83, 0x20, 0xD0, 0xBF, 0xD1, 0x83, 0xD1, 0x81, 0xD1, 0x82, 0xD1, 0x8B, 0xD0, + 0xBD, 0xD0, 0xBD, 0xD1, 0x8B, 0xD1, 0x85, 0x20, 0xD0, 0xB2, 0xD0, 0xBE, 0xD0, 0xBB, 0xD0, 0xBD, 0x00}; + // Pushkin's Bronze Horse (Russian) (24 actual chars + 1 null) + + UTF8LenPair testStrings[] = + { + { pucUTF8Odysseus, 30}, + { pucUTF8BronzeHorse, 25} + }; + + IF_DOMNode * pNode = NULL; + FLMUINT uiNumChars; + FLMUINT uiLoop; + FLMBOOL bTransActive = FALSE; + char szTestName[100]; + FLMBOOL bDibCreated = FALSE; + + beginTest( + "String Value Length Test Setup", + "Set up for string value length tests", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransActive = TRUE; + + if ( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DATA_COLLECTION, + ELM_DOCUMENT_TITLE_TAG, // chosen because it is a text type + &pNode))) + { + MAKE_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + for( uiLoop = 0; + uiLoop < sizeof(testStrings)/sizeof(testStrings[0]); + uiLoop++) + { + f_sprintf( szTestName, "String Value Length Test %u", uiLoop); + + beginTest( + szTestName, + "Set a UTF8 value in a node then call getUTF8 and getUnicode " + "without buffers to get the character count", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = pNode->setUTF8( m_pDb, testStrings[uiLoop].pucUTF8Str))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = pNode->getUTF8( + m_pDb, NULL, 0, 0, 0, &uiNumChars))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiNumChars != testStrings[uiLoop].uiNumChars) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Unexpected string length.", m_szDetails, rc); + goto Exit; + } + + // call getUnicode with no buffer to validate number of chars + + if ( RC_BAD( rc = pNode->getUnicode( + m_pDb, + NULL, + 0, 0, 0, &uiNumChars))) + { + MAKE_ERROR_STRING( "getUnicode failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiNumChars != testStrings[uiLoop].uiNumChars) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_ERROR_STRING( "Unexpected string length.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + } + +Exit: + + if ( bTransActive) + { + if ( RC_OK( rc)) + { + rc = m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if ( pNode) + { + pNode->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::uniqueChildTests( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiDefNum1 = 0; + FLMUINT uiDefNum2; + IF_DOMNode * pRootElement = NULL; + IF_DOMNode * pElement = NULL; + IF_DOMNode * pUniqueElement = NULL; + char szName [80]; + FLMUINT uiNumToUse; + FLMUINT uiLoop; + FLMBOOL bStartedTrans = FALSE; + FLMBOOL bDibCreated = FALSE; + FLMUINT uiRefCount; + + beginTest( + "Unique Child Elements Database Check Test", + "Make sure a check succeeds on a dib with unique child element lists", + "Self explanatory", + "No Additional Details."); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + // Start an update transaction + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + if( RC_BAD( rc = m_pDb->createUniqueElmDef( + NULL, "unique_element", &uiDefNum1))) + { + MAKE_FLM_ERROR_STRING( "createUniqueElmDef failed", m_szDetails, rc); + goto Exit; + } + + // Create 200 element unique element definitions. + + uiDefNum2 = uiDefNum1 + 1; + for (uiLoop = 0; uiLoop < 200; uiLoop++) + { + uiNumToUse = uiDefNum2 + uiLoop; + + f_sprintf( szName, "c_element%u", (unsigned)uiLoop); + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, szName, XFLM_NODATA_TYPE, &uiNumToUse))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + } + + // Create a root node, with two child nodes. + + if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, + ELM_ELEMENT_TAG, &pRootElement))) + { + MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); + goto Exit; + } + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum1, XFLM_FIRST_CHILD, + &pUniqueElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + for (uiLoop = 0; uiLoop < 200; uiLoop++) + { + // Should not allow a data node. + + if( RC_BAD( rc = pUniqueElement->createNode( m_pDb, + ELEMENT_NODE, uiDefNum2 + uiLoop, + XFLM_LAST_CHILD, &pElement, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + } + + pRootElement->Release(); + pRootElement = NULL; + + pElement->Release(); + pElement = NULL; + + pUniqueElement->Release(); + pUniqueElement = NULL; + + if ( RC_BAD( rc = rc = m_pDb->transCommit())) + { + goto Exit; + } + bStartedTrans = FALSE; + + uiRefCount = m_pDb->Release(); + flmAssert( uiRefCount == 0); + m_pDb = NULL; + + // do a check + +// if ( RC_BAD( rc = m_pDbSystem->dbCheck( // VISIT +// DB_NAME_STR, +// NULL, +// NULL, +// XFLM_DO_LOGICAL_CHECK, +// NULL, +// NULL))) +// { +// MAKE_FLM_ERROR_STRING("dbCheck failed", m_szDetails, rc); +// goto Exit; +// } + + endTest("PASS"); + +Exit: + + if ( bStartedTrans) + { + if (RC_OK(rc)) + { + rc = m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if (pRootElement) + { + pRootElement->Release(); + } + + if (pElement) + { + pElement->Release(); + } + + if ( pUniqueElement) + { + pUniqueElement->Release(); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IDOMNodeTestImpl::concurrentValueModifyTests( void) +{ + RCODE rc = NE_XFLM_OK; + IF_DOMNode * pRootElement = NULL; + IF_DOMNode * pBinNode = NULL; + IF_DOMNode * pTextNode = NULL; + IF_DOMNode * pOtherBinNode = NULL; + IF_DOMNode * pOtherNumNode = NULL; + IF_DOMNode * pNumNode = NULL; + FLMBOOL bStartedTrans = FALSE; + FLMUINT uiBinId = 0; + FLMUINT uiTextId = 0; + FLMUINT uiNumId = 0; + FLMUINT uiNewColl = 0; + FLMBYTE pucBinVal[] = {0x01,0x02,0x03,0x04,0x05}; + FLMBOOL bDibCreated = FALSE; + + beginTest( + "Simultaneous Streaming Test Setup", + "Set up the database for the simultaneous streaming tests", + "Self explanatory", + "No Additional Details."); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + // Start an update transaction + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, "bin_val", XFLM_BINARY_TYPE, &uiBinId, NULL))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, "text_val", XFLM_TEXT_TYPE, &uiTextId, NULL))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createElementDef( + NULL, "num_val", XFLM_NUMBER_TYPE, &uiNumId, NULL))) + { + MAKE_FLM_ERROR_STRING( "createElementDef failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createCollectionDef("new_collection", &uiNewColl))) + { + MAKE_FLM_ERROR_STRING( "createCollectionDef failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createRootElement( + uiNewColl, ELM_ELEMENT_TAG, &pRootElement))) + { + MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiBinId, XFLM_FIRST_CHILD, + &pOtherBinNode, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiNumId, XFLM_FIRST_CHILD, + &pOtherNumNode, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, + ELM_ELEMENT_TAG, &pRootElement))) + { + MAKE_FLM_ERROR_STRING( "createRootElement failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiBinId, XFLM_FIRST_CHILD, + &pBinNode, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiTextId, XFLM_FIRST_CHILD, + &pTextNode, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = pRootElement->createNode( m_pDb, + ELEMENT_NODE, uiNumId, XFLM_FIRST_CHILD, + &pNumNode, NULL))) + { + MAKE_FLM_ERROR_STRING( "createNode failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + + if (RC_BAD( rc = m_pDb->transCommit())) + { + MAKE_FLM_ERROR_STRING( "transCommit failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = FALSE; + + beginTest( + "Simultaneous Streaming Test #1", + "Make sure we disallow setting of values while a streaming update has not completed", + "Self explanatory", + "No Additional Details."); + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + if ( RC_BAD( rc = pBinNode->setBinary( + m_pDb, pucBinVal, sizeof(pucBinVal), FALSE))) + { + MAKE_FLM_ERROR_STRING( "setBinary failed", m_szDetails, rc); + goto Exit; + } + + if ( ( rc = pNumNode->setUINT( m_pDb, 123)) != NE_XFLM_INPUT_PENDING) + { + rc = RC_SET( NE_XFLM_FAILURE); + MAKE_FLM_ERROR_STRING( "setUINT was allowed", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_FLM_ERROR_STRING( "transAbort failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = FALSE; + + if (RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS))) + { + MAKE_FLM_ERROR_STRING( "transBegin failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = TRUE; + + if ( ( rc = pBinNode->setBinary( + m_pDb, pucBinVal, sizeof(pucBinVal), FALSE)) != NE_XFLM_INPUT_PENDING) + { + MAKE_FLM_ERROR_STRING( "unexpected rc from setBinary", m_szDetails, rc); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if ( ( rc = pOtherNumNode->setUINT( m_pDb, 123)) != NE_XFLM_INPUT_PENDING) + { + MAKE_FLM_ERROR_STRING( "unexpected rc from setUINT", m_szDetails, rc); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if ( ( rc = pOtherBinNode->setBinary( + m_pDb, pucBinVal, sizeof(pucBinVal), FALSE)) != NE_XFLM_INPUT_PENDING) + { + MAKE_FLM_ERROR_STRING( "unexpected rc from setBinary", m_szDetails, rc); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + if (RC_BAD( rc = m_pDb->transAbort())) + { + MAKE_FLM_ERROR_STRING( "transAbort failed", m_szDetails, rc); + goto Exit; + } + bStartedTrans = FALSE; + + endTest("PASS"); + +Exit: + + if ( pRootElement) + { + pRootElement->Release(); + } + + if ( pBinNode) + { + pBinNode->Release(); + } + + if ( pOtherBinNode) + { + pOtherBinNode->Release(); + } + + if ( pTextNode) + { + pTextNode->Release(); + } + + if ( pOtherNumNode) + { + pOtherNumNode->Release(); + } + + if ( pNumNode) + { + pNumNode->Release(); + } + + if ( bStartedTrans) + { + if (RC_OK(rc)) + { + rc = m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + return( rc); +} diff --git a/version5/util/enctestsrv.cpp b/version5/util/enctestsrv.cpp new file mode 100644 index 0000000..7b24fdf --- /dev/null +++ b/version5/util/enctestsrv.cpp @@ -0,0 +1,1769 @@ +//------------------------------------------------------------------------------ +// Desc: Encryption tests +// +// 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: enctestsrv.cpp 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_NLM) + #define DB_NAME_STR "SYS:\\ENC.DB" + #define BACKUP_NAME_STR "SYS:\\ENC.BAK" +#else + #define DB_NAME_STR "enc.db" + #define BACKUP_NAME_STR "enc.bak" +#endif + +#define PASSWORD "password" +#define BAD_PASSWORD "bad_password" +#define BACKUP_PASSWORD "backup_password" +#define UINT_VAL 1234 +#define INT_VAL -1234 +#define UINT64_VAL (FLMUINT64)5000000 +#define INT64_VAL (FLMINT64)-5000000 +#define NATIVE_VAL "native_val" +#define BIN_VAL ((FLMBYTE *)"\x01\x02\x03") +#define BIN_VAL_LEN 3 + +/**************************************************************************** +Desc: +****************************************************************************/ +class IEncTestImpl : public TestBase +{ +public: + + IEncTestImpl(); + ~IEncTestImpl(); + + const char * getName( void); + + RCODE suite1( void); + + RCODE suite2( void); + + RCODE execute( void); + +private: + + FLMUINT m_uiNumberElm; + FLMUINT m_uiTextElm; + FLMUINT m_uiBinaryElm; + FLMUINT m_uiNoDataElm; + FLMUINT m_uiNumberAttr; + FLMUINT m_uiTextAttr; + FLMUINT m_uiBinaryAttr; + IF_DOMNode * m_pRootNode; + IF_DOMNode * m_pNumberElmNode; + IF_DOMNode * m_pTextElmNode; + IF_DOMNode * m_pBinElmNode; + IF_DOMNode * m_pAttributedNode; + IF_DOMNode * m_pNumberAttrNode; + IF_DOMNode * m_pTextAttrNode; + IF_DOMNode * m_pBinAttrNode; + IF_Backup * m_pBackup; + FLMUINT m_uiAESDef; + FLMUINT m_uiDES3Def; + + RCODE importEncDefs( void); + + RCODE setupElemAndAttrNodes( void); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE getTest( IFlmTest ** ppTest) +{ + RCODE rc = NE_XFLM_OK; + + if( (*ppTest = new IEncTestImpl) == NULL) + { + rc = NE_XFLM_MEM; + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * IEncTestImpl::getName( void) +{ + return( "Encryption Test"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IEncTestImpl::setupElemAndAttrNodes( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_nodata_element", + XFLM_NODATA_TYPE, &m_uiNoDataElm))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createRootElement( XFLM_DATA_COLLECTION, + m_uiNoDataElm, &m_pRootNode))) + { + MAKE_ERROR_STRING( "createRootNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_number_element", + XFLM_NUMBER_TYPE, &m_uiNumberElm))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, + m_uiNumberElm, XFLM_LAST_CHILD, &m_pNumberElmNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_text_element", + XFLM_TEXT_TYPE, &m_uiTextElm))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, + m_uiTextElm, XFLM_LAST_CHILD, &m_pTextElmNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createElementDef( NULL, "generic_binary_element", + XFLM_BINARY_TYPE, &m_uiBinaryElm))) + { + MAKE_ERROR_STRING( "createElementDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, + m_uiBinaryElm, XFLM_LAST_CHILD, &m_pBinElmNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + // Create the element node to hang all the attribute nodes off of + + if( RC_BAD( rc = m_pRootNode->createNode( m_pDb, ELEMENT_NODE, + m_uiNoDataElm, XFLM_LAST_CHILD, &m_pAttributedNode))) + { + MAKE_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_number_attr", + XFLM_NUMBER_TYPE, &m_uiNumberAttr))) + { + MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pAttributedNode->createAttribute( + m_pDb, m_uiNumberAttr, &m_pNumberAttrNode))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_text_attr", + XFLM_TEXT_TYPE, &m_uiTextAttr))) + { + MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pAttributedNode->createAttribute( m_pDb, + m_uiTextAttr, &m_pTextAttrNode))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->createAttributeDef( NULL, "generic_binary_attr", + XFLM_BINARY_TYPE, &m_uiBinaryAttr))) + { + MAKE_ERROR_STRING( "createAttributeDef failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pAttributedNode->createAttribute( m_pDb, + m_uiBinaryAttr, &m_pBinAttrNode))) + { + MAKE_ERROR_STRING( "createAttribute failed.", m_szDetails, rc); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IEncTestImpl::IEncTestImpl( void) +{ + m_uiNumberElm = 0; + m_uiTextElm = 0; + m_uiBinaryElm = 0; + m_uiNoDataElm = 0; + m_uiNumberAttr = 0; + m_uiTextAttr = 0; + m_uiBinaryAttr = 0; + m_pRootNode = NULL; + m_pNumberElmNode = NULL; + m_pTextElmNode = NULL; + m_pBinElmNode = NULL; + m_pAttributedNode = NULL; + m_pNumberAttrNode = NULL; + m_pTextAttrNode = NULL; + m_pBinAttrNode = NULL; + m_pBackup = NULL; + m_uiAESDef = 0; + m_uiDES3Def = 0; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IEncTestImpl::~IEncTestImpl( void) +{ + if( m_pRootNode) + { + m_pRootNode->Release(); + } + + if( m_pNumberElmNode) + { + m_pNumberElmNode->Release(); + } + + if( m_pTextElmNode) + { + m_pTextElmNode->Release(); + } + + if( m_pBinElmNode) + { + m_pBinElmNode->Release(); + } + + if( m_pAttributedNode) + { + m_pAttributedNode->Release(); + } + + if( m_pNumberAttrNode) + { + m_pNumberAttrNode->Release(); + } + + if( m_pTextAttrNode) + { + m_pTextAttrNode->Release(); + } + + if( m_pBinAttrNode) + { + m_pBinAttrNode->Release(); + } + + if( m_pBackup) + { + m_pBackup->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IEncTestImpl::importEncDefs( void) +{ + RCODE rc = NE_XFLM_OK; + char * ppszEncDefs[] = {XFLM_ENC_AES_OPTION_STR, XFLM_ENC_DES3_OPTION_STR}; + FLMUINT puiEncDef[ 2]; + char szEncDef[ 200]; + FLMUINT uiLoop = 0; + + for( + uiLoop = 0; + uiLoop < sizeof(ppszEncDefs)/sizeof(ppszEncDefs[0]); + uiLoop++) + { + f_sprintf( + szEncDef, + "", + ppszEncDefs[uiLoop], + ppszEncDefs[uiLoop]); + + if ( RC_BAD( rc = importBuffer( szEncDef, XFLM_DICT_COLLECTION))) + { + MAKE_ERROR_STRING( "importBuffer failed.", m_szDetails, rc); + goto Exit; + } + + f_sprintf( szEncDef, "%s definition", ppszEncDefs[uiLoop]); + if (RC_BAD( rc = m_pDb->getEncDefId( (char *)szEncDef, + (FLMUINT *)&puiEncDef[ uiLoop]))) + { + goto Exit; + } + } + + m_uiAESDef = puiEncDef[ 0]; + m_uiDES3Def = puiEncDef[ 1]; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IEncTestImpl::execute( void) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = suite1())) + { + goto Exit; + } + + if( RC_BAD( rc = suite2())) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IEncTestImpl::suite1( void) +{ + RCODE rc = NE_XFLM_OK; +#ifdef FLM_USE_NICI + FLMBOOL bDibCreated = FALSE; + FLMBOOL bTransBegun = FALSE; + FLMUINT puiEncDefs[ 4]; + char * ppszEncDefs[ 4]; + FLMUINT uiResult = 0; + FLMUINT64 ui64Result = 0; + FLMINT iResult = 0; + FLMINT64 i64Result = 0; + char szTemp[ 128]; + FLMUINT uiLoop; + FLMUINT uiLoop2; + char * ppszTextSizes[] = {"Medium","Large","Small"}; + char * ppszNativeValues[] = + {"MediumMedium","LargeLargeLargeLargeLargeLargeLarge","Small"}; + + FLMUNICODE puzMedium[] = + {'M','e','d','i','u','m','M','e','d','i','u','m','\0'}; + FLMUNICODE puzLarge[] = + {'L','a','r','g','e','L','a','r','g','e','L','a','r','g','e', 'L','a','r','g','e','\0'}; + FLMUNICODE puzSmall[] = + {'S','m','a','l','l','\0'}; + + FLMUNICODE * ppuzUnicodeValues[3]; + FLMBYTE * ppucBinaryValues[3]; + + FLMBYTE pucMedium[] = {0x01,0x02,0x03}; + FLMBYTE pucLarge[] = {0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C}; + FLMBYTE pucSmall[] = {0x0D,0x0E,0x0F,0x10,0x11}; + FLMUINT uiBinaryLen; + FLMUNICODE * puzResult = NULL; + FLMBYTE pucResult[100]; + char * pszResult = NULL; + FLMBYTE pucUTF8Result[100]; + FLMUINT uiBufSize; + + ppuzUnicodeValues[ 0] = puzMedium; + ppuzUnicodeValues[ 1] = puzLarge; + ppuzUnicodeValues[ 2] = puzSmall; + + ppucBinaryValues[ 0] = pucMedium; + ppucBinaryValues[ 1] = pucLarge; + ppucBinaryValues[ 2] = pucSmall; + + beginTest( + "Encryption test setup", + "Creates all the necessary database objects to run the encryption tests", + "Create the database/create necessary element and attribute definitions/" + "create encryption definitions", + "none"); + + if( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + // Set up element and attribute definitions of all data types + + if( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = setupElemAndAttrNodes())) + { + goto Exit; + } + + if ( RC_BAD( rc = importEncDefs())) + { + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + endTest("PASS"); + + puiEncDefs[0] = m_uiAESDef; + puiEncDefs[1] = m_uiDES3Def; + puiEncDefs[2] = 0; // No encryption; + + ppszEncDefs[0] = XFLM_ENC_AES_OPTION_STR; + ppszEncDefs[1] = XFLM_ENC_DES3_OPTION_STR; + ppszEncDefs[2] = "none"; + + for ( uiLoop = 0; uiLoop < 3; uiLoop++) + { + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueUINT Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setAttributeValueUINT API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUINT( + m_pDb, + m_uiNumberAttr, + UINT_VAL, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUINT( + m_pDb, + m_uiNumberAttr, + &uiResult))) + { + MAKE_ERROR_STRING( "getAttributeValueUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiResult != UINT_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UINT value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueUINT64 Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setAttributeValueUINT64 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUINT64( + m_pDb, + m_uiNumberAttr, + UINT64_VAL, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUINT64( + m_pDb, + m_uiNumberAttr, + &ui64Result))) + { + MAKE_ERROR_STRING( "getAttributeValueUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( ui64Result != UINT64_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UINT64 value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueINT Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setAttributeValueINT API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueINT( + m_pDb, + m_uiNumberAttr, + INT_VAL, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueINT( + m_pDb, + m_uiNumberAttr, + &iResult))) + { + MAKE_ERROR_STRING( "getAttributeValueINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( iResult != INT_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "INT value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueINT64 Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setAttributeValueINT64 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueINT64( + m_pDb, + m_uiNumberAttr, + INT64_VAL, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueINT64( + m_pDb, + m_uiNumberAttr, + &i64Result))) + { + MAKE_ERROR_STRING( "getAttributeValueINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( i64Result != INT64_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "INT64 value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setUINT Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setUINT API using encryption definitions", + "Self-explanatory", + ""); + + // "Direct" setter tests + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pNumberElmNode->setUINT( + m_pDb, + UINT_VAL))) + { + MAKE_ERROR_STRING( "setUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pNumberElmNode->getUINT( + m_pDb, + &uiResult))) + { + MAKE_ERROR_STRING( "getUINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( uiResult != UINT_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UINT value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setUINT64 Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setUINT64 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pNumberElmNode->setUINT64( + m_pDb, + UINT64_VAL))) + { + MAKE_ERROR_STRING( "setUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pNumberElmNode->getUINT64( + m_pDb, + &ui64Result))) + { + MAKE_ERROR_STRING( "getUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( ui64Result != UINT64_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UINT64 value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setINT Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setINT API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pNumberElmNode->setINT( + m_pDb, + INT_VAL))) + { + MAKE_ERROR_STRING( "setINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pNumberElmNode->getINT( + m_pDb, + &iResult))) + { + MAKE_ERROR_STRING( "getINT failed.", m_szDetails, rc); + goto Exit; + } + + if ( iResult != INT_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "INT value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setINT64 Encrypted Value Test (encryption=%s)", + ppszEncDefs[uiLoop]); + + beginTest( + szTemp, + "Tests setINT64 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pNumberElmNode->setINT64( + m_pDb, + INT64_VAL))) + { + MAKE_ERROR_STRING( "setINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pNumberElmNode->getINT64( + m_pDb, + &i64Result))) + { + MAKE_ERROR_STRING( "getINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( i64Result != INT64_VAL) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "INT64 value corruption detected.", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /******************************text value tests*************************/ + + for ( + uiLoop2 = 0; + uiLoop2 < sizeof(ppszNativeValues)/sizeof(ppszNativeValues[0]); + uiLoop2++) + { + uiBinaryLen = (uiLoop2 == 0) + ? sizeof(pucMedium) + : (uiLoop2 == 1) + ? sizeof(pucLarge) + : sizeof(pucSmall); + + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueUnicode Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setAttributeValueUnicode API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUnicode( + m_pDb, + m_uiTextAttr, + ppuzUnicodeValues[uiLoop2], + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueUnicode failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( puzResult) + { + f_free( &puzResult); + } + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUnicode( + m_pDb, + m_uiTextAttr, + &puzResult))) + { + MAKE_ERROR_STRING( "getAttributeValueUnicode failed.", + m_szDetails, rc); + goto Exit; + } + + if ( f_unicmp( puzResult, ppuzUnicodeValues[uiLoop2]) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "unicode value corruption detection.", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueBinary Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setAttributeValueBinary API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueBinary( + m_pDb, + m_uiBinaryAttr, + ppucBinaryValues[uiLoop2], + uiBinaryLen, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueBinary failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueBinary( + m_pDb,m_uiBinaryAttr, pucResult, uiBinaryLen, &uiBufSize))) + { + MAKE_ERROR_STRING( "getAttributeValueBinary failed.", + m_szDetails, rc); + goto Exit; + } + + flmAssert( uiBufSize == uiBinaryLen); + + if ( f_memcmp( pucResult, ppucBinaryValues[uiLoop2], uiBinaryLen) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "Binary value corruption detected.", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setAttributeValueUTF8 Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setAttributeValueUTF8 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pAttributedNode->setAttributeValueUTF8( + m_pDb, + m_uiTextAttr, + (FLMBYTE *)ppszNativeValues[uiLoop2], + strlen(ppszNativeValues[uiLoop2]), + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setAttributeValueUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + uiBufSize = sizeof(pucUTF8Result); + if ( RC_BAD( rc = m_pAttributedNode->getAttributeValueUTF8( + m_pDb, + m_uiTextAttr, + pucUTF8Result, + uiBufSize, + &uiBufSize))) + { + MAKE_ERROR_STRING( "getAttributeValueUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( f_strcmp( (char *)pucUTF8Result, ppszNativeValues[uiLoop2]) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UTF8 value corruption detected", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setUnicode Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setUnicode API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pTextElmNode->setUnicode( + m_pDb, + ppuzUnicodeValues[uiLoop2], + 0, + TRUE, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setUnicode failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + if ( puzResult) + { + f_free( &puzResult); + } + + if ( RC_BAD( rc = m_pTextElmNode->getUnicode( + m_pDb, + &puzResult))) + { + MAKE_ERROR_STRING( "getUnicode failed.", + m_szDetails, rc); + goto Exit; + } + + if ( f_unicmp( puzResult, ppuzUnicodeValues[uiLoop2]) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "Unicode value corruption detected", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setUTF8 Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setUTF8 API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pTextElmNode->setUTF8( + m_pDb, + (FLMBYTE *)ppszNativeValues[uiLoop2], + 0, + TRUE, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + uiBufSize = sizeof(pucUTF8Result); + if ( RC_BAD( rc = m_pTextElmNode->getUTF8( + m_pDb, + pucUTF8Result, + uiBufSize, + 0, + uiBufSize))) + { + MAKE_ERROR_STRING( "getUTF8 failed.", + m_szDetails, rc); + goto Exit; + } + + if ( f_strcmp( (char *)pucUTF8Result, ppszNativeValues[uiLoop2]) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "UTF8 value corruption detected", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + /***********************************************************************/ + + f_sprintf( szTemp, + "setBinary Encrypted Value Test (encryption=%s/value size=%s)", + ppszEncDefs[uiLoop], + ppszTextSizes[uiLoop2]); + + beginTest( + szTemp, + "Tests setBinary API using encryption definitions", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = m_pBinElmNode->setBinary( + m_pDb, + ppucBinaryValues[uiLoop2], + uiBinaryLen, + TRUE, + puiEncDefs[uiLoop]))) + { + MAKE_ERROR_STRING( "setBinary failed.", + m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + uiBufSize = sizeof(pucResult); + if ( RC_BAD( rc = m_pBinElmNode->getBinary( + m_pDb, + pucResult, + 0, + uiBufSize, + &uiBufSize))) + { + if (rc != NE_XFLM_EOF_HIT || + uiBufSize != uiBinaryLen) + { + MAKE_ERROR_STRING( "getBinary failed.", + m_szDetails, rc); + goto Exit; + } + } + + if ( memcmp( pucResult, ppucBinaryValues[uiLoop2], uiBinaryLen) != 0) + { + rc = NE_XFLM_DATA_ERROR; + MAKE_ERROR_STRING( "binary value corruption detected", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + } + } + + // DB Key Rollover tests + // + // Create a new database key + // Close the database + // Verify we can open the database and read encrypted data. + + beginTest( + "Database Key Rollover Test", + "Replace the DB key with a new one", + "Self-explanatory", + ""); + + if (RC_BAD( rc = m_pDb->rollOverDbKey())) + { + MAKE_ERROR_STRING( "rollOverDbKey function failed", m_szDetails, rc); + goto Exit; + } + + m_pDb->Release(); + m_pDb = NULL; + if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) + { + MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); + goto Exit; + } + + if (RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, &m_pDb))) + { + MAKE_ERROR_STRING( "dbOpen failed", m_szDetails, rc); + goto Exit; + } + + // VISIT: Add code to read an encrypted value from the database + + endTest("PASS"); + + // WRAP KEY TESTS + + // Wrap the database key in a password + // Close the database + // Try reopening the database with the incorrect password + // Open the database with the correct password + + beginTest( + "Wrap Key Test", + "Wraps the database key in a password", + "Self-explanatory", + ""); + + if ( RC_BAD( rc = m_pDb->wrapKey( PASSWORD))) + { + MAKE_ERROR_STRING( "binary value corruption detected", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Open Database - Incorrect Password Test", + "Ensure the dbOpen fails if the password is incorrect", + "Close the database/call dbOpen with the incorrect password", + ""); + + if ( m_pDb->Release()) + { + display("Warning, database did not close!"); + } + m_pDb = NULL; + + rc = m_pDbSystem->dbOpen( + DB_NAME_STR, + NULL, + NULL, + &m_pDb, + BAD_PASSWORD); + + if ( rc != NE_XFLM_PBE_DECRYPT_FAILED) + { + MAKE_ERROR_STRING( "Unexpected rc from dbOpen with incorrect password", + m_szDetails, rc); + rc = NE_XFLM_FAILURE; + goto Exit; + } + + endTest("PASS"); + + beginTest( + "Open Database - Correct Password Test", + "Ensure the dbOpen succeeds if the password is correct", + "call dbOpen with the correct password", + ""); + + if (m_pDb) + { + m_pDb->Release(); + m_pDb = NULL; + } + + if ( RC_BAD( rc = m_pDbSystem->dbOpen( + DB_NAME_STR, + NULL, + NULL, + &m_pDb, + PASSWORD))) + { + MAKE_ERROR_STRING( "dbOpen Failed", + m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + + beginTest( + "backup (w/ password)", + "backup the database using a password", + "Self-explanatory", + "No Additional Details."); + + // Backup the database + + if( RC_BAD( rc = m_pDb->backupBegin( XFLM_FULL_BACKUP, + XFLM_READ_TRANS, 0, &m_pBackup))) + { + MAKE_FLM_ERROR_STRING( "backupBegin failed", m_szDetails, rc); + goto Exit; + } + + rc = m_pBackup->backup( BACKUP_NAME_STR, + BACKUP_PASSWORD, NULL, NULL, NULL); + + if( RC_BAD( rc)) + { + MAKE_FLM_ERROR_STRING( "backup failed", m_szDetails, rc); + goto Exit; + } + + m_pBackup->Release(); + m_pBackup = NULL; + + endTest("PASS"); + + beginTest( + "dbRestore (w/ password)", + "restore the database from the backup we just made", + "Self-explanatory", + "No Additional Details."); + + if( RC_BAD( rc = m_pDbSystem->closeUnusedFiles( 0))) + { + MAKE_FLM_ERROR_STRING( "closeUnusedFiles failed", m_szDetails, rc); + goto Exit; + } + + m_pDb->Release(); + m_pDb = NULL; + + // Remove the database + if( RC_BAD( rc = m_pDbSystem->dbRemove( DB_NAME_STR, NULL, NULL, TRUE))) + { + MAKE_FLM_ERROR_STRING( "dbRemove failed", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDbSystem->dbRestore( DB_NAME_STR, + NULL, NULL, BACKUP_NAME_STR, BACKUP_PASSWORD, NULL, NULL))) + { + MAKE_FLM_ERROR_STRING( "dbRestore failed", m_szDetails, rc); + goto Exit; + } + + endTest("PASS"); + +Exit: + + if (puzResult) + { + f_free( &puzResult); + } + + if (pszResult) + { + f_free( &pszResult); + } + + if ( RC_BAD( rc)) + { + endTest("FAIL"); + } + + if ( bTransBegun) + { + if ( RC_OK( rc)) + { + m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + shutdownTestState( DB_NAME_STR, bDibCreated); +#endif + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IEncTestImpl::suite2( void) +{ + RCODE rc = NE_XFLM_OK; +#ifdef FLM_USE_NICI + FLMBOOL bDibCreated = FALSE; + FLMBOOL bTransBegun = FALSE; + FLMUINT * puiEncDefs[] = {&m_uiAESDef,&m_uiDES3Def}; + FLMUINT64 uiLoop; + FLMUINT64 ui64TextNodeId; + FLMUINT64 ui64BinNodeId; + FLMUINT64 ui64NumNodeId; + char * pszRetVal = NULL; + FLMBYTE pucRetVal[100]; + FLMUINT64 ui64RetVal; + FLMUINT uiBinValRetLen; + FLMUINT uiRefCount; + + beginTest( + "Encryption Force-To-Disk Test Setup", + "Creates all the necessary database objects to run the encryption tests", + "Create the database/create necessary element and attribute definitions/" + "create encryption definitions", + "none"); + + if ( RC_BAD( rc = initCleanTestState( DB_NAME_STR))) + { + goto Exit; + } + bDibCreated = TRUE; + + // ENCRYPTED VALUE TESTS + + // Set up element and attribute definitions of all data types + + if ( RC_BAD( rc = m_pDb->transBegin( XFLM_UPDATE_TRANS, XFLM_NO_TIMEOUT))) + { + MAKE_ERROR_STRING( "transBegin failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = TRUE; + + if ( RC_BAD( rc = setupElemAndAttrNodes())) + { + goto Exit; + } + + if ( RC_BAD( rc = importEncDefs())) + { + goto Exit; + } + + if( RC_BAD( rc = m_pNumberElmNode->getNodeId( m_pDb, &ui64NumNodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pBinElmNode->getNodeId( m_pDb, &ui64BinNodeId))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pTextElmNode->getNodeId( m_pDb, &ui64TextNodeId))) + { + goto Exit; + } + + if ( RC_BAD( rc = m_pDb->transCommit( ))) + { + MAKE_ERROR_STRING( "transCommit failed.", m_szDetails, rc); + goto Exit; + } + bTransBegun = FALSE; + + endTest("PASS"); + + if( m_pAttributedNode) + { + m_pAttributedNode->Release(); + m_pAttributedNode = NULL; + } + + if( m_pNumberAttrNode) + { + m_pNumberAttrNode->Release(); + m_pNumberAttrNode = NULL; + } + + if( m_pTextAttrNode) + { + m_pTextAttrNode->Release(); + m_pTextAttrNode = NULL; + } + + if( m_pBinAttrNode) + { + m_pBinAttrNode->Release(); + m_pBinAttrNode = NULL; + } + + beginTest( + "Encryption Force-To-Disk test", + "Force element values to disk to ensure encryption/decryption is working", + "Create the database/set node values/close database/open database/retrieve and " + "verify database values", + "none"); + + + for ( uiLoop = 0; uiLoop < 2; uiLoop++) + { + if ( RC_BAD( rc = m_pNumberElmNode->setUINT64( + m_pDb, UINT64_VAL, *puiEncDefs[uiLoop]))) + { + MAKE_FLM_ERROR_STRING( "setUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pBinElmNode->setBinary( + m_pDb, BIN_VAL, BIN_VAL_LEN, TRUE, *puiEncDefs[uiLoop]))) + { + MAKE_FLM_ERROR_STRING( "setBinary failed.", m_szDetails, rc); + goto Exit; + } + + if ( RC_BAD( rc = m_pTextElmNode->setUTF8( m_pDb, (FLMBYTE *)NATIVE_VAL, + 0, TRUE, *puiEncDefs[uiLoop]))) + { + MAKE_FLM_ERROR_STRING( "setUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if (m_pRootNode) + { + m_pRootNode->Release(); + m_pRootNode = NULL; + } + + if (m_pNumberElmNode) + { + m_pNumberElmNode->Release(); + m_pNumberElmNode = NULL; + } + + if( m_pTextElmNode) + { + m_pTextElmNode->Release(); + m_pTextElmNode = NULL; + } + + if( m_pBinElmNode) + { + m_pBinElmNode->Release(); + m_pBinElmNode = NULL; + } + + // close the database -- force values to disk + uiRefCount = m_pDb->Release(); + flmAssert( !uiRefCount); + + if ( RC_BAD( rc = m_pDbSystem->dbOpen( DB_NAME_STR, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + MAKE_FLM_ERROR_STRING( "dbOpen failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, + ui64NumNodeId, &m_pNumberElmNode))) + { + MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, + ui64BinNodeId, &m_pBinElmNode))) + { + MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->getNode( XFLM_DATA_COLLECTION, + ui64TextNodeId, &m_pTextElmNode))) + { + MAKE_FLM_ERROR_STRING( "getNode failed.", m_szDetails, rc); + goto Exit; + } + + // Retrieve and check values + + if( RC_BAD( rc = m_pNumberElmNode->getUINT64( m_pDb, &ui64RetVal))) + { + MAKE_FLM_ERROR_STRING( "getUINT64 failed.", m_szDetails, rc); + goto Exit; + } + + if( ui64RetVal != UINT64_VAL) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pBinElmNode->getBinary( m_pDb, pucRetVal, 0, + BIN_VAL_LEN, &uiBinValRetLen))) + { + MAKE_FLM_ERROR_STRING( "getBinary failed.", m_szDetails, rc); + goto Exit; + } + + if( uiBinValRetLen != BIN_VAL_LEN || + f_memcmp( pucRetVal, BIN_VAL, BIN_VAL_LEN) != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); + goto Exit; + } + + if( pszRetVal) + { + f_free( &pszRetVal); + } + + if( RC_BAD( rc = m_pTextElmNode->getUTF8( m_pDb, + (FLMBYTE **)&pszRetVal))) + { + MAKE_FLM_ERROR_STRING( "getUTF8 failed.", m_szDetails, rc); + goto Exit; + } + + if( f_strcmp( pszRetVal, NATIVE_VAL) != 0) + { + rc = RC_SET( NE_XFLM_DATA_ERROR); + MAKE_FLM_ERROR_STRING( "Unexpected value.", m_szDetails, rc); + goto Exit; + } + } + + endTest("PASS"); + +Exit: + + if( bTransBegun) + { + if( RC_OK( rc)) + { + m_pDb->transCommit(); + } + else + { + m_pDb->transAbort(); + } + } + + if( pszRetVal) + { + f_free( &pszRetVal); + } + + shutdownTestState( DB_NAME_STR, bDibCreated); + +#endif + + return( rc); +} diff --git a/version5/util/fdomedt.cpp b/version5/util/fdomedt.cpp new file mode 100644 index 0000000..786f781 --- /dev/null +++ b/version5/util/fdomedt.cpp @@ -0,0 +1,13633 @@ +//------------------------------------------------------------------------------ +// Desc: DOM editor class +// +// 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: fdomedt.cpp 3133 2006-01-25 12:00:01 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "ftx.h" +#include "flm_lutl.h" +#include "domedit.h" +#include "fdynbuf.h" +#include "fxpath.h" +#ifdef FLM_UNIX +#include "ctype.h" +#endif + +static char * pszDomeditMonths [12] = +{ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +}; + +static char * pszDomeditFullMonthNames [12] = +{ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; + + +FSTATIC FLMBOOL domeditIsNum( + char * pszToken, + FLMUINT * puiNum); + +FSTATIC char * domeditSkipChars( + char * pszStr, + char * pszCharsToSkip); + +FSTATIC FLMBOOL domeditStrToDate( + char * s, + FLMUINT * puiYear, + FLMUINT * puiMonth, + FLMUINT * puiDay); + +FSTATIC void domGetOutputFileName( + FTX_WINDOW * pWindow, + char * pszOutputFileName, + FLMUINT uiOutputFileNameBufSize); + +FSTATIC RCODE makeNewRow( + DME_ROW_INFO ** ppTmpRow, + FLMUNICODE * puzValue, + FLMUINT64 ui64Id, + FLMBOOL bUseValue = FALSE); + +FSTATIC FLMUINT unicodeStrLen( + FLMUNICODE * puzStr); + +FSTATIC void asciiToUnicode( + char * pszAsciiString, + FLMUNICODE * puzString); + +FSTATIC RCODE unicodeToAscii( + FLMUNICODE * puzString, + char * pszString, + FLMUINT uiLength); + +FSTATIC RCODE formatText( + FLMUNICODE * puzBuf, + FLMBOOL bQuoted, + char * pszPreText, + char * pszPostText, + char ** ppszString); + +FSTATIC RCODE formatDocumentNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags); + +FSTATIC RCODE formatElementNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags); + +FSTATIC RCODE formatAttributeNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags); + +FSTATIC RCODE formatDataNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags, + char * pszPreText, + char * pszPostText); + +FSTATIC RCODE formatProcessingInstruction( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags); + +FSTATIC RCODE formatRow( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags); + +FSTATIC RCODE formatIndexKeyNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals); + +FSTATIC void printNodeType( + DME_ROW_INFO * pRow, + FTX_WINDOW * pStatusWin); + +FSTATIC void releaseRow( + DME_ROW_INFO ** ppRow); + +FSTATIC RCODE getDOMNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow); + +FSTATIC RCODE f_KeyEditorKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +FSTATIC RCODE f_QueryEditorKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +FSTATIC RCODE f_ViewOnlyKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData); + +FSTATIC RCODE f_IndexRangeDispHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals); + +FSTATIC RCODE setupIndexRow( + F_DataVector * pKey, + FLMUINT uiElementNumber, + FLMUINT uiIndex, + FLMUINT uiFlag, + DME_ROW_INFO ** ppTmpRow); + +/* +DOMEdit prototypes +*/ + +RCODE _domEditBackgroundThread( + F_Thread * pThread); + +RCODE domEditVerifyRun( void); + +// Imported global variable(s)... +extern FLMBOOL gv_bShutdown; + +/* +Local prototypes +*/ + +#define FLM_START_STATS +#define FLM_STOP_STATS +#define FLM_RESET_STATS + +/* +Function / Method Implementations +*/ + +/**************************************************************************** +Desc: Default constructor +*****************************************************************************/ +F_DomEditor::F_DomEditor( void) +{ + m_pEditWindow = NULL; + m_pEditStatusWin = NULL; + m_uiEditCanvasRows = 0; + m_bSetupCalled = FALSE; + m_pDisplayHook = (F_DOMEDIT_DISP_HOOK)F_DomEditorDefaultDispHook; + m_pNameTable = NULL; + m_DisplayData = 0; + m_pKeyHook = NULL; + m_KeyData = 0; + m_bMonochrome = FALSE; + m_pDb = NULL; + m_bOpenedDb = FALSE; + m_pRowAnchor = NULL; + m_pDocList = NULL; + m_pCurDoc = NULL; + m_pScrFirstRow = NULL; + m_pScrLastRow = NULL; + m_pCurRow = NULL; + reset(); +} + + +/**************************************************************************** +Desc: Destructor +*****************************************************************************/ +F_DomEditor::~F_DomEditor( void) +{ + DME_ROW_INFO * pTmpRow = m_pScrFirstRow; + DME_ROW_INFO * pDocList = m_pDocList; + + if (m_bOpenedDb && m_pDb != NULL) + { + m_pDb->Release(); + m_pDb = NULL; + } + + if (m_pScrFirstRow) + { + releaseAllRows(); + } + + while (pDocList) + { + pTmpRow = pDocList->pNext; + if (pDocList->puzValue) + { + f_free( &pDocList->puzValue); + } + if (pDocList->pDomNode) + { + if (!(pDocList->uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + pDocList->pDomNode->Release(); + } + else + { + pDocList->pDomNode = NULL; + } + } + f_free( &pDocList); + pDocList = pTmpRow; + } + + reset(); +} + +/**************************************************************************** +Desc: Prepares the editor for use +*****************************************************************************/ +RCODE F_DomEditor::Setup( + FTX_SCREEN * pScreen) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( pScreen != NULL); + + m_pScreen = pScreen; + m_bSetupCalled = TRUE; + + return( rc); +} + + +/**************************************************************************** +Desc: Reset the DOMEditor object +*****************************************************************************/ +void F_DomEditor::reset( void) +{ + m_pScrFirstRow = NULL; + m_pScrLastRow = NULL; + m_pCurRow = NULL; + if (m_bOpenedDb && m_pDb != NULL) + { + m_pDb->Release(); + } + m_bOpenedDb = FALSE; + m_pDb = NULL; + m_uiCollection = XFLM_DATA_COLLECTION; + m_szTitle[ 0] = '\0'; + m_bReadOnly = FALSE; + m_pbShutdown = NULL; + m_pParent = NULL; + m_uiCurRow = 0; + m_uiEditCanvasRows = 0; + m_pHelpHook = NULL; + m_pEventHook = NULL; + m_bDocList = FALSE; + m_uiLastKey = 0; + m_uiULX = 0; + m_uiULY = 0; + m_uiLRX = 0; + m_uiLRY = 0; + m_uiNumRows = 0; + + if( m_pRowAnchor) + { + f_free( &m_pRowAnchor); + } + + m_pDocList = NULL; + m_pCurDoc = NULL; + + if (m_pNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } +} + +/**************************************************************************** +Name: insertRecord +Desc: +*****************************************************************************/ +RCODE F_DomEditor::insertRow( + DME_ROW_INFO * pRow, + DME_ROW_INFO * pStartRow) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = _insertRow( pRow, pStartRow))) + { + goto Exit; + } + +Exit: + + return( rc); + +} + + +/**************************************************************************** +Desc: _insertRow +*****************************************************************************/ +RCODE F_DomEditor::_insertRow( + DME_ROW_INFO * pRow, + DME_ROW_INFO * pStartRow) +{ + DME_ROW_INFO * pTmpRow = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( m_pEventHook) + { + + if( RC_BAD( rc = m_pEventHook( this, F_DOMEDIT_EVENT_RECINSERT, + (void *)(pRow), m_EventData))) + { + goto Exit; + } + } + + if( !m_pScrFirstRow) + { + m_pScrFirstRow = pRow; + m_pCurRow = m_pScrFirstRow; + m_uiNumRows = 1; + } + else + { + if (pStartRow) + { + pTmpRow = pStartRow->pNext; + pStartRow->pNext = pRow; + } + else + { + pTmpRow = m_pScrFirstRow; + } + pRow->pNext = pTmpRow; + pRow->pPrev = pStartRow; + if (pTmpRow) + { + pTmpRow->pPrev = pRow; + } + if (!pStartRow) + { + m_pScrFirstRow = pRow; + } + + m_uiNumRows++; + } + + // Adjust the last display row if needed. + if ( m_uiNumRows == 1) + { + m_pScrLastRow = m_pScrFirstRow; + } + else + { + while (m_pScrLastRow->pNext) + { + m_pScrLastRow = m_pScrLastRow->pNext; + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: setTitle +Desc: +*****************************************************************************/ +RCODE F_DomEditor::setTitle( + char * pszTitle) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiBack; + FLMUINT uiFore; + + flmAssert( m_bSetupCalled == TRUE); + + m_szTitle[ 0] = ' '; + f_strncpy( &m_szTitle[ 1], pszTitle, F_DOMEDIT_MAX_TITLE_SIZE - 2); + f_strcat( m_szTitle, " "); + m_szTitle[ F_DOMEDIT_MAX_TITLE_SIZE] = '\0'; + + if (m_pEditWindow) + { + uiBack = (FLMUINT)((m_bMonochrome) + ? (FLMUINT)WPS_BLACK + : (FLMUINT)WPS_BLUE); + uiFore = (FLMUINT)WPS_WHITE; + if( FTXWinSetTitle( m_pEditWindow, m_szTitle, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + +Exit: + return( rc); +} + + +/**************************************************************************** +Desc: Set the default source Collection... +*****************************************************************************/ +RCODE F_DomEditor::setSource( + F_Db * pDb, + FLMUINT uiCollection) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if (m_bOpenedDb && m_pDb != NULL && pDb != m_pDb) + { + m_pDb->Release(); + m_bOpenedDb = FALSE; + } + + if (m_pDb != pDb) + { + if (m_pDb) + { + m_pDb->Release(); + } + m_pDb = pDb; + } + m_pDb->AddRef(); + + if (pDb) + { + m_bOpenedDb = TRUE; + } + + m_uiCollection = uiCollection; + + return( rc); +} + +/**************************************************************************** +Desc: QueryStatus class. +*****************************************************************************/ +class EditQueryStatus : public IF_QueryStatus +{ +public: + +#define LABEL_WIDTH 30 +#define LABEL_COLUMN 2 +#define DATA_COLUMN (LABEL_COLUMN + LABEL_WIDTH + 1) +#define QUERY_LINE 2 +#define SOURCE_LINE (QUERY_LINE + 1) +#define USING_LINE (SOURCE_LINE + 1) +#define COST_LINE (USING_LINE + 1) +#define VERIFY_LINE (COST_LINE + 1) +#define NODE_MATCH_LINE (VERIFY_LINE + 1) +#define CAN_COMPARE_KEY_LINE (NODE_MATCH_LINE + 1) +#define KEYS_READ_LINE (CAN_COMPARE_KEY_LINE + 1) +#define DUP_KEYS_LINE (KEYS_READ_LINE + 1) +#define KEYS_PASSED_LINE (DUP_KEYS_LINE + 1) +#define NODES_READ_LINE (KEYS_PASSED_LINE + 1) +#define NODES_TESTED_LINE (NODES_READ_LINE + 1) +#define NODES_PASSED_LINE (NODES_TESTED_LINE + 1) +#define DOCUMENTS_READ_LINE (NODES_PASSED_LINE + 1) +#define DOCUMENTS_DUP_LINE (DOCUMENTS_READ_LINE + 1) +#define NODES_FAILED_VAL_LINE (DOCUMENTS_DUP_LINE + 1) +#define DOCS_FAILED_VAL_LINE (NODES_FAILED_VAL_LINE + 1) +#define DOCUMENTS_PASSED_LINE (DOCS_FAILED_VAL_LINE + 1) +#define TOT_DOCS_READ_LINE (DOCUMENTS_PASSED_LINE + 2) +#define TOT_DOCS_PASSED_LINE (TOT_DOCS_READ_LINE + 1) +#define CAN_RETRIEVE_DOCS_LINE (TOT_DOCS_PASSED_LINE + 1) +#define MESSAGE_LINE (CAN_RETRIEVE_DOCS_LINE + 2) + + EditQueryStatus() + { + m_pWindow = NULL; + m_uiSourceCnt = 0; + m_pszQuery = NULL; + f_memset( &m_optInfo, 0, sizeof( XFLM_OPT_INFO)); + m_ui64TotalDocsRead = 0; + m_ui64TotalDocsPassed = 0; + m_bCanRetrieveDocs = FALSE; + m_bKeepResults = TRUE; + } + + virtual ~EditQueryStatus() + { + if (m_pWindow) + { + FTXWinFree( &m_pWindow); + } + if (m_pszQuery) + { + f_free( &m_pszQuery); + } + } + + RCODE XFLMAPI queryStatus( + XFLM_OPT_INFO * pOptInfo); + + RCODE XFLMAPI newSource( + XFLM_OPT_INFO * pOptInfo); + + RCODE XFLMAPI resultSetStatus( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed, + FLMBOOL bCanRetrieveDocs); + + RCODE XFLMAPI resultSetComplete( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed); + + void createQueryStatusWindow( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + char * pszQuery); + + RCODE testEscape( + FLMUINT uiSourceCnt, + FLMUINT * puiChar); + + FINLINE FLMBOOL keepResults( void) + { + return( m_bKeepResults); + } + + void refreshStatus( + FLMBOOL bRefreshAll, + FLMUINT uiSource, + FLMUINT uiSourceCnt, + XFLM_OPT_INFO * pOptInfo); + + void refreshResultSetStatus( + FLMBOOL bRefreshAll, + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed, + FLMBOOL bCanRetrieveDocs); + + FINLINE FLMUINT getRefCount( void) + { + return( IF_QueryStatus::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_QueryStatus::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_QueryStatus::Release()); + } + +private: + + void outputLabel( + FLMUINT uiRow, + char * pszLabel); + + void outputStr( + FLMUINT uiRow, + char * pszValue); + + FINLINE void outputBool( + FLMUINT uiRow, + FLMBOOL bBool + ) + { + outputStr( uiRow, (char *)(bBool ? (char *)"YES" : (char *)"NO")); + } + + void outputUINT( + FLMUINT uiRow, + FLMUINT uiValue); + + void outputUINT64( + FLMUINT uiRow, + FLMUINT64 ui64Value); + + FTX_WINDOW * m_pWindow; + FLMUINT m_uiNumCols; + FLMUINT m_uiNumRows; + FLMUINT m_uiSourceCnt; + XFLM_OPT_INFO m_optInfo; + FLMUINT64 m_ui64TotalDocsRead; + FLMUINT64 m_ui64TotalDocsPassed; + FLMBOOL m_bCanRetrieveDocs; + char * m_pszQuery; + FLMBOOL m_bKeepResults; +}; + +/**************************************************************************** +Desc: Displays a message window +*****************************************************************************/ +void EditQueryStatus::createQueryStatusWindow( + FTX_SCREEN * pScreen, + FLMUINT uiBack, + FLMUINT uiFore, + char * pszQuery) +{ + FLMBOOL bOk = FALSE; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiQueryStrLen = f_strlen( pszQuery); + + if (FTXScreenGetSize( pScreen, &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + goto Exit; + } + + m_uiNumCols = uiNumCols - 8; + m_uiNumRows = uiNumRows - 4; + + if (FTXWinInit( pScreen, m_uiNumCols, m_uiNumRows, + &m_pWindow) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (FTXWinSetScroll( m_pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetCursorType( m_pWindow, WPS_CURSOR_INVISIBLE); + + if (FTXWinSetBackFore( m_pWindow, uiBack, uiFore) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (FTXWinClear( m_pWindow) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (FTXWinDrawBorder( m_pWindow) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (FTXWinMove( m_pWindow, (FLMUINT)((uiNumCols - m_uiNumCols) / 2), + (FLMUINT)((uiNumRows - m_uiNumRows) / 2)) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (FTXWinOpen( m_pWindow) != FTXRC_SUCCESS) + { + goto Exit; + } + + if (RC_BAD( f_alloc( uiQueryStrLen + 1, &m_pszQuery))) + { + goto Exit; + } + f_memcpy( m_pszQuery, pszQuery, uiQueryStrLen + 1); + if (uiQueryStrLen > m_uiNumCols - 2 - DATA_COLUMN) + { + m_pszQuery [m_uiNumCols - 2 - DATA_COLUMN] = 0; + } + + FTXRefresh( pScreen->pFtxInfo); + + bOk = TRUE; + +Exit: + + if (!bOk && m_pWindow) + { + FTXWinFree( &m_pWindow); + } +} + +/**************************************************************************** +Desc: Output a label on the query status screen. +*****************************************************************************/ +void EditQueryStatus::outputLabel( + FLMUINT uiRow, + char * pszLabel + ) +{ + char szLabel [50]; + FLMUINT uiStrLen = f_strlen( pszLabel); + + f_memset( szLabel, '.', LABEL_WIDTH); + szLabel [LABEL_WIDTH] = ' '; + szLabel [LABEL_WIDTH + 1] = 0; + if (uiStrLen > LABEL_WIDTH) + { + uiStrLen = LABEL_WIDTH; + } + f_memcpy( szLabel, pszLabel, uiStrLen); + FTXWinSetCursorPos( m_pWindow, LABEL_COLUMN, uiRow); + FTXWinPrintf( m_pWindow, "%s", szLabel); +} + +/**************************************************************************** +Desc: Output a string value on the query status screen. +*****************************************************************************/ +void EditQueryStatus::outputStr( + FLMUINT uiRow, + char * pszValue + ) +{ + FTXWinSetCursorPos( m_pWindow, DATA_COLUMN, uiRow); + FTXWinPrintf( m_pWindow, "%s", pszValue); + FTXWinClearToEOL( m_pWindow); +} + +/**************************************************************************** +Desc: Output a FLMUINT value on the query status screen. +*****************************************************************************/ +void EditQueryStatus::outputUINT( + FLMUINT uiRow, + FLMUINT uiValue + ) +{ + char szValue [20]; + + f_sprintf( szValue, "%,u", (unsigned)uiValue); + outputStr( uiRow, szValue); +} + +/**************************************************************************** +Desc: Output a FLMUINT64 value on the query status screen. +*****************************************************************************/ +void EditQueryStatus::outputUINT64( + FLMUINT uiRow, + FLMUINT64 ui64Value + ) +{ + char szValue [60]; + + f_sprintf( szValue, "%,I64u", ui64Value); + outputStr( uiRow, szValue); +} + +/**************************************************************************** +Desc: Refresh query status screen +*****************************************************************************/ +void EditQueryStatus::refreshStatus( + FLMBOOL bRefreshAll, + FLMUINT uiSource, + FLMUINT uiSourceCnt, + XFLM_OPT_INFO * pOptInfo) +{ + char szTmp [60]; + + if (bRefreshAll) + { + f_memcpy( &m_optInfo, pOptInfo, sizeof( XFLM_OPT_INFO)); + outputLabel( QUERY_LINE, "Query"); + outputStr( QUERY_LINE, m_pszQuery); + outputLabel( SOURCE_LINE, "Source"); + if (!uiSourceCnt) + { + outputUINT( SOURCE_LINE, uiSource); + } + else + { + f_sprintf( szTmp, "%u of %u", (unsigned)uiSource, + (unsigned)uiSourceCnt); + outputStr( SOURCE_LINE, szTmp); + } + outputLabel( COST_LINE, " Cost"); + outputLabel( VERIFY_LINE, " Must Verify Path"); + outputLabel( NODE_MATCH_LINE, " Do Node Match"); + outputLabel( CAN_COMPARE_KEY_LINE, " Can Compare Key"); + switch (m_optInfo.eOptType) + { + case XFLM_QOPT_USING_INDEX: + outputLabel( USING_LINE, "Using Index"); + if (m_optInfo.szIxName [0]) + { + FLMUINT uiStrLen = f_strlen( m_optInfo.szIxName); + if (uiStrLen > m_uiNumCols - 2 - DATA_COLUMN) + { + m_optInfo.szIxName [m_uiNumCols - 2 - DATA_COLUMN] = 0; + } + outputStr( USING_LINE, (char *)m_optInfo.szIxName); + } + else + { + f_sprintf( szTmp, "#%u", (unsigned)m_optInfo.uiIxNum); + outputStr( USING_LINE, szTmp); + } + outputUINT( COST_LINE, m_optInfo.uiCost); + outputBool( VERIFY_LINE, m_optInfo.bMustVerifyPath); + outputBool( NODE_MATCH_LINE, m_optInfo.bDoNodeMatch); + outputBool( CAN_COMPARE_KEY_LINE, m_optInfo.bCanCompareOnKey); + break; + case XFLM_QOPT_FULL_COLLECTION_SCAN: + outputLabel( USING_LINE, "Using"); + outputStr( USING_LINE, "Collection Scan"); + outputStr( COST_LINE, "N/A"); + outputStr( VERIFY_LINE, "N/A"); + outputStr( NODE_MATCH_LINE, "N/A"); + outputStr( CAN_COMPARE_KEY_LINE, "N/A"); + break; + case XFLM_QOPT_SINGLE_NODE_ID: + outputLabel( USING_LINE, "Using Node ID"); + f_sprintf( szTmp, "#%I64u", m_optInfo.ui64NodeId); + outputStr( USING_LINE, szTmp); + outputUINT( COST_LINE, m_optInfo.uiCost); + outputBool( VERIFY_LINE, m_optInfo.bMustVerifyPath); + outputStr( NODE_MATCH_LINE, "N/A"); + outputStr( CAN_COMPARE_KEY_LINE, "N/A"); + break; + case XFLM_QOPT_NODE_ID_RANGE: + outputLabel( USING_LINE, "Using Node ID Range"); + f_sprintf( szTmp, "#%I64u to %I64u", m_optInfo.ui64NodeId, + m_optInfo.ui64EndNodeId); + outputStr( USING_LINE, szTmp); + outputUINT( COST_LINE, m_optInfo.uiCost); + outputBool( VERIFY_LINE, m_optInfo.bMustVerifyPath); + outputStr( NODE_MATCH_LINE, "N/A"); + outputStr( CAN_COMPARE_KEY_LINE, "N/A"); + break; + default: + break; + } + outputLabel( KEYS_READ_LINE, "Keys Read"); + outputLabel( DUP_KEYS_LINE, "Keys Elim By Dup Docs"); + outputLabel( KEYS_PASSED_LINE, "Keys Passed"); + outputLabel( NODES_READ_LINE, "Nodes Read"); + outputLabel( NODES_TESTED_LINE, "Nodes Tested"); + outputLabel( NODES_PASSED_LINE, "Nodes Passed"); + outputLabel( DOCUMENTS_READ_LINE, "Documents Read"); + outputLabel( DOCUMENTS_DUP_LINE, "Dup Docs Eliminated"); + outputLabel( NODES_FAILED_VAL_LINE, "Nodes Failed Validation"); + outputLabel( DOCS_FAILED_VAL_LINE, "Docs Failed Validation"); + outputLabel( DOCUMENTS_PASSED_LINE, "Documents Passed"); + refreshResultSetStatus( TRUE, m_ui64TotalDocsRead, m_ui64TotalDocsPassed, + m_bCanRetrieveDocs); + } + if (bRefreshAll || pOptInfo->ui64KeysRead != m_optInfo.ui64KeysRead) + { + m_optInfo.ui64KeysRead = pOptInfo->ui64KeysRead; + outputUINT64( KEYS_READ_LINE, m_optInfo.ui64KeysRead); + } + if (bRefreshAll || pOptInfo->ui64KeyHadDupDoc != m_optInfo.ui64KeyHadDupDoc) + { + m_optInfo.ui64KeyHadDupDoc = pOptInfo->ui64KeyHadDupDoc; + outputUINT64( DUP_KEYS_LINE, m_optInfo.ui64KeyHadDupDoc); + } + if (bRefreshAll || pOptInfo->ui64KeysPassed != m_optInfo.ui64KeysPassed) + { + m_optInfo.ui64KeysPassed = pOptInfo->ui64KeysPassed; + outputUINT64( KEYS_PASSED_LINE, m_optInfo.ui64KeysPassed); + } + if (bRefreshAll || pOptInfo->ui64NodesRead != m_optInfo.ui64NodesRead) + { + m_optInfo.ui64NodesRead = pOptInfo->ui64NodesRead; + outputUINT64( NODES_READ_LINE, m_optInfo.ui64NodesRead); + } + if (bRefreshAll || pOptInfo->ui64NodesTested != m_optInfo.ui64NodesTested) + { + m_optInfo.ui64NodesTested = pOptInfo->ui64NodesTested; + outputUINT64( NODES_TESTED_LINE, m_optInfo.ui64NodesTested); + } + if (bRefreshAll || pOptInfo->ui64NodesPassed != m_optInfo.ui64NodesPassed) + { + m_optInfo.ui64NodesPassed = pOptInfo->ui64NodesPassed; + outputUINT64( NODES_PASSED_LINE, m_optInfo.ui64NodesPassed); + } + if (bRefreshAll || pOptInfo->ui64DocsRead != m_optInfo.ui64DocsRead) + { + m_optInfo.ui64DocsRead = pOptInfo->ui64DocsRead; + outputUINT64( DOCUMENTS_READ_LINE, m_optInfo.ui64DocsRead); + } + if (bRefreshAll || pOptInfo->ui64DupDocsEliminated != m_optInfo.ui64DupDocsEliminated) + { + m_optInfo.ui64DupDocsEliminated = pOptInfo->ui64DupDocsEliminated; + outputUINT64( DOCUMENTS_DUP_LINE, m_optInfo.ui64DupDocsEliminated); + } + if (bRefreshAll || pOptInfo->ui64NodesFailedValidation != m_optInfo.ui64NodesFailedValidation) + { + m_optInfo.ui64NodesFailedValidation = pOptInfo->ui64NodesFailedValidation; + outputUINT64( NODES_FAILED_VAL_LINE, m_optInfo.ui64NodesFailedValidation); + } + if (bRefreshAll || pOptInfo->ui64DocsFailedValidation != m_optInfo.ui64DocsFailedValidation) + { + m_optInfo.ui64DocsFailedValidation = pOptInfo->ui64DocsFailedValidation; + outputUINT64( DOCS_FAILED_VAL_LINE, m_optInfo.ui64DocsFailedValidation); + } + if (bRefreshAll || pOptInfo->ui64DocsPassed != m_optInfo.ui64DocsPassed) + { + m_optInfo.ui64DocsPassed = pOptInfo->ui64DocsPassed; + outputUINT64( DOCUMENTS_PASSED_LINE, m_optInfo.ui64DocsPassed); + } +} + +/**************************************************************************** +Desc: Refresh query status screen +*****************************************************************************/ +void EditQueryStatus::refreshResultSetStatus( + FLMBOOL bRefreshAll, + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed, + FLMBOOL bCanRetrieveDocs) +{ + if (bRefreshAll) + { + m_ui64TotalDocsRead = ui64TotalDocsRead; + m_ui64TotalDocsPassed = ui64TotalDocsPassed; + m_bCanRetrieveDocs = bCanRetrieveDocs; + outputLabel( TOT_DOCS_READ_LINE, "Total Docs Read"); + outputLabel( TOT_DOCS_PASSED_LINE, "Total Docs Passed"); + outputLabel( CAN_RETRIEVE_DOCS_LINE, "Can Retrieve Docs"); + } + if (bRefreshAll || ui64TotalDocsRead != m_ui64TotalDocsRead) + { + m_ui64TotalDocsRead = ui64TotalDocsRead; + outputUINT64( TOT_DOCS_READ_LINE, m_ui64TotalDocsRead); + } + if (bRefreshAll || ui64TotalDocsPassed != m_ui64TotalDocsPassed) + { + m_ui64TotalDocsPassed = ui64TotalDocsPassed; + outputUINT64( TOT_DOCS_PASSED_LINE, m_ui64TotalDocsPassed); + } + if (bRefreshAll || bCanRetrieveDocs != m_bCanRetrieveDocs) + { + m_bCanRetrieveDocs = bCanRetrieveDocs; + outputUINT64( CAN_RETRIEVE_DOCS_LINE, m_bCanRetrieveDocs); + } +} + +/**************************************************************************** +Desc: See if user pressed escape +*****************************************************************************/ +RCODE EditQueryStatus::testEscape( + FLMUINT uiSourceCnt, + FLMUINT * puiChar) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pWindow) + { + if (uiSourceCnt) + { + FTXWinSetCursorPos( m_pWindow, LABEL_COLUMN, MESSAGE_LINE); + if (uiSourceCnt == 1) + { + FTXWinPrintf( m_pWindow, "Press any character to show query results: "); + } + else + { + FTXWinPrintf( m_pWindow, "N,P=Show Next/Prev Source, other=Show query results: "); + } + FTXWinInputChar( m_pWindow, puiChar); + } + else if (FTXWinTestKB( m_pWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( m_pWindow, &uiChar); + + if (uiChar == WPK_ESCAPE) + { + FTXWinSetCursorPos( m_pWindow, LABEL_COLUMN, MESSAGE_LINE); + FTXWinPrintf( m_pWindow, + "Escape pressed, exit? (Y=Show results, ESC=quit): "); + if (FTXWinInputChar( m_pWindow, &uiChar) == FTXRC_SHUTDOWN || + uiChar == WPK_ESCAPE) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + m_bKeepResults = FALSE; + goto Exit; + } + else if (uiChar == 'Y' || uiChar == 'y') + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + else + { + FTXWinSetCursorPos( m_pWindow, LABEL_COLUMN, MESSAGE_LINE); + FTXWinPrintf( m_pWindow, + " "); + } + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Query status callback +*****************************************************************************/ +RCODE EditQueryStatus::queryStatus( + XFLM_OPT_INFO * pOptInfo) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pWindow) + { + + // See if the user pressed escape. + + if (RC_BAD( rc = testEscape( 0, NULL))) + { + goto Exit; + } + + // Update our statistics display + + refreshStatus( FALSE, m_uiSourceCnt, 0, pOptInfo); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Query status callback +*****************************************************************************/ +RCODE EditQueryStatus::newSource( + XFLM_OPT_INFO * pOptInfo) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pWindow) + { + + // See if the user pressed escape. + + if (RC_BAD( rc = testEscape( 0, NULL))) + { + goto Exit; + } + + // Update our statistics display + + m_uiSourceCnt++; + refreshStatus( TRUE, m_uiSourceCnt, 0, pOptInfo); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Query status callback +*****************************************************************************/ +RCODE XFLMAPI EditQueryStatus::resultSetStatus( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed, + FLMBOOL bCanRetrieveDocs) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pWindow) + { + + // See if the user pressed escape. + + if (RC_BAD( rc = testEscape( 0, NULL))) + { + goto Exit; + } + refreshResultSetStatus( FALSE, ui64TotalDocsRead, ui64TotalDocsPassed, + bCanRetrieveDocs); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Query status callback +*****************************************************************************/ +RCODE XFLMAPI EditQueryStatus::resultSetComplete( + FLMUINT64 ui64TotalDocsRead, + FLMUINT64 ui64TotalDocsPassed) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pWindow) + { + + // See if the user pressed escape. + + if (RC_BAD( rc = testEscape( 0, NULL))) + { + goto Exit; + } + refreshResultSetStatus( FALSE, ui64TotalDocsRead, ui64TotalDocsPassed, + TRUE); + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Interact with the user... +*****************************************************************************/ +RCODE F_DomEditor::interactiveEdit( + FLMUINT uiULX, + FLMUINT uiULY, + FLMUINT uiLRX, + FLMUINT uiLRY, + FLMBOOL bBorder, + FLMUINT uiStatusLines, + FLMUINT uiStartChar) +{ + DME_ROW_INFO * pTmpRow; + FLMBOOL bRefreshEditWindow = FALSE; + FLMBOOL bRefreshStatusWindow = FALSE; + FLMUINT uiNumRows = 0; + FLMUINT uiNumCols = 0; + FLMUINT uiMaxRow = 0; + FLMUINT uiStartCol = 0; + eDbTransType eTransType = XFLM_NO_TRANS; + FLMUINT uiLoop; + FLMUINT uiCurFlags; + char szAction[ 2]; + FLMUINT uiTermChar; + FLMUINT uiHelpKey = uiStartChar; + FLMBOOL bDoneEditing = FALSE; + RCODE rc = NE_XFLM_OK; + RCODE tmpRc = NE_XFLM_OK; + FLMUINT uiFore; + FLMUINT uiBack; + F_Thread * pIxManagerThrd = NULL; + F_Thread * pMemManagerThrd = NULL; + char * pszQuery = NULL; + FLMUINT uiSzQueryBufSize; + F_DbSystem dbSystem; + + flmAssert( m_bSetupCalled == TRUE); + flmAssert( m_pScreen != NULL); + + m_uiCurRow = 0; + m_pScrFirstRow = NULL; + m_uiLastKey = 0; + uiSzQueryBufSize = 1024; + if (RC_BAD( rc = f_alloc( uiSzQueryBufSize, &pszQuery))) + { + goto Exit; + } + *pszQuery = 0; + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( !uiLRX && !uiLRY) + { + if( FTXScreenGetSize( m_pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + uiNumRows -= uiULY; + uiNumCols -= uiULX; + + } + else + { + uiNumRows = (uiLRY - uiULY) + 1; + uiNumCols = (uiLRX - uiULX) + 1; + } + + uiStartCol = uiULX; + + uiNumRows -= uiStatusLines; // Subtract however many lines are for the status area + + m_uiULX = uiULX; + m_uiULY = uiULY; + m_uiLRX = uiLRX; + m_uiLRY = uiLRY; + + if( FTXWinInit( m_pScreen, uiNumCols, + uiNumRows, &m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinMove( m_pEditWindow, uiStartCol, uiULY) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetScroll( m_pEditWindow, FALSE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetLineWrap( m_pEditWindow, FALSE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + FTXWinSetCursorType( m_pEditWindow, WPS_CURSOR_INVISIBLE); + + uiBack = (FLMUINT)((m_bMonochrome) + ? (FLMUINT)WPS_BLACK + : (FLMUINT)WPS_BLUE); + uiFore = (FLMUINT)WPS_WHITE; + if( FTXWinSetBackFore( m_pEditWindow, uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinClear( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( bBorder) + { + if( FTXWinDrawBorder( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if( FTXWinSetTitle( m_pEditWindow, m_szTitle, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( uiStatusLines) + { + if( FTXWinInit( m_pScreen, uiNumCols, uiStatusLines, + &m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinMove( m_pEditStatusWin, uiULX, + uiULY + uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetScroll( m_pEditStatusWin, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetCursorType( m_pEditStatusWin, WPS_CURSOR_INVISIBLE); + + if( FTXWinSetBackFore( m_pEditStatusWin, + m_bMonochrome ? WPS_BLACK : WPS_GREEN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinClear( m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinOpen( m_pEditStatusWin) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if( FTXWinOpen( m_pEditWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, &uiNumRows); + m_uiEditCanvasRows = uiNumRows; + m_uiEditCanvasCols = uiNumCols; + uiMaxRow = uiNumRows - 1; + + bRefreshEditWindow = TRUE; + bRefreshStatusWindow = TRUE; + + if( m_pDb != NULL) + { + eTransType = m_pDb->getTransType(); + } + + /* + Call the callback to indicate that the interactive + editor has been invoked + */ + + if( m_pEventHook) + { + m_pEventHook( this, F_DOMEDIT_EVENT_IEDIT, + 0, m_EventData); + } + + FTXRefresh( m_pScreen->pFtxInfo); + while( !bDoneEditing && !isExiting()) + { + if( bRefreshEditWindow) + { + /* + Refresh the edit window + */ + + if( m_pParent) + { + m_bMonochrome = m_pParent->isMonochrome(); + } + + if( FTXWinPaintBackground( m_pEditWindow, + m_bMonochrome ? WPS_BLACK : WPS_BLUE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( m_pEventHook) + { + if( RC_BAD( rc = m_pEventHook( this, F_DOMEDIT_EVENT_REFRESH, + 0, m_EventData))) + { + goto Exit; + } + } + + refreshEditWindow( &m_pScrFirstRow, m_pCurRow, &m_uiCurRow); + FTXWinSetCursorPos( m_pEditWindow, 0, m_uiCurRow); + bRefreshEditWindow = FALSE; + bRefreshStatusWindow = TRUE; + } + + if( m_pEditStatusWin && bRefreshStatusWindow) + { + /* + Update the status window + */ + + if( FTXWinSetBackFore( m_pEditStatusWin, + m_bMonochrome ? WPS_LIGHTGRAY : WPS_GREEN, + m_bMonochrome ? WPS_BLACK : WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + + getControlFlags( m_pCurRow, &uiCurFlags); + if( !(uiCurFlags & F_DOMEDIT_FLAG_LIST_ITEM)) + { + switch( eTransType) + { + case XFLM_UPDATE_TRANS: + { + FTXWinPrintf( m_pEditStatusWin, " | UTRANS"); + break; + } + case XFLM_READ_TRANS: + { + FTXWinPrintf( m_pEditStatusWin, " | RTRANS"); + break; + } + default: + { + break; + } + } + } + + if( m_pCurRow) + { + FLMUINT64 ui64DocId = m_pCurRow->ui64DocId; + FLMUINT64 ui64NodeId = m_pCurRow->ui64NodeId; + FLMUINT uiNameId = m_pCurRow->uiNameId; + + if ( ui64NodeId || ui64DocId || uiNameId) + { + FTXWinPrintf( m_pEditStatusWin, "["); + + if ( ui64DocId) + { + FTXWinPrintf( m_pEditStatusWin, "Doc:%,10I64u", ui64DocId); + + if ( ui64NodeId || uiNameId) + { + FTXWinPrintf( m_pEditStatusWin, " / "); + } + } + + if ( ui64NodeId) + { + FTXWinPrintf( m_pEditStatusWin, "Node:%,10I64u", ui64NodeId); + + if ( uiNameId) + { + FTXWinPrintf( m_pEditStatusWin, " / "); + } + } + + if ( uiNameId) + { + FTXWinPrintf( m_pEditStatusWin, "Name:%,13u", uiNameId); + } + + FTXWinPrintf( m_pEditStatusWin, "]"); + + if ( ui64NodeId) + { + printNodeType( m_pCurRow, m_pEditStatusWin); + } + } + } + + FTXWinClearToEOL( m_pEditStatusWin); + FTXRefresh( m_pScreen->pFtxInfo); + bRefreshStatusWindow = FALSE; + } + + if( uiHelpKey || FTXWinTestKB( m_pEditWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + bRefreshEditWindow = TRUE; + + if( uiHelpKey) + { + uiChar = uiHelpKey; + uiHelpKey = 0; + } + else + { + FTXWinInputChar( m_pEditWindow, &uiChar); + } + + if( uiChar) + { + if( m_pKeyHook) + { + m_pKeyHook( this, m_pCurRow, uiChar, &uiChar, m_KeyData); + } + if (uiChar != WPK_TAB) + { + m_uiLastKey = uiChar; + } + } + + if( uiChar == WPK_TAB) + { + // Grab the last keystroke that was passed to the editor. + // This is needed in environments where the ALT and + // control keys are not available and must be selected from + // the command list. Rather than requiring the user to + // re-select the command from the list each time, he/she can + // simply press the tab key to repeat the last keystroke. + + uiChar = m_uiLastKey; + } + + getControlFlags( m_pCurRow, &uiCurFlags); + switch( uiChar) + { + case 0: + { + /* + Key handled by the keyboard callback. Refresh the + edit window so that any changes made to the buffer by + the callback will be displayed. + */ + + bRefreshEditWindow = TRUE; + break; + } + + case WPK_ENTER: + { + if( !m_pCurRow) + { + break; + } + + if( uiCurFlags & F_DOMEDIT_FLAG_LIST_ITEM) + { + setControlFlags( m_pCurRow, + uiCurFlags | F_DOMEDIT_FLAG_SELECTED); + bDoneEditing = TRUE; + } + else if( !canEditRow( m_pCurRow)) + { + displayMessage( "The row cannot be edited", + RC_SET( NE_XFLM_ILLEGAL_OP), NULL, WPS_RED, WPS_WHITE); + } + else if( RC_BAD( tmpRc = editRow( m_uiCurRow, m_pCurRow))) + { + displayMessage( "The field could not be edited", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + bRefreshEditWindow = TRUE; + break; + } + + case 'S': + case 's': + { + bRefreshEditWindow = TRUE; + break; + } + + // Just view (edit without saving changes) + case 'V': + case 'v': + { + if( !m_pCurRow) + { + break; + } + + if( RC_BAD( tmpRc = editRow( m_uiCurRow, m_pCurRow, TRUE))) + { + displayMessage( "The field could not be edited", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + bRefreshEditWindow = TRUE; + break; + } + + // Expand the current row. + case WPK_RIGHT: + { + DME_ROW_INFO * pLastRow = NULL; + if (!m_pCurRow->bExpanded) + { + if (RC_BAD( tmpRc = expandRow( m_pCurRow, TRUE, &pLastRow))) + { + displayMessage( "Error expanding current row", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + if (m_pCurRow->bExpanded) + { + if ((pLastRow == m_pCurRow) || + (pLastRow->uiLevel != m_pCurRow->uiLevel)) + { + // We need to mark the starting row since the expansion is + // incomplete. + if (m_pRowAnchor) + { + f_free( &m_pRowAnchor); + } + + if( RC_BAD( rc = f_alloc( sizeof( DME_ROW_ANCHOR), &m_pRowAnchor))) + { + goto Exit; + } + + f_memset( m_pRowAnchor, 0, sizeof(DME_ROW_ANCHOR)); + m_pRowAnchor->ui64NodeId = m_pCurRow->ui64NodeId; + m_pRowAnchor->uiAnchorLevel = m_pCurRow->uiLevel; + m_pRowAnchor->bSingleLevel = TRUE; + } + } + bRefreshEditWindow = TRUE; + } + break; + } + + // Expand the current row. + case WPK_PLUS: + { + DME_ROW_INFO * pLastRow = NULL; + if (!m_pCurRow->bExpanded) + { + if (RC_BAD( rc = expandRow( m_pCurRow, FALSE, &pLastRow))) + { + goto Exit; + } + if (m_pCurRow->bExpanded) + { + if ((pLastRow == NULL) || + (pLastRow->uiLevel != m_pCurRow->uiLevel)) + { + // We need to mark the starting row since the expansion is + // incomplete. + if (m_pRowAnchor) + { + f_free( &m_pRowAnchor); + } + + if( RC_BAD( rc = f_alloc( sizeof( DME_ROW_ANCHOR), &m_pRowAnchor))) + { + goto Exit; + } + f_memset( m_pRowAnchor, 0, sizeof(DME_ROW_ANCHOR)); + m_pRowAnchor->ui64NodeId = m_pCurRow->ui64NodeId; + m_pRowAnchor->uiAnchorLevel = m_pCurRow->uiLevel; + m_pRowAnchor->bSingleLevel = FALSE; + } + } + bRefreshEditWindow = TRUE; + } + break; + } + + // Collapse the current row. + + case WPK_LEFT: + case WPK_MINUS: + { + if (m_pCurRow->bExpanded) + { + if (RC_BAD( tmpRc = collapseRow( &m_pCurRow))) + { + displayMessage( "Error collapsing current row", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + bRefreshEditWindow = TRUE; + } + break; + } + + // Move field cursor to the next row + + case WPK_DOWN: + { + if (RC_BAD( tmpRc = getNextRow( m_pCurRow, + &pTmpRow, + TRUE, + m_pCurRow ? !m_pCurRow->bExpanded : FALSE))) + { + displayMessage( "Failed to retrieve a new row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + + if (pTmpRow != NULL) + { + if( m_uiCurRow < uiMaxRow) + { + refreshRow( m_uiCurRow, m_pCurRow, FALSE); + m_uiCurRow++; + refreshRow( m_uiCurRow, pTmpRow, TRUE); + bRefreshStatusWindow = TRUE; + } + else + { + bRefreshEditWindow = TRUE; + } + m_pCurRow = pTmpRow; + checkDocument( &m_pCurDoc, m_pCurRow); + } + break; + } + + // Move field cursor to the prior row + + case WPK_UP: + { + if (RC_BAD( tmpRc = getPrevRow( m_pCurRow, + &pTmpRow, + TRUE))) + { + displayMessage( "Failed to retrieve a previous row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + if( pTmpRow != NULL) + { + if( m_uiCurRow > 0) + { + refreshRow( m_uiCurRow, m_pCurRow, FALSE); + m_uiCurRow--; + refreshRow( m_uiCurRow, pTmpRow, TRUE); + bRefreshStatusWindow = TRUE; + } + else + { + bRefreshEditWindow = TRUE; + } + m_pCurRow = pTmpRow; + // Did we change to another document? + checkDocument( &m_pCurDoc, m_pCurRow); + } + break; + } + + /* + Page up + */ + + case WPK_PGUP: + { + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + + if (RC_BAD( tmpRc = getPrevRow( m_pCurRow, + &pTmpRow, + TRUE))) + { + displayMessage( "Failed to retrieve a previous row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break;; + } + + if( pTmpRow != NULL) + { + m_pCurRow = pTmpRow; + // Did we change to another document? + checkDocument( &m_pCurDoc, m_pCurRow); + } + else + { + m_uiCurRow = 0; + break; + } + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Page down + */ + + case WPK_PGDN: + { + FLMBOOL bIgnoreAnchor = !m_pCurRow->bExpanded; + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if (RC_BAD( rc = getNextRow( m_pCurRow, + &pTmpRow, + TRUE, + bIgnoreAnchor))) + { + displayMessage( "Failed to retrieve a next row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + if (pTmpRow != NULL) + { + m_pCurRow = pTmpRow; + checkDocument( &m_pCurDoc, m_pCurRow); + } + else + { + m_uiCurRow += uiLoop; + if( m_uiCurRow >= uiNumRows) + { + m_uiCurRow = uiNumRows - 1; + } + break; + } + bIgnoreAnchor = FALSE; + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Go to the top of the buffer + */ +#if 0 // Removed functionality + case WPK_HOME: + { + m_pCurRow = m_pScrFirstRow; + m_uiCurRow = 0; + bRefreshEditWindow = TRUE; + checkDocument( &m_pCurDoc, m_pCurRow); + + break; + } + + /* + Jump to the end of the buffer + */ + + case WPK_END: + { + m_uiCurRow = uiMaxRow; + for( ;;) + { + if ( RC_BAD( rc = getNextRow( m_pCurRow, &pTmpRow, FALSE))) + { + goto Exit; + } + if (pTmpRow != NULL) + { + m_pCurRow = pTmpRow; + checkDocument( &m_pCurDoc, m_pCurRow); + } + else + { + break; + } + } + + setCurrentAtBottom(); + bRefreshEditWindow = TRUE; + break; + } +#endif + + case WPK_END: + { + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + + + FTXWinPrintf( m_pEditStatusWin, + "Scanning to the end of the display ..."); + + FTXWinClearToEOL( m_pEditStatusWin); + } + + m_uiCurRow = uiMaxRow; + for( ;;) + { + if ( RC_BAD( tmpRc = getNextRow( m_pCurRow, + &pTmpRow, + TRUE))) + { + displayMessage( "Failed to retrieve a next row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + if (pTmpRow != NULL) + { + m_pCurRow = pTmpRow; + // We need to check to see if we have moved to another document + checkDocument( &m_pCurDoc, m_pCurRow); + } + else + { + break; + } + } + + setCurrentAtBottom(); + bRefreshEditWindow = TRUE; + break; + } + + /* + Jump to the top of the buffer + */ + + case WPK_HOME: + { + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + + + FTXWinPrintf( m_pEditStatusWin, + "Scanning to the beginning of the display ..."); + + FTXWinClearToEOL( m_pEditStatusWin); + } + + m_uiCurRow = uiMaxRow; + for( ;;) + { + if ( RC_BAD( tmpRc = getPrevRow( m_pCurRow, + &pTmpRow, + TRUE))) + { + displayMessage( "Failed to retrieve a previous row.", tmpRc, + NULL, WPS_RED, WPS_WHITE); + break; + } + if (pTmpRow != NULL) + { + m_pCurRow = pTmpRow; + // We need to check to see if we have moved to another document + checkDocument( &m_pCurDoc, m_pCurRow); + } + else + { + m_pScrFirstRow = m_pCurRow; + break; + } + } + + setCurrentAtTop(); + bRefreshEditWindow = TRUE; + break; + } + + // Add something + case 'N': + case 'n': + case WPK_ALT_A: + { + if (RC_BAD( tmpRc = addSomething( &m_pCurRow))) + { + displayMessage( "Add/Insert operation failed", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + break; + } + bRefreshEditWindow = TRUE; + break; + } + + // Display attributes + case 'A': + case 'a': + { + if (RC_BAD( tmpRc = displayAttributes( m_pCurRow))) + { + displayMessage( + "Attributes could not be displayed", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + break; + } + + // Display node informatoin + case 'D': + case 'd': + { + if (RC_BAD( tmpRc = displayNodeInfo( m_pCurRow))) + { + displayMessage( + "Node information could not be displayed", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + break; + } + + case 'X': + case 'x': + { + if (RC_BAD( tmpRc = exportNode( m_pCurRow))) + { + displayMessage( + "Node could not be exported", + RC_SET( tmpRc), NULL, WPS_RED, WPS_WHITE); + } + break; + } + /* + Index operations + */ + + case 'I': + case 'i': + case WPK_ALT_I: + { + if( m_pDb == NULL) + { + break; + } + + if( RC_BAD( tmpRc = indexList())) + { + displayMessage( "Index List Operation Failed", + RC_SET(tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + bRefreshEditWindow = TRUE; + break; + } + + /* + List all documents + */ + case 'L': + case 'l': + case WPK_ALT_L: + { + + FLMUINT uiCollection = m_uiCollection; + FLMUINT64 ui64NodeId; + char szResponse[ 32]; + FLMUINT uiTermChar; + DME_ROW_INFO * pDocList = m_pDocList; + + szResponse [0] = 0; + + if (!m_pDocList) + { + + if( RC_BAD( tmpRc = selectCollection( &uiCollection, + &uiTermChar))) + { + displayMessage( "Error getting collection", + RC_SET(tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + m_uiCollection = uiCollection; + } + + if( RC_BAD( tmpRc = retrieveDocumentList( uiCollection, + &ui64NodeId, + &uiTermChar))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Unable to retrieve document list", + RC_SET(tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + while ( pDocList && pDocList->ui64DocId != ui64NodeId) + { + pDocList = pDocList->pNext; + } + + if (pDocList) + { + displayMessage( "Document already selected", + NE_XFLM_FAILURE, + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + // Retrieve the selected document + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + + FTXWinPrintf( m_pEditStatusWin, + "Retrieving selected document from the database ..."); + + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = retrieveNodeFromDb( uiCollection, ui64NodeId, 0))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Unable to retrieve selected document", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + // Add the document to the document list. + if (RC_BAD( tmpRc = addDocumentToList( uiCollection, ui64NodeId))) + { + if ( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Unable to add document to document list", + RC_SET(tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + bRefreshEditWindow = TRUE; + break; + } + + /* + Retrieve a node + */ + + case 'R': + case 'r': + case WPK_ALT_R: + { + FLMUINT uiCollection; + char szResponse[ 32]; + FLMUINT uiTermChar; + FLMUINT uiSrcLen; + FLMUINT uiNodeId; + FLMUINT64 ui64Tmp; + + szResponse [0] = 0; + requestInput( "[READ] Node Number", + szResponse, + sizeof( szResponse), + &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( (uiSrcLen = (FLMUINT)f_strlen( szResponse)) == 0) + { + uiNodeId = 0; + } + else + { + if( RC_BAD( tmpRc = getNumber( szResponse, &ui64Tmp, NULL))) + { + displayMessage( "Invalid node number", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + uiNodeId = (FLMUINT)ui64Tmp; + } + + + if (!m_pDocList) + { + + if( RC_BAD( tmpRc = selectCollection( &uiCollection, + &uiTermChar))) + { + displayMessage( "Error getting collection", + RC_SET(tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + if( uiTermChar != WPK_ENTER) + { + break; + } + + m_uiCollection = uiCollection; + } + + if( RC_BAD( tmpRc = retrieveNodeFromDb( uiCollection, uiNodeId, 0))) + { + if( m_pEditStatusWin) + { + FTXWinClearLine( m_pEditStatusWin, 0, 0); + } + displayMessage( "Unable to retrieve node", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + + // VISIT: Check to see if this is a document, we can add it to the list. + + bRefreshEditWindow = TRUE; + break; + } + + /* + Retrieve nodes via XPATH + */ + + case 'F': + case 'f': + case WPK_ALT_F: + doQuery( pszQuery, uiSzQueryBufSize); + break; + + /* + Clear all records from the current editor buffer. + NOTE: This will discard all changes + */ + + case 'C': + case 'c': + case WPK_ALT_C: + { + char szResponse[ 2]; + FLMUINT uiTermChar; + + szResponse [0] = 0; + requestInput( + "Clear buffer and discard modifications? (Y/N)", + szResponse, 2, &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( szResponse [0] == 'y' || szResponse [0] == 'Y') + { + setScrFirstRow( NULL); + if (RC_BAD( rc = setCurrentRow(NULL, 0))) + { + goto Exit; + } + + bRefreshEditWindow = TRUE; + } + break; + } + + /* + Global administration options (including statistics gathering) + */ + + case '#': + { + szAction[ 0] = '\0'; + requestInput( + "Statistics (b = begin, e = end, r = reset)", + szAction, sizeof( szAction), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + break; + } + + if( m_pEditStatusWin) + { + FTXWinSetCursorPos( m_pEditStatusWin, 0, 0); + } + + if( szAction [0] == 'b' || szAction [0] == 'B') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Starting statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( + F_DOMEDIT_CONFIG_STATS_START))) + { + displayMessage( "Error Starting Statistics", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + } + else if( szAction [0] == 'e' || szAction [0] == 'E') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Stopping statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( + F_DOMEDIT_CONFIG_STATS_STOP))) + { + displayMessage( "Error Stopping Statistics", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + } + else if( szAction [0] == 'r' || szAction [0] == 'R') + { + if( m_pEditStatusWin) + { + FTXWinPrintf( m_pEditStatusWin, + "Resetting statistics ..."); + FTXWinClearToEOL( m_pEditStatusWin); + } + + if( RC_BAD( tmpRc = globalConfig( F_DOMEDIT_CONFIG_STATS_RESET))) + { + displayMessage( "Error Resetting Statistics", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + } + else + { + displayMessage( "Invalid Request", + RC_SET( NE_XFLM_FAILURE), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + bRefreshStatusWindow = TRUE; + break; + } + + case '?': + { + showHelp( &uiHelpKey); + break; + } + + case WPK_F10: + { + if( m_bMonochrome) + { + m_bMonochrome = FALSE; + } + else + { + m_bMonochrome = TRUE; + } + + bRefreshEditWindow = TRUE; + break; + } + + case WPK_F8: /* Index Manager */ + { + char szDbPath [F_PATH_MAX_SIZE]; + F_Db * pTmpDb = NULL; + + if( m_pDb == NULL) + { + break; + } + + f_threadDestroy( &pIxManagerThrd); + + (void)m_pDb->getDbControlFileName( szDbPath, sizeof( szDbPath)); + + if (RC_OK( tmpRc = dbSystem.dbOpen( szDbPath, + NULL, + NULL, NULL, TRUE, + (IF_Db **)&pTmpDb))) + { + f_threadCreate( &pIxManagerThrd, + flstIndexManagerThread, + "index_manager", + FLM_DEFAULT_THREAD_GROUP, 0, + (void *)pTmpDb); + } + else + { + displayMessage( "Failed to open database", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + } + break; + } + + case WPK_F9: /* Memory Manager */ + { + f_threadDestroy( &pMemManagerThrd); + f_threadCreate( &pMemManagerThrd, + flstMemoryManagerThread, "memory_manager"); + break; + } + + case WPK_DELETE: + { + if (RC_BAD( tmpRc = deleteRow( &m_pCurRow))) + { + displayMessage( "Delete operation failed", + RC_SET( tmpRc), + NULL, + WPS_RED, + WPS_WHITE); + break; + } + bRefreshEditWindow = TRUE; + break; + } + + case WPK_ESCAPE: + case 'Q': + case 'q': + case 'Z': + case 'z': + case WPK_ALT_Q: + case WPK_ALT_Z: + { + bDoneEditing = TRUE; + break; + } + + default: + { + /* + Unrecognized key ... ignore. + */ + + break; + } + } + } + else + { + f_sleep( 1); + } + } + +Exit: + + f_free( &pszQuery); + + f_threadDestroy( &pIxManagerThrd); + f_threadDestroy( &pMemManagerThrd); + + if( m_pEditWindow) + { + (void) FTXWinFree( &m_pEditWindow); + } + + if( m_pEditStatusWin) + { + (void) FTXWinFree( &m_pEditStatusWin); + } + + return( rc); +} + + +/**************************************************************************** +Desc: Draw the screen - refresh the display +*****************************************************************************/ +RCODE F_DomEditor::refreshEditWindow( + DME_ROW_INFO ** ppFirstRow, + DME_ROW_INFO * pCursorRow, + FLMUINT * puiCurRow) +{ + FLMUINT uiLoop; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + DME_ROW_INFO * pFirstRow; + DME_ROW_INFO * pTmpRow; + FLMUINT uiCurRow; + FLMBOOL bCurrentVisible = FALSE; + FLMBOOL bStartedTrans = FALSE; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( pCursorRow == NULL) + { + *ppFirstRow = NULL; + *puiCurRow = 0; + } + + FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, &uiNumRows); + + /* + See if the cursor row is already being displayed. + */ + + uiCurRow = 0; + pFirstRow = *ppFirstRow; + pTmpRow = pFirstRow; + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( pCursorRow == pTmpRow) + { + uiCurRow = uiLoop; + bCurrentVisible = TRUE; + break; + } + + if (RC_BAD( rc = getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + if (pTmpRow == NULL) + { + break; + } + checkDocument(&m_pCurDoc, pTmpRow); + } + + /* + If the current node is not displayed, scroll the screen + so that the node is visible. + */ + + if( !bCurrentVisible) + { + uiCurRow = *puiCurRow; + pFirstRow = pCursorRow; + while( uiCurRow && pFirstRow) + { + if (RC_BAD( rc = getPrevRow( pFirstRow, &pTmpRow))) + { + goto Exit; + } + if( pTmpRow) + { + pFirstRow = pTmpRow; + } + uiCurRow--; + } + } + + *ppFirstRow = pFirstRow; + *puiCurRow = uiCurRow; + + // Turn display refresh off temporarily + + FTXSetRefreshState( m_pScreen->pFtxInfo, TRUE); + + // Start a transaction + + if( RC_BAD( rc = m_pDb->checkTransaction( XFLM_READ_TRANS, &bStartedTrans))) + { + goto Exit; + } + + // Refresh all rows of the edit window. All rows beyond the end + // of the tree are cleared. + + pTmpRow = *ppFirstRow; + for( uiLoop = 0; uiLoop < uiNumRows; uiLoop++) + { + if( pTmpRow && pTmpRow == pCursorRow) + { + refreshRow( uiLoop, pTmpRow, TRUE); + *puiCurRow = uiLoop; + } + else + { + refreshRow( uiLoop, pTmpRow, FALSE); + } + + if( pTmpRow) + { + if (RC_BAD( rc = getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + } + +Exit: + + if( bStartedTrans) + { + m_pDb->transAbort(); + } + + /* + Re-enable display refresh + */ + + FTXSetRefreshState( m_pScreen->pFtxInfo, FALSE); + return( rc); +} + + +/**************************************************************************** +Desc: Get the domnode +*****************************************************************************/ +RCODE F_DomEditor::getDomNode( + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId, + F_DOMNode ** ppDomNode + ) +{ + RCODE rc = NE_XFLM_OK; + + if (uiAttrNameId) + { + if (RC_BAD( rc = m_pDb->getAttribute( m_uiCollection, ui64NodeId, + uiAttrNameId, + (IF_DOMNode **)ppDomNode))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = m_pDb->getNode( m_uiCollection, ui64NodeId, ppDomNode))) + { + goto Exit; + } + } + +Exit: + + return rc; + +} +/**************************************************************************** +Name: F_DomEditorDefaultDispHook +Desc: Default line display format routine +*****************************************************************************/ +RCODE F_DomEditorDefaultDispHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFlags = 0; + + if( !pRow) + { + goto Exit; + } + + pDomEditor->getControlFlags( pRow, &uiFlags); + + // Are we just displaying a value, or should we use the Dom? + if (pRow->bUseValue) + { + rc = formatRow( pDomEditor, pRow, puiNumVals, uiFlags); + goto Exit; + } + + + // Need to find out what type of node we have to display. + if (RC_BAD( rc = getDOMNode( pDomEditor, pRow))) + { + goto Exit; + } + + switch (pRow->eType) + { + case DOCUMENT_NODE: + { + if (RC_BAD( rc = formatDocumentNode( pDomEditor, pRow, puiNumVals, uiFlags))) + { + goto Exit; + } + break; + } + case ELEMENT_NODE: + { + + // It is possible to have data embedded in the element node. + + if (uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA) + { + if (RC_BAD( rc = formatDataNode( pDomEditor, pRow, puiNumVals, uiFlags, NULL, NULL))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = formatElementNode( pDomEditor, pRow, puiNumVals, uiFlags))) + { + goto Exit; + } + } + break; + } + case DATA_NODE: + { + if (RC_BAD( rc = formatDataNode( pDomEditor, pRow, puiNumVals, uiFlags, NULL, NULL))) + { + goto Exit; + } + break; + } + case CDATA_SECTION_NODE: + { + if (RC_BAD( rc = formatDataNode( pDomEditor, pRow, puiNumVals, uiFlags, + ""))) + { + goto Exit; + } + break; + } + case COMMENT_NODE: + { + if (RC_BAD( rc = formatDataNode( pDomEditor, pRow, puiNumVals, uiFlags, + ""))) + { + goto Exit; + } + break; + } + case PROCESSING_INSTRUCTION_NODE: + { + if (RC_BAD( rc = formatProcessingInstruction( pDomEditor, pRow, puiNumVals, uiFlags))) + { + goto Exit; + } + break; + } + case ATTRIBUTE_NODE: + { + if (RC_BAD( rc = formatAttributeNode( pDomEditor, pRow, puiNumVals, uiFlags))) + { + goto Exit; + } + break; + } + case INVALID_NODE: + break; // Don't know just yet what to do, but will figure it out soon. + default: + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + } + + +Exit: + // Don't hold on to the Dom node when finished. + if ( pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + return( rc); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::refreshRow( + FLMUINT uiRow, + DME_ROW_INFO * pRow, + FLMBOOL bSelected) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumVals; + FLMUINT uiLoop; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + FTXWinGetCanvasSize( m_pEditWindow, &uiNumCols, &uiNumRows); + + FTXWinSetCursorPos( m_pEditWindow, 0, uiRow); + FTXWinClearLine( m_pEditWindow, 0, uiRow); + + if (!pRow) + { + goto Exit; + } + + f_memset( m_dispColumns, 0, sizeof( m_dispColumns)); + uiNumVals = 0; + + /* + Call the display formatter + */ + + if( m_pDisplayHook) + { + if( RC_BAD( rc = m_pDisplayHook( this, pRow, &uiNumVals))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = F_DomEditorDefaultDispHook( this, pRow, &uiNumVals))) + { + goto Exit; + } + } + + for( uiLoop = 0; uiLoop < uiNumVals; uiLoop++) + { + FTXWinSetCursorPos( m_pEditWindow, m_dispColumns[ uiLoop].uiCol, uiRow); + FTXWinCPrintf( m_pEditWindow, m_dispColumns[ uiLoop].uiBackground, + m_dispColumns[ uiLoop].uiForeground, "%s", m_dispColumns[ uiLoop].szString); + } + + if( bSelected) + { + FLMUINT uiBackground = m_bMonochrome ? WPS_LIGHTGRAY : WPS_CYAN; + FLMUINT uiForeground = m_bMonochrome ? WPS_BLACK : WPS_WHITE; + FTXWinPaintRow( m_pEditWindow, &uiBackground, &uiForeground, uiRow); + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Repositions the cursor to display at the top of + the editor window +*****************************************************************************/ +void F_DomEditor::setCurrentAtTop( void) +{ + m_uiCurRow = 0; + m_pCurRow = m_pScrFirstRow; +} + + +/**************************************************************************** +Desc: Repositions the cursor (and current node) to display at the bottom of + the editor window +*****************************************************************************/ +void F_DomEditor::setCurrentAtBottom( void) +{ + FLMUINT uiNumRows; + + flmAssert( m_pEditWindow != NULL); + + FTXWinGetCanvasSize( m_pEditWindow, NULL, &uiNumRows); + uiNumRows--; + + m_pCurRow = m_pScrLastRow; + + m_uiCurRow = uiNumRows; +} + +/**************************************************************************** +Name: displayMessage +Desc: +*****************************************************************************/ +RCODE F_DomEditor::displayMessage( + char * pszMessage, + RCODE rcOfMessage, + FLMUINT * puiTermChar, + FLMUINT uiBackground, + FLMUINT uiForeground) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiTermChar) + { + *puiTermChar = 0; + } + + FTXDisplayMessage( m_pScreen, m_bMonochrome ? WPS_LIGHTGRAY : uiBackground, + m_bMonochrome ? WPS_BLACK : uiForeground, + pszMessage, F_DbSystem::_errorString( rcOfMessage), puiTermChar); + + return( rc); +} + +/**************************************************************************** +Name: openNewDb +Desc: +*****************************************************************************/ +RCODE F_DomEditor::openNewDb( void) +{ + RCODE rc = NE_XFLM_OK; + char szResponse [100]; + FLMUINT uiChar; + F_DbSystem dbSystem; + + szResponse [0] = 0; + flmAssert( m_pDb == NULL); + for (;;) + { + if (RC_BAD( rc = requestInput( "Enter name of DB to open", + szResponse, sizeof( szResponse), &uiChar))) + { + goto Exit; + } + + if (uiChar == WPK_ESCAPE) + { + break; + } + if (RC_BAD( rc = dbSystem.dbOpen( szResponse, NULL, NULL, NULL, TRUE, + (IF_Db **)&m_pDb))) + { + displayMessage( "Unable to open database", rc, + NULL, WPS_RED, WPS_WHITE); + m_pDb = NULL; + continue; + } + m_bOpenedDb = TRUE; + break; + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: requestInput +Desc: +*****************************************************************************/ +RCODE F_DomEditor::requestInput( + char * pszMessage, + char * pszResponse, + FLMUINT uiMaxRespLen, + FLMUINT * puiTermChar) +{ + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows = 3; + FLMUINT uiNumWinCols; + FTX_WINDOW * pWindow = NULL; + IF_FileHdl * pFileHdl = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXScreenGetSize( m_pScreen, &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + uiNumWinCols = uiNumCols - 8; + + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetScroll( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetCursorType( pWindow, WPS_CURSOR_UNDERLINE); + + if( FTXWinSetBackFore( pWindow, m_bMonochrome ? WPS_BLACK : WPS_CYAN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinMove( pWindow, (uiNumCols - uiNumWinCols) / 2, + (uiNumRows - uiNumWinRows) / 2) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + for( ;;) + { + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + FTXWinPrintf( pWindow, "%s: ", pszMessage); + + if( FTXLineEdit( pWindow, pszResponse, uiMaxRespLen, uiMaxRespLen, + NULL, puiTermChar) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if( *puiTermChar == WPK_F1) + { + FLMUINT uiBytesRead; + char * pszSrc; + char * pszDest; + + if( RC_BAD( rc = gv_pFileSystem->Open( pszResponse, XFLM_IO_RDONLY, + &pFileHdl))) + { + displayMessage( "Unable to open file", rc, + NULL, WPS_RED, WPS_WHITE); + continue; + } + + if( RC_BAD( rc = pFileHdl->Read( 0, uiMaxRespLen, + pszResponse, &uiBytesRead))) + { + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + pFileHdl->Release(); + pFileHdl = NULL; + pszResponse[ uiBytesRead] = '\0'; + + // Convert newlines to spaces. Multiple consecutive newlines + // will be converted to a single space. + + pszSrc = pszDest = pszResponse; + while (*pszSrc) + { + if (*pszSrc == '\r' || *pszSrc == '\n') + { + *pszDest = ' '; + pszDest++; + pszSrc++; + while (*pszSrc == '\r' || *pszSrc == '\n') + { + pszSrc++; + } + } + else + { + if (pszDest != pszSrc) + { + *pszDest = *pszSrc; + } + pszSrc++; + pszDest++; + } + } + *pszDest = 0; + } + else + { + break; + } + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/**************************************************************************** +Name: getControlFlags +Desc: +*****************************************************************************/ +RCODE F_DomEditor::getControlFlags( + DME_ROW_INFO * pCurRow, + FLMUINT * puiFlags) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + if ( !pCurRow) + { + goto Exit; + } + + *puiFlags = pCurRow->uiFlags; +Exit: + return( rc); +} + + +/**************************************************************************** +Name: setControlFlags +Desc: +*****************************************************************************/ +RCODE F_DomEditor::setControlFlags( + DME_ROW_INFO * pCurRow, + FLMUINT uiFlags) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + pCurRow->uiFlags = uiFlags; + + return( rc); +} + + +/**************************************************************************** +Desc: Get the previous row to display. +*****************************************************************************/ +RCODE F_DomEditor::getPrevRow( + DME_ROW_INFO * pCurRow, + DME_ROW_INFO ** ppPrevRow, + FLMBOOL bFetchPrevRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pPrevRow = NULL; + DME_ROW_INFO * pDocList = m_pCurDoc; + F_DOMNode * pChildDomNode = NULL; + F_DOMNode * pSiblingDomNode = NULL; + F_DOMNode * pParentDomNode = NULL; + FLMBOOL bCurRowDataLocal; + + flmAssert( m_bSetupCalled == TRUE); + + if (pCurRow == NULL) + { + goto Exit; + } + + // Get the previous row. + pPrevRow = pCurRow->pPrev; + + // Got it, we're done... + if (pPrevRow) + { + goto Exit; + } + + + if (!bFetchPrevRow) + { + goto Exit; + } + + // If we were on the first display line, we will need to see about fetching + // the previous row + if (pCurRow == m_pScrFirstRow) + { + if (m_bDocList) + { + if (RC_BAD( rc = getPrevTitle( pCurRow, &pPrevRow))) + { + goto Exit; + } + if (pPrevRow == NULL) + { + goto Exit; + } + goto InsertRow; + } + + if (pCurRow->uiFlags & F_DOMEDIT_FLAG_NODOM) + { + goto Exit; + } + + if (RC_BAD( rc = getDOMNode( this, pCurRow))) + { + goto Exit; + } + + if (RC_BAD( rc = pCurRow->pDomNode->isDataLocalToNode( m_pDb, &bCurRowDataLocal))) + { + goto Exit; + } + + if ((pCurRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG) && + (!(pCurRow->uiFlags & F_DOMEDIT_FLAG_NOCHILD) || bCurRowDataLocal)) + { + if (bCurRowDataLocal && pCurRow->pDomNode->getNodeType() == ELEMENT_NODE) + { + pChildDomNode = pCurRow->pDomNode; + pChildDomNode->AddRef(); + } + else if (RC_BAD( rc = pCurRow->pDomNode->getLastChild( m_pDb, (IF_DOMNode **)&pChildDomNode))) + { + flmAssert( rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND); + goto Exit; + } + + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel+1, + pChildDomNode, + &pPrevRow))) + { + goto Exit; + } + + if (!bCurRowDataLocal) + { + pPrevRow->uiFlags = pCurRow->uiFlags | F_DOMEDIT_FLAG_ENDTAG; + if (RC_BAD( rc = pChildDomNode->isDataLocalToNode( m_pDb, &pPrevRow->bHasElementData))) + { + goto Exit; + } + } + else + { + pPrevRow->uiFlags = pCurRow->uiFlags & ~F_DOMEDIT_FLAG_ENDTAG; + } + + if (bCurRowDataLocal) + { + pPrevRow->uiFlags |= F_DOMEDIT_FLAG_ELEMENT_DATA; + } + + if (m_pRowAnchor) + { + pPrevRow->bExpanded = !m_pRowAnchor->bSingleLevel; + } + + goto InsertRow; + } + + if (pCurRow->eType != DOCUMENT_NODE && + !(pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + if (RC_BAD( rc = pCurRow->pDomNode->getPreviousSibling( + m_pDb, + (IF_DOMNode **)&pSiblingDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + + if (RC_OK( rc)) + { + // Build a new row. + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel, + pSiblingDomNode, + &pPrevRow))) + { + goto Exit; + } + + pPrevRow->uiFlags = pCurRow->uiFlags & ~(F_DOMEDIT_FLAG_ENDTAG); + if (m_pRowAnchor) + { + pPrevRow->bExpanded = !m_pRowAnchor->bSingleLevel && pPrevRow->bHasChildren; + } + if ( pPrevRow->bExpanded) + { + pPrevRow->uiFlags |= F_DOMEDIT_FLAG_ENDTAG; + } + goto InsertRow; + } + } + + if (!(pCurRow->uiFlags & F_DOMEDIT_FLAG_NOPARENT) || + (bCurRowDataLocal && pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + if (bCurRowDataLocal && pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA) + { + pParentDomNode = pCurRow->pDomNode; + pParentDomNode->AddRef(); + } + else if (RC_BAD( rc = pCurRow->pDomNode->getParentNode( + m_pDb, + (IF_DOMNode **)&pParentDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + + if (RC_OK( rc)) + { + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel - 1, + pParentDomNode, + &pPrevRow))) + { + goto Exit; + } + + pPrevRow->uiFlags = pCurRow->uiFlags & ~(F_DOMEDIT_FLAG_ENDTAG | F_DOMEDIT_FLAG_ELEMENT_DATA); + if (RC_BAD( rc = pParentDomNode->isDataLocalToNode( m_pDb, &pPrevRow->bHasElementData))) + { + goto Exit; + } + pPrevRow->bExpanded = TRUE; + goto InsertRow; + } + } + + rc = NE_XFLM_OK; + + // Is there a previous document that we can display? + // The pPrevRow should always be NULL at this point. + + flmAssert( pPrevRow == NULL); + + if (pDocList) + { + pDocList = pDocList->pPrev; + } + + if (pDocList) + { + if (RC_BAD( rc = getDOMNode( this, pDocList))) + { + goto Exit; + } + + // Build a new row. + if (RC_BAD( rc = buildNewRow( -1, + pDocList->pDomNode, + &pPrevRow))) + { + goto Exit; + } + + goto InsertRow; + + } + else + { + goto Exit; + } + +InsertRow: + + // Insert at the front. + if (RC_BAD( rc = insertRow( pPrevRow, NULL))) + { + goto Exit; + } + m_pCurRow = m_pScrFirstRow; + + // Need to remove the last row. + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseLastRow(); + } + + } + + +Exit: + + + if (pSiblingDomNode) + { + pSiblingDomNode->Release(); + } + + if (pChildDomNode) + { + pChildDomNode->Release(); + } + + if (pParentDomNode) + { + pParentDomNode->Release(); + } + + if (pCurRow && pCurRow->pDomNode) + { + pCurRow->pDomNode->Release(); + pCurRow->pDomNode = NULL; + } + + if ( m_pCurDoc && m_pCurDoc->pDomNode) + { + m_pCurDoc->pDomNode->Release(); + m_pCurDoc->pDomNode = NULL; + } + + if (pDocList && pDocList->pDomNode) + { + pDocList->pDomNode->Release(); + pDocList->pDomNode = NULL; + } + + if (pPrevRow && pPrevRow->pDomNode) + { + pPrevRow->pDomNode->Release(); + pPrevRow->pDomNode = NULL; + } + + *ppPrevRow = pPrevRow; + + return rc; +} + + +/**************************************************************************** +Desc: Get the next row. If the current row is the last row being displayed, + we need to also remove the first row. +*****************************************************************************/ +RCODE F_DomEditor::getNextRow( + DME_ROW_INFO * pCurRow, + DME_ROW_INFO ** ppNextRow, + FLMBOOL bFetchNextRow, + FLMBOOL bIgnoreAnchor + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pNextRow = NULL; + DME_ROW_INFO * pDocList = m_pCurDoc; + F_DOMNode * pChildDomNode = NULL; + F_DOMNode * pSiblingDomNode = NULL; + F_DOMNode * pParentDomNode = NULL; + FLMBOOL bCurRowDataLocal; + + flmAssert( m_bSetupCalled == TRUE); + + if (pCurRow == NULL) + { + goto Exit; + } + + pNextRow = pCurRow->pNext; + + // Got it, we're done... + if (pNextRow) + { + goto Exit; + } + + + if (!bFetchNextRow) + { + goto Exit; + } + + // If we were on the last display line, we will need to see about fetching + // the next row + if (m_uiNumRows <= m_uiEditCanvasRows) + { + if (m_bDocList) + { + if (RC_BAD( rc = getNextTitle( pCurRow, &pNextRow))) + { + goto Exit; + } + if (pNextRow == NULL) + { + goto Exit; + } + goto InsertRow; + } + + if (pCurRow->uiFlags & F_DOMEDIT_FLAG_NODOM) + { + goto Exit; + } + + // If the current row was expanded or there is an anchor row, we will + // need to check the Anchor to see how we are expanding it. Either + // deep or single level. + if (RC_BAD( rc = getDOMNode( this, pCurRow))) + { + goto Exit; + } + + if (RC_BAD( rc = pCurRow->pDomNode->isDataLocalToNode( m_pDb, &bCurRowDataLocal))) + { + goto Exit; + } + + if (m_pRowAnchor && !bIgnoreAnchor && pCurRow->bExpanded) + { + if (( m_pRowAnchor->bSingleLevel && + m_pRowAnchor->uiAnchorLevel == pCurRow->uiLevel) || + (!m_pRowAnchor->bSingleLevel)) + { + if (!(pCurRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + if (bCurRowDataLocal && pCurRow->pDomNode->getNodeType() == ELEMENT_NODE && + !(pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + pChildDomNode = pCurRow->pDomNode; + pChildDomNode->AddRef(); + } + // Get the first child. + else if (RC_BAD( rc = pCurRow->pDomNode->getFirstChild( + m_pDb, (IF_DOMNode **)&pChildDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + + } + + if (RC_OK( rc)) + { + // Build a new row. + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel+1, + pChildDomNode, + &pNextRow))) + { + goto Exit; + } + + pNextRow->uiFlags = pCurRow->uiFlags & ~(F_DOMEDIT_FLAG_ENDTAG); + if (bCurRowDataLocal && pCurRow->pDomNode->getNodeType() == ELEMENT_NODE) + { + pNextRow->uiFlags |= F_DOMEDIT_FLAG_ELEMENT_DATA; + } + + if (!bCurRowDataLocal) + { + if (RC_BAD( rc = pChildDomNode->isDataLocalToNode( m_pDb, &pNextRow->bHasElementData))) + { + goto Exit; + } + } + + pNextRow->bExpanded = !m_pRowAnchor->bSingleLevel; + goto InsertRow; + } + } + } + } + + rc = NE_XFLM_OK; + + if (pCurRow->bExpanded && !bIgnoreAnchor) + { + if ((!(pCurRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG)) && + (!(pCurRow->uiFlags & F_DOMEDIT_FLAG_NOCHILD) || bCurRowDataLocal)) + { + if (bCurRowDataLocal && pCurRow->pDomNode->getNodeType() == ELEMENT_NODE && + !(pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + pChildDomNode = pCurRow->pDomNode; + pChildDomNode->AddRef(); + } + // Get the first child. + else if (RC_BAD( rc = pCurRow->pDomNode->getFirstChild( + m_pDb, (IF_DOMNode **)&pChildDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + + if (RC_OK( rc)) + { + // Build a new row. + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel+1, + pChildDomNode, + &pNextRow))) + { + goto Exit; + } + + pNextRow->uiFlags = pCurRow->uiFlags & ~(F_DOMEDIT_FLAG_ENDTAG); + if (bCurRowDataLocal && pCurRow->pDomNode->getNodeType() == ELEMENT_NODE) + { + pNextRow->uiFlags |= F_DOMEDIT_FLAG_ELEMENT_DATA; + } + + if (m_pRowAnchor) + { + pNextRow->bExpanded = !m_pRowAnchor->bSingleLevel; + } + goto InsertRow; + } + } + } + + rc = NE_XFLM_OK; + + if (pCurRow->eType != DOCUMENT_NODE) + { + if (RC_BAD( rc = pCurRow->pDomNode->getNextSibling( + m_pDb, + (IF_DOMNode **)&pSiblingDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + + if (RC_OK( rc)) + { + // Build a new row. + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel, + pSiblingDomNode, + &pNextRow))) + { + goto Exit; + } + + pNextRow->uiFlags = pCurRow->uiFlags & ~(F_DOMEDIT_FLAG_ENDTAG); + + if (m_pRowAnchor) + { + pNextRow->bExpanded = !m_pRowAnchor->bSingleLevel && pNextRow->bHasChildren; + } + goto InsertRow; + } + } + + rc = NE_XFLM_OK; + + // No sibling node found. Must go up one level to parent if we can + if (!(pCurRow->uiFlags & F_DOMEDIT_FLAG_NOPARENT) || + (bCurRowDataLocal && pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + if (bCurRowDataLocal && (pCurRow->uiFlags & F_DOMEDIT_FLAG_ELEMENT_DATA)) + { + pParentDomNode = pCurRow->pDomNode; + pParentDomNode->AddRef(); + } + else if (RC_BAD( rc = pCurRow->pDomNode->getParentNode( + m_pDb, + (IF_DOMNode **)&pParentDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + } + + if (RC_OK( rc)) + { + if (RC_BAD( rc = buildNewRow( (FLMINT)pCurRow->uiLevel - 1, + pParentDomNode, + &pNextRow))) + { + goto Exit; + } + pNextRow->uiFlags = (pCurRow->uiFlags & ~F_DOMEDIT_FLAG_ELEMENT_DATA) | F_DOMEDIT_FLAG_ENDTAG; + pNextRow->bExpanded = TRUE; + + if (RC_BAD( rc = pParentDomNode->isDataLocalToNode( m_pDb, &pNextRow->bHasElementData))) + { + goto Exit; + } + + goto InsertRow; + } + } + + rc = NE_XFLM_OK; + + // If nothing was found, is there another document in the list that we + // can display? Getting to this point indicates that we could not find + // another row to display in the current document. + + flmAssert( pNextRow == NULL); + + if (pDocList) + { + pDocList = pDocList->pNext; + } + + if (pDocList) + { + + if (RC_BAD( rc = getDOMNode( this, pDocList))) + { + goto Exit; + } + + // Build a new row. + if (RC_BAD( rc = buildNewRow( -1, + pDocList->pDomNode, + &pNextRow))) + { + goto Exit; + } + goto InsertRow; + + } + else + { + goto Exit; + } + + +InsertRow: + + if (RC_BAD( rc = insertRow( pNextRow, + pCurRow))) + { + goto Exit; + } + + // Need to remove the first row. + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseRow( &m_pScrFirstRow); + m_uiNumRows--; + } + + // Set the new last row + m_pScrLastRow = pNextRow; + + // If there is an anchor row, then did we just display it's end tag? + if (m_pRowAnchor) + { + if (m_pRowAnchor->ui64NodeId == pNextRow->ui64NodeId) + { + // We can remove the anchor now. + + f_free( &m_pRowAnchor); + } + } + + } + + +Exit: + + if (pSiblingDomNode) + { + pSiblingDomNode->Release(); + } + + if (pChildDomNode) + { + pChildDomNode->Release(); + } + + if (pParentDomNode) + { + pParentDomNode->Release(); + } + + if (pCurRow && pCurRow->pDomNode) + { + pCurRow->pDomNode->Release(); + pCurRow->pDomNode = NULL; + } + + if (m_pCurDoc && m_pCurDoc->pDomNode) + { + m_pCurDoc->pDomNode->Release(); + m_pCurDoc->pDomNode = NULL; + } + + if (pDocList && pDocList->pDomNode) + { + pDocList->pDomNode->Release(); + pDocList->pDomNode = NULL; + } + + if (pNextRow && pNextRow->pDomNode) + { + pNextRow->pDomNode->Release(); + pNextRow->pDomNode = NULL; + } + + *ppNextRow = pNextRow; + + return rc; +} + + +/**************************************************************************** +Desc: Set's root row - first row in the list. +*****************************************************************************/ +void F_DomEditor::setScrFirstRow( + DME_ROW_INFO * pScrFirstRow + ) +{ + m_uiNumRows = 0; + if (m_pScrFirstRow) + { + while (m_pScrFirstRow) + { + releaseRow( &m_pScrFirstRow); + } + } + + if (m_pDocList) + { + while (m_pDocList) + { + releaseRow( &m_pDocList); + } + m_pCurDoc = NULL; + } + + if (pScrFirstRow) + { + m_pScrFirstRow = pScrFirstRow; + m_uiNumRows++; + + m_pScrLastRow = pScrFirstRow; + while (m_pScrLastRow->pNext) + { + m_pScrLastRow = m_pScrLastRow->pNext; + m_uiNumRows++; + } + } + else + { + m_pScrLastRow = m_pScrFirstRow; + f_free( &m_pRowAnchor); + } +} + + + + +/**************************************************************************** +Desc: Build a new row structure. Note that when deleting the row, it will + also be necessary to delete the value buffer and release the Dom object. +*****************************************************************************/ +RCODE F_DomEditor::buildNewRow( + FLMINT iLevel, + F_DOMNode * pDomNode, + DME_ROW_INFO ** ppNewRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pNewRow; + F_DOMNode * pCurrDOMNode = NULL; + + // Build the new row + + if( RC_BAD( rc = f_alloc( sizeof( DME_ROW_INFO), &pNewRow))) + { + goto Exit; + } + + f_memset( pNewRow, 0, sizeof( DME_ROW_INFO)); + + // Determine the node level if it isn't passed in. + if (iLevel == -1) + { + iLevel = 0; + pCurrDOMNode = pDomNode; + pCurrDOMNode->AddRef(); + while (RC_OK( rc = pCurrDOMNode->getParentNode( + m_pDb, (IF_DOMNode **)&pCurrDOMNode))) + { + iLevel++; + } + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + pCurrDOMNode->Release(); + pCurrDOMNode = NULL; + rc = NE_XFLM_OK; + } + + pNewRow->eType = pDomNode->getNodeType(); + if (pNewRow->eType == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = pDomNode->getParentId( m_pDb, &pNewRow->ui64NodeId))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pDomNode->getNodeId( m_pDb, &pNewRow->ui64NodeId))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pDomNode->getDocumentId( m_pDb, &pNewRow->ui64DocId))) + { + goto Exit; + } + + pNewRow->uiLevel = (FLMUINT)iLevel; + pDomNode->getNameId( m_pDb, &pNewRow->uiNameId); + pNewRow->bExpanded = FALSE; + if ( pDomNode->getNodeType() != ATTRIBUTE_NODE) + { + if (RC_BAD( rc = pDomNode->hasChildren( m_pDb, &pNewRow->bHasChildren))) + { + goto Exit; + } + + if (RC_BAD( rc = pDomNode->hasAttributes( m_pDb, &pNewRow->bHasAttributes))) + { + goto Exit; + } + } + else + { + pNewRow->bHasChildren = FALSE; + pNewRow->bHasAttributes = FALSE; + } + + //pNewRow->pDomNode = pDomNode; + *ppNewRow = pNewRow; + pNewRow = NULL; + +Exit: + + if (pCurrDOMNode) + { + pCurrDOMNode->Release(); + } + + if (pNewRow) + { + if ( pNewRow->puzValue) + { + f_free( pNewRow->puzValue); + } + + if (pNewRow->pDomNode) + { + pNewRow->pDomNode->Release(); + } + + f_free( &pNewRow); + } + return rc; +} + + +/**************************************************************************** +Desc: Expands a row to reveal it immediate children. +*****************************************************************************/ +RCODE F_DomEditor::expandRow( + DME_ROW_INFO * pRow, + FLMBOOL bOneLevel, + DME_ROW_INFO ** ppLastRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pNewRow = NULL; + DME_ROW_INFO * pPrevRow = pRow; + F_DOMNode * pDomNode = NULL; + F_DOMNode * pChildNode = NULL; + FLMBOOL bHaveFirstChild = FALSE; + FLMBOOL bRowHasElementData = pRow->bHasElementData; + + // Is this row already expanded, or does it not have any children and it doesn't have any data? + if ( pRow->bExpanded || (!pRow->bHasChildren && !pRow->bHasElementData)) + { + goto Exit; + } + + // Get the Dom Node if it isn't already attached to the row. + if (RC_BAD( rc = getDOMNode( this, pRow))) + { + goto Exit; + } + + + while (!(pRow == m_pScrLastRow && m_uiNumRows >= m_uiEditCanvasRows) && + (!(pPrevRow == m_pScrLastRow && m_uiNumRows >= m_uiEditCanvasRows))) + { + if (bRowHasElementData) + { + pDomNode = pRow->pDomNode; + pDomNode->AddRef(); + } + else if (!bHaveFirstChild) + { + if (RC_BAD( rc = pRow->pDomNode->getFirstChild( m_pDb, + (IF_DOMNode **)&pDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + break; + } + bHaveFirstChild = TRUE; + } + else + { + if (RC_BAD( rc = pChildNode->getNextSibling( m_pDb, + (IF_DOMNode **)&pDomNode))) + { + if (rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + break; + } + } + + if (RC_BAD( rc = buildNewRow( pRow->uiLevel + 1, + pDomNode, + &pNewRow))) + { + goto Exit; + } + + if (bRowHasElementData) + { + pNewRow->uiFlags |= F_DOMEDIT_FLAG_ELEMENT_DATA; + bRowHasElementData = FALSE; + } + else + { + // If the new row DOM node is an element node and it has embedded data, + // we need to set the bHasElementData flag. + + if (pDomNode->getNodeType() == ELEMENT_NODE) + { + FLMBOOL bDataIsLocal; + if (RC_BAD( rc = pDomNode->isDataLocalToNode( m_pDb, &bDataIsLocal))) + { + goto Exit; + } + if (bDataIsLocal) + { + pNewRow->bHasElementData = TRUE; + } + else + { + pNewRow->bHasElementData = FALSE; + } + } + } + + if (pChildNode) + { + pChildNode->Release(); + pChildNode = NULL; + } + pChildNode = pDomNode; + pDomNode = NULL; + + + // Link the row in + if (RC_BAD( rc = insertRow( pNewRow, pPrevRow))) + { + goto Exit; + } + + pPrevRow = pNewRow; + pNewRow = NULL; + + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseLastRow(); + } + + // Recursively expand... + if (!bOneLevel && (pPrevRow->bHasChildren || pPrevRow->bHasElementData)) + { + if (RC_BAD( rc = expandRow( pPrevRow, + bOneLevel, + &pPrevRow))) + { + goto Exit; + } + } + } + + if (!(pPrevRow == m_pScrLastRow && m_uiNumRows >= m_uiEditCanvasRows)) + { + // Now build the end row + if (RC_BAD( rc = buildNewRow( pRow->uiLevel, + pRow->pDomNode, + &pNewRow))) + { + goto Exit; + } + + // Now set the flags and link this row after the current row. + pNewRow->uiFlags |= F_DOMEDIT_FLAG_ENDTAG; + pNewRow->bExpanded = TRUE; + pNewRow->bHasElementData = pRow->bHasElementData; + + // Link the row in + if (RC_BAD(rc = insertRow( pNewRow, + pPrevRow))) + { + goto Exit; + } + pPrevRow = pNewRow; + pNewRow = NULL; + + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseLastRow(); + } + } + + // Mark that the current row has been expanded... + pRow->bExpanded = TRUE; + + if (pPrevRow->pDomNode) + { + pPrevRow->pDomNode->Release(); + pPrevRow->pDomNode = NULL; + } + + if (ppLastRow) + { + *ppLastRow = pPrevRow; + } + +Exit: + + if (pPrevRow->pDomNode) + { + pPrevRow->pDomNode->Release(); + pPrevRow->pDomNode = NULL; + } + + if (pNewRow) + { + if (pNewRow->puzValue) + { + f_free( &pNewRow->puzValue); + } + if (pNewRow->pDomNode) + { + pNewRow->pDomNode->Release(); + pNewRow->pDomNode = NULL; + } + f_free( &pNewRow); + } + + if (pDomNode) + { + pDomNode->Release(); + } + + if ( pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + if (pChildNode) + { + pChildNode->Release(); + } + + if (pDomNode) + { + pDomNode->Release(); + } + + return rc; +} + + + +/**************************************************************************** +Desc: Collapses a row to reveal it immediate children. +*****************************************************************************/ +RCODE F_DomEditor::collapseRow( + DME_ROW_INFO ** ppRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pReleaseRow = NULL; + DME_ROW_INFO * pRow = *ppRow; + FLMUINT uiLevel; + FLMBOOL bIgnoreAnchor; + + // Is this row already collapsed, or does it not have any children? + if ( !pRow->bExpanded || !pRow->bHasChildren && !pRow->bHasElementData) + { + goto Exit; + } + + uiLevel = pRow->uiLevel; + + // Are we looking at the first or last entry to be collapsed? + if (pRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG) + { + for (pTmpRow = pRow->pPrev; + pTmpRow && pTmpRow->uiLevel >= uiLevel; + ) + { + FLMUINT uiOldLevel = pTmpRow->uiLevel; + + pReleaseRow = pTmpRow; + pTmpRow = pTmpRow->pPrev; + releaseRow( &pReleaseRow); + m_uiNumRows--; + if (uiOldLevel == uiLevel) + { + break; + } + } + + pRow->bExpanded = FALSE; + pRow->uiFlags &= ~(FLMUINT)F_DOMEDIT_FLAG_ENDTAG; + + // If we deleted the first row, we must reset it. + if (pTmpRow == NULL) + { + m_pScrFirstRow = pRow; + } + + + } + else + { + + pRow->bExpanded = FALSE; + pTmpRow = pRow->pNext; + while (pTmpRow && pTmpRow->uiLevel >= uiLevel) + { + FLMUINT uiOldLevel = pTmpRow->uiLevel; + releaseRow( &pTmpRow); + m_uiNumRows--; + if ( uiOldLevel == uiLevel) + { + break; + } + } + + // If there is no last row, then we need to set it to the current row. + if (pTmpRow == NULL) + { + m_pScrLastRow = pRow; + } + + } + + + // If we just collapsed the anchored row, we don't want the anchor to + // interfere with getting the rest of the appropriate rows. If we leave the + // row anchor there, we will just expand again - oops. + if (m_pRowAnchor) + { + if (m_pRowAnchor->ui64NodeId == pRow->ui64NodeId) + { + f_free( &m_pRowAnchor); + } + } + + // Now, let's see if we can fill the screen with more rows. + bIgnoreAnchor = !m_pScrLastRow->bExpanded; + while (m_uiNumRows < m_uiEditCanvasRows) + { + + // Set the docList to sync with the last row displayed. + checkDocument(&m_pCurDoc, m_pScrLastRow); + if (RC_BAD( rc = getNextRow( m_pScrLastRow, &pTmpRow, TRUE, bIgnoreAnchor))) + { + goto Exit; + } + if (pTmpRow == NULL) + { + break; + } + m_pScrLastRow = pTmpRow; + bIgnoreAnchor = FALSE; + // Make sure our document list and last row are in sync. This prevents + // us from filling the screen with bogus rows. + } + + // Resync the docList with the cursor row. + checkDocument(&m_pCurDoc, pRow); + +Exit: + + return rc; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +/* +NODE_p F_DomEditor::findRow( + FLMUINT uiCollection, + FLMUINT uiDrn, + DME_ROW_INFO * pStartRow) +{ + FLMUINT uiSourceCont; + FLMUINT uiSourceDrn; + DME_ROW_INFO * pTmpRow; + DME_ROW_INFO * pCurRow = m_pRoot; + FLMBOOL bForward = TRUE; + + flmAssert( m_bSetupCalled == TRUE); + + if( pStartRow) + { + pCurRow = getRootRow( pStartRow); + } + else if( m_pCurRow) + { + pTmpRow = getRootRow( m_pCurRow); + if( RC_OK( GedGetRecSource( pTmpNd, NULL, + &uiSourceCont, &uiSourceDrn))) + { + pCurNd = pTmpNd; + if( uiSourceCont > uiCollection || + (uiSourceCont == uiCollection && uiSourceDrn > uiDrn)) + { + pCurNd = getPrevRecord( pCurNd); + bForward = FALSE; + } + + } + } + + while( pCurNd) + { + if( RC_OK( GedGetRecSource( pCurNd, NULL, + &uiSourceCont, &uiSourceDrn))) + { + if( uiSourceCont == uiCollection && uiSourceDrn == uiDrn) + { + break; + } + + if( bForward) + { + if( uiSourceCont > uiCollection || + (uiSourceCont == uiCollection && uiSourceDrn > uiDrn)) + { + pCurNd = NULL; + break; + } + } + else + { + if( uiSourceCont < uiCollection || + (uiSourceCont == uiCollection && uiSourceDrn < uiDrn)) + { + pCurNd = NULL; + break; + } + } + } + + if( bForward) + { + pCurNd = getNextRecord( pCurNd); + } + else + { + pCurNd = getPrevRecord( pCurNd); + } + + // Release CPU to prevent CPU hog + + f_yieldCPU(); + } + + return( pCurNd); +} +*/ + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::clearSelections( void) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + FLMUINT uiFlags = 0; + + flmAssert( m_bSetupCalled == TRUE); + + /* + Clear the "selected" flags + */ + + pTmpRow = m_pScrFirstRow; + while( pTmpRow) + { + (void)getControlFlags( pTmpRow, &uiFlags); + if( (uiFlags & F_DOMEDIT_FLAG_SELECTED)) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + if( RC_BAD( rc = setControlFlags( pTmpRow, uiFlags))) + { + goto Exit; + } + } + pTmpRow = pTmpRow->pNext; + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::setCurrentRow( + DME_ROW_INFO * pCurRow, + FLMUINT uiCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if (pCurRow) + { + + refreshRow( m_uiCurRow, m_pCurRow, TRUE); + } + + m_pCurRow = pCurRow; + if (uiCurRow <= m_uiEditCanvasRows) + { + m_uiCurRow = uiCurRow; + } + + if (pCurRow) + { + + refreshRow( m_uiCurRow, m_pCurRow, FALSE); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +DME_ROW_INFO * F_DomEditor::getCurrentRow( + FLMUINT * puiCurRow + ) +{ + + flmAssert( m_bSetupCalled == TRUE); + + *puiCurRow = m_uiCurRow; + + return( m_pCurRow); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::setFirstRow( + DME_ROW_INFO * pRow + ) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + m_pScrFirstRow = pRow; + + return( rc); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::getDisplayValue( + DME_ROW_INFO * pRow, + char * pszBuf, + FLMUINT uiBufSize) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + /* + This is a stupid check, but keep it for now. + */ + + if( uiBufSize <= 32 || !pRow || !pszBuf) + { + flmAssert( 0); + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + *pszBuf = '\0'; + + if (pRow->uiLength > uiBufSize) + { + f_sprintf( pszBuf, "%*s...", uiBufSize - 3, pRow->puzValue); + } + else + { + f_sprintf( pszBuf, "%*s", pRow->uiLength, pRow->puzValue); + } + + if( m_pEventHook) + { + if( RC_BAD( rc = m_pEventHook( this, F_DOMEDIT_EVENT_GETDISPVAL, + (void *)(pRow), m_EventData))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Can we edit this row? +*****************************************************************************/ +FLMBOOL F_DomEditor::canEditRow( + DME_ROW_INFO * pCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bCanEdit = TRUE; + eDomNodeType eType; + FLMBOOL bHasAttrs; + + flmAssert( m_bSetupCalled == TRUE); + + if( m_bReadOnly || !pCurRow) + { + bCanEdit = FALSE; + goto Exit; + } + + if (pCurRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG || + pCurRow->uiFlags & F_DOMEDIT_FLAG_COMMENT || + pCurRow->uiFlags & F_DOMEDIT_FLAG_READ_ONLY) + { + bCanEdit = FALSE; + goto Exit; + } + + if (pCurRow->ui64NodeId) + { + if (RC_BAD( rc = getDOMNode( this, pCurRow))) + { + bCanEdit = FALSE; + goto Exit; + } + + eType = pCurRow->pDomNode->getNodeType(); + + switch (eType) + { + // case DATA_NODE: + // case CDATA_SECTION_NODE: + // case COMMENT_NODE: + // case ATTRIBUTE_NODE: + case ELEMENT_NODE: + if (RC_BAD( rc = pCurRow->pDomNode->hasAttributes( m_pDb, &bHasAttrs))) + { + goto Exit; + } + if (!bHasAttrs) + { + bCanEdit = FALSE; + } + else + { + bCanEdit = TRUE; + } + break; + case PROCESSING_INSTRUCTION_NODE: + case DOCUMENT_NODE: + case INVALID_NODE: + { + bCanEdit = FALSE; + break; + } + default: + { + bCanEdit = TRUE; + break; + } + } + } + + +Exit: + + if (pCurRow->pDomNode) + { + pCurRow->pDomNode->Release(); + pCurRow->pDomNode = NULL; + } + + return( bCanEdit); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL F_DomEditor::canDeleteRow( + DME_ROW_INFO * pCurRow + ) +{ + FLMUINT uiFlags; + FLMBOOL bCanDelete = TRUE; + + flmAssert( m_bSetupCalled == TRUE); + + if( m_bReadOnly || !pCurRow) + { + bCanDelete = FALSE; + goto Exit; + } + + (void)getControlFlags( pCurRow, &uiFlags); + if( uiFlags & (F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NO_DELETE)) + { + bCanDelete = FALSE; + goto Exit; + } + +Exit: + + return( bCanDelete); +} + + + +/**************************************************************************** +Desc: Retrieve the list of Documents in the database - return the selected + document. +*****************************************************************************/ +RCODE F_DomEditor::retrieveDocumentList( + FLMUINT uiCollection, + FLMUINT64 * pui64NodeId, + FLMUINT * puiTermChar + ) +{ + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPriorRow = NULL; + FLMUINT uiFlags; + F_DomEditor * pDocumentList = NULL; + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDOMNode = NULL; + F_DOMNode * pSiblingNode = NULL; + FLMUINT uiDocumentCount; + FLMUNICODE * puzTitle = NULL; + FLMUINT64 ui64DocumentID; + FLMBOOL bGotFirstDoc; + + + flmAssert( m_bSetupCalled == TRUE); + + if( pui64NodeId) + { + *pui64NodeId = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pDocumentList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pDocumentList->Setup( m_pScreen))) + { + goto Exit; + } + + pDocumentList->setParent( this); + pDocumentList->setReadOnly( TRUE); + pDocumentList->setShutdown( m_pbShutdown); + pDocumentList->setTitle( "Document List - Select One"); + pDocumentList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pDocumentList->setSource( m_pDb, uiCollection); + + if( m_pDb == NULL) + { + goto Exit; + } + + + // Get the document list. + uiDocumentCount = 0; + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NODOM); + + bGotFirstDoc = FALSE; + while (uiDocumentCount < m_uiEditCanvasRows) + { + if (!bGotFirstDoc) + { + if (RC_BAD( rc = m_pDb->getFirstDocument( + uiCollection, (IF_DOMNode **)&pDOMNode))) + { + goto Exit; + } + bGotFirstDoc = TRUE; + } + else + { + if (RC_BAD( rc = pDOMNode->getNextDocument( + m_pDb, (IF_DOMNode **)&pSiblingNode))) + { + if (rc != NE_XFLM_EOF_HIT && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + break; + } + (void)pDOMNode->Release(); + pDOMNode = pSiblingNode; + pSiblingNode = NULL; + } + + if( RC_BAD( rc = pDOMNode->getNodeId( m_pDb, &ui64DocumentID))) + { + goto Exit; + } + + if( RC_BAD( rc = makeNewRow( &pTmpRow, NULL, ui64DocumentID))) + { + goto Exit; + } + + pTmpRow->uiIndex = uiDocumentCount; + pTmpRow->ui64DocId = ui64DocumentID; + + pTmpRow->uiFlags = uiFlags; + + // Link the rows into the list of titles to display. + if (RC_BAD( rc = pDocumentList->insertRow( pTmpRow, pPriorRow))) + { + goto Exit; + } + pPriorRow = pTmpRow; + pTmpRow = NULL; + uiDocumentCount++; + } + if (pDOMNode) + { + pDOMNode->Release(); + pDOMNode = NULL; + } + + pDocumentList->setDocList( TRUE); + + // Set the rows... + pDocumentList->setCurrentAtTop(); + + if( RC_BAD( rc = pDocumentList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pDocumentList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pDocumentList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pDocumentList->setControlFlags( pTmpRow, uiFlags); + if( pui64NodeId) + { + *pui64NodeId = pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pDocumentList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pDocumentList->getLastKey(); + } + +Exit: + + // Cleanup ... + f_free( &puzTitle); + + if (pDocumentList) + { + pDocumentList->Release(); + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + if (pSiblingNode) + { + pSiblingNode->Release(); + } + if (pDOMNode) + { + pDOMNode->Release(); + } + + return rc; +} + +/**************************************************************************** +Desc: Retrieve the range of nodes from the database... +*****************************************************************************/ +RCODE F_DomEditor::retrieveNodeFromDb( + FLMUINT uiCollection, + FLMUINT64 ui64NodeId, + FLMUINT uiAttrNameId) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDomNode = NULL; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPriorRow = NULL; + + // Can we put this node on the screen? + if (m_uiNumRows < m_uiEditCanvasRows) + { + if (uiAttrNameId) + { + rc = m_pDb->getAttribute( uiCollection, ui64NodeId, uiAttrNameId, + (IF_DOMNode **)&pDomNode); + } + else + { + rc = m_pDb->getNode( uiCollection, ui64NodeId, &pDomNode); + } + + if( RC_BAD( rc)) + { + if( rc != NE_XFLM_NOT_FOUND && + rc != NE_XFLM_EOF_HIT && + rc != NE_XFLM_BOF_HIT && + rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + goto Exit; + } + } + + // Find the last row + pPriorRow = getScrLastRow(); + + // Now build the new rows + if (RC_BAD( rc = buildNewRow( -1, + pDomNode, + &pTmpRow))) + { + goto Exit; + } + + if (RC_BAD( rc = insertRow( pTmpRow, pPriorRow))) + { + goto Exit; + } + + // Make the new node current. + m_pCurRow = pTmpRow; + + if (RC_BAD( rc = expandRow( pTmpRow, FALSE, NULL))) + { + goto Exit; + } + + // We need to mark the starting row since the expansion is + // incomplete. + if (m_pRowAnchor) + { + f_free( &m_pRowAnchor); + } + + if( RC_BAD( rc = f_alloc( sizeof( DME_ROW_ANCHOR), &m_pRowAnchor))) + { + goto Exit; + } + f_memset( m_pRowAnchor, 0, sizeof(DME_ROW_ANCHOR)); + m_pRowAnchor->ui64NodeId = pTmpRow->ui64NodeId; + m_pRowAnchor->uiAnchorLevel = pTmpRow->uiLevel; + m_pRowAnchor->bSingleLevel = FALSE; + } + +Exit: + + if( pDomNode) + { + pDomNode->Release(); + } + + return( rc); +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::displayAttributes( + DME_ROW_INFO * pRow + ) +{ + RCODE rc = NE_XFLM_OK; + F_DomEditor * pAttrList = NULL; + F_DOMNode * pDOMNode = NULL; + F_DOMNode * pAttrNode = NULL; + FLMUINT uiAttributeCount; + FLMBOOL bGotFirstAttr; + FLMUINT uiFlags; + FLMUINT64 ui64DocumentID; + FLMUINT uiAttrNameId; + DME_ROW_INFO * pTmpRow=NULL; + DME_ROW_INFO * pPriorRow=NULL; + + flmAssert( m_bSetupCalled == TRUE); + if (!pRow) + { + displayMessage( + "Node DOM Nodes to display attributes for", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + if ( pRow->eType != ELEMENT_NODE) + { + displayMessage( + "DOM Node is not an ELEMENT_NODE", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + if (!pRow->bHasAttributes) + { + displayMessage( + "DOM Node does not have attributes", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + + // Make sure we have a name table.... + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pAttrList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pAttrList->Setup( m_pScreen))) + { + goto Exit; + } + + pAttrList->setParent( this); + pAttrList->setReadOnly( FALSE); // Allow editing these attributes. + pAttrList->setShutdown( m_pbShutdown); + pAttrList->setTitle( "Element Attribute List"); + pAttrList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pAttrList->setSource( m_pDb, m_uiCollection); + + if( m_pDb == NULL) + { + goto Exit; + } + + // Get the DOMnode of the current element. + if (RC_BAD( rc = getDomNode( pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pDOMNode))) + { + goto Exit; + } + + + // Get the attribute list. + uiAttributeCount = 0; + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_NOPARENT | F_DOMEDIT_FLAG_NOCHILD); + + bGotFirstAttr = FALSE; + while (uiAttributeCount < m_uiEditCanvasRows) + { + if (!bGotFirstAttr) + { + if (RC_BAD( rc = pDOMNode->getFirstAttribute( m_pDb, + (IF_DOMNode **)&pAttrNode))) + { + goto Exit; + } + bGotFirstAttr = TRUE; + (void)pDOMNode->Release(); + pDOMNode = NULL; + } + else + { + if (RC_BAD( rc = pAttrNode->getNextSibling( + m_pDb, (IF_DOMNode **)&pAttrNode))) + { + if (rc != NE_XFLM_EOF_HIT && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + rc = NE_XFLM_OK; + break; + } + } + + if( RC_BAD( rc = pAttrNode->getParentId( m_pDb, &ui64DocumentID))) + { + goto Exit; + } + if (RC_BAD( rc = pAttrNode->getNameId( m_pDb, &uiAttrNameId))) + { + goto Exit; + } + + if (RC_BAD( rc = makeNewRow( &pTmpRow, NULL, ui64DocumentID))) + { + goto Exit; + } + + pTmpRow->eType = ATTRIBUTE_NODE; + pTmpRow->uiNameId = uiAttrNameId; + pTmpRow->uiIndex = uiAttributeCount; + pTmpRow->ui64DocId = ui64DocumentID; + + pTmpRow->uiFlags = uiFlags; + + // Link the rows into the list of titles to display. + if (RC_BAD( rc = pAttrList->insertRow( pTmpRow, pPriorRow))) + { + goto Exit; + } + pPriorRow = pTmpRow; + pTmpRow = NULL; + uiAttributeCount++; + } + if (pDOMNode) + { + pDOMNode->Release(); + pDOMNode = NULL; + } + + pAttrList->setDocList( FALSE); + + + // Set the rows... + pAttrList->setCurrentAtTop(); + + if( RC_BAD( rc = pAttrList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + +Exit: + + + // Cleanup ... + if (pAttrList) + { + pAttrList->Release(); + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + + if (pAttrNode) + { + pAttrNode->Release(); + } + + if (pDOMNode) + { + pDOMNode->Release(); + } + + return rc; +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC void domGetOutputFileName( + FTX_WINDOW * pWindow, + char * pszOutputFileName, + FLMUINT uiOutputFileNameBufSize) +{ + FLMUINT uiChar; + + *pszOutputFileName = 0; + FTXWinPrintf( pWindow, "Enter Output File Name: "); + if (FTXLineEdit( pWindow, pszOutputFileName, uiOutputFileNameBufSize - 1, + uiOutputFileNameBufSize - 1, NULL, &uiChar) != FTXRC_SUCCESS) + { + *pszOutputFileName = 0; + } + FTXWinPrintf( pWindow, "\n"); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::displayNodeInfo( + DME_ROW_INFO * pRow + ) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWindow = NULL; + FLMUINT uiChar; + FLMUINT uiTermChar; + char szOutputFile [200]; + + if (RC_BAD( rc = createStatusWindow( + "Node Information", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pWindow))) + { + goto Exit; + } + FTXWinOpen( pWindow); + + for (;;) + { + if (m_uiCollection == XFLM_DATA_COLLECTION) + { + FTXWinPrintf( pWindow, "\nInfo Type (N,ENTER=Node Only, S=Subtree, D=directory View, ESC=Cancel): "); + } + else + { + FTXWinPrintf( pWindow, "\nInfo Type (N,ENTER=Node Only, S=Subtree, ESC=Cancel): "); + } + FTXWinInputChar( pWindow, &uiChar); + FTXWinSetCursorPos( pWindow, 0, 0); + FTXWinClear( pWindow); + + switch (uiChar) + { + case 'D': + case 'd': + if (m_uiCollection == XFLM_DATA_COLLECTION) + { + domGetOutputFileName( pWindow, szOutputFile, sizeof( szOutputFile)); + domDisplayEntryInfo( pWindow, szOutputFile, m_pDb, pRow->ui64NodeId, TRUE); + } + else + { + FTXDisplayMessage( m_pScreen, m_bMonochrome ? WPS_LIGHTGRAY : WPS_RED, + m_bMonochrome ? WPS_BLACK : WPS_WHITE, + "Invalid option", NULL, &uiTermChar); + uiChar = 0; + } + break; + case 'S': + case 's': + domGetOutputFileName( pWindow, szOutputFile, sizeof( szOutputFile)); + domDisplayNodeInfo( pWindow, szOutputFile, m_pDb, m_uiCollection, pRow->ui64NodeId, TRUE, TRUE); + break; + case 'N': + case 'n': + case WPK_ENTER: + domGetOutputFileName( pWindow, szOutputFile, sizeof( szOutputFile)); + domDisplayNodeInfo( pWindow, szOutputFile, m_pDb, m_uiCollection, pRow->ui64NodeId, FALSE, TRUE); + break; + case WPK_ESC: + break; + default: + FTXDisplayMessage( m_pScreen, m_bMonochrome ? WPS_LIGHTGRAY : WPS_RED, + m_bMonochrome ? WPS_BLACK : WPS_WHITE, + "Invalid option", NULL, &uiTermChar); + uiChar = 0; + break; + } + if (uiChar) + { + break; + } + } + +Exit: + + if (pWindow) + { + FTXWinFree( &pWindow); + } + + return rc; +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_DomEditor::exportNode( + DME_ROW_INFO * pRow + ) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWindow = NULL; + IF_DOMNode * pNode = NULL; + FLMUINT uiChar; + FLMUINT uiTermChar; + char szFileName [80]; + eExportFormatType eFormat = XFLM_EXPORT_INDENT; + IF_OStream * pFileOStream = NULL; + F_DbSystem dbSystem; + + if (RC_BAD( rc = createStatusWindow( + "Export Node Subtree", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pWindow))) + { + goto Exit; + } + FTXWinOpen( pWindow); + + szFileName [0] = 0; + FTXWinClear( pWindow); + for (;;) + { + FTXWinSetCursorPos( pWindow, 2, 1); + FTXWinClearLine( pWindow, 2, 1); + FTXWinPrintf( pWindow, "Enter Export File Name: "); + if ((rc = FTXLineEdit( pWindow, szFileName, sizeof( szFileName) - 1, + sizeof( szFileName) - 1, + NULL, &uiChar)) != FTXRC_SUCCESS) + { + goto Exit; + } + if (!szFileName [0]) + { + goto Exit; + } + if (RC_BAD( rc = dbSystem.openFileOStream( szFileName, TRUE, &pFileOStream))) + { + displayMessage( "Error creating export file", rc, NULL, WPS_RED, WPS_WHITE); + } + else + { + break; + } + } + + for (;;) + { + FTXWinSetCursorPos( pWindow, 2, 2); + FTXWinClearLine( pWindow, 2, 2); + FTXWinPrintf( pWindow, + "Format (I,ENTER=Indent, N=Newline, D=Indent Data, X=None, ESC=Cancel): "); + FTXWinInputChar( pWindow, &uiChar); + + switch (uiChar) + { + case 'I': + case 'i': + case WPK_ENTER: + eFormat = XFLM_EXPORT_INDENT; + FTXWinPrintf( pWindow, "I"); + break; + case 'N': + case 'n': + eFormat = XFLM_EXPORT_NEW_LINE; + FTXWinPrintf( pWindow, "N"); + break; + case 'D': + case 'd': + eFormat = XFLM_EXPORT_INDENT_DATA; + FTXWinPrintf( pWindow, "D"); + break; + case 'X': + case 'x': + eFormat = XFLM_EXPORT_NO_FORMAT; + FTXWinPrintf( pWindow, "X"); + break; + case WPK_ESC: + goto Exit; + default: + FTXDisplayMessage( m_pScreen, m_bMonochrome ? WPS_LIGHTGRAY : WPS_RED, + m_bMonochrome ? WPS_BLACK : WPS_WHITE, + "Invalid option", NULL, &uiTermChar); + uiChar = 0; + break; + } + if (uiChar) + { + break; + } + } + + if (pRow->eType == ATTRIBUTE_NODE) + { + rc = m_pDb->getAttribute( m_uiCollection, pRow->ui64NodeId, + pRow->uiNameId, + (IF_DOMNode **)&pNode); + } + else + { + rc = m_pDb->getNode( m_uiCollection, pRow->ui64NodeId, &pNode); + } + if (RC_BAD( rc)) + { + displayMessage( "Error getting node", rc, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + if( RC_BAD( rc = m_pDb->exportXML( pNode, pFileOStream, eFormat))) + { + displayMessage( "Error exporting data", rc, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + FTXWinSetCursorPos( pWindow, 2, 3); + FTXWinClearLine( pWindow, 2, 3); + FTXWinPrintf( pWindow, "Export Done, press any character to exit: "); + FTXWinInputChar( pWindow, &uiChar); + +Exit: + + if (pFileOStream) + { + pFileOStream->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + if (pWindow) + { + FTXWinFree( &pWindow); + } + + return rc; +} + + +/**************************************************************************** +Desc: Retrieve information about the document and add it to the document linked + list. +*****************************************************************************/ +RCODE F_DomEditor::addDocumentToList( + FLMUINT uiCollection, + FLMUINT64 ui64DocumentId + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDomNode = NULL; + DME_ROW_INFO * pTmpRow; + DME_ROW_INFO * pLastRow = m_pDocList; + + if( RC_BAD( rc = m_pDb->getNode( uiCollection, + ui64DocumentId, + &pDomNode))) + { + if( rc != NE_XFLM_NOT_FOUND && rc != NE_XFLM_EOF_HIT && + rc != NE_XFLM_BOF_HIT && rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + goto Exit; + } + else + { + rc = NE_XFLM_OK; + } + } + + + // Now build the new row + if (RC_BAD( buildNewRow( -1, + pDomNode, + &pTmpRow))) + { + goto Exit; + } + + if (m_pDocList == NULL) + { + m_pDocList = pTmpRow; + m_pCurDoc = pTmpRow; + } + else + { + for (;;) + { + if (pLastRow->pNext) + { + pLastRow = pLastRow->pNext; + } + else + { + pLastRow->pNext = pTmpRow; + pTmpRow->pPrev = pLastRow; + break; + } + } + if (m_pCurRow->ui64DocId != m_pCurDoc->ui64DocId) + { + m_pCurDoc = pTmpRow; + } + } + + +Exit: + + if (pDomNode) + { + pDomNode->Release(); + } + + return rc; +} + +/**************************************************************************** +Desc: Allows the user to interactively select a Collection +*****************************************************************************/ +RCODE F_DomEditor::selectCollection( + FLMUINT * puiCollection, + FLMUINT * puiTermChar) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiFlags; + FLMUNICODE uzItemName[ 128]; + FLMUINT uiId; + FLMUINT uiNextPos; + F_DomEditor * pCollectionList = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiCollection) + { + *puiCollection = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pCollectionList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pCollectionList->Setup( m_pScreen))) + { + goto Exit; + } + + pCollectionList->setParent( this); + pCollectionList->setReadOnly( TRUE); + pCollectionList->setShutdown( m_pbShutdown); + pCollectionList->setTitle( "Collections - Select One"); + pCollectionList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pCollectionList->setSource( m_pDb, XFLM_DICT_COLLECTION); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY); + + asciiToUnicode( "Default Data", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + pTmpRow->uiNameId = XFLM_DATA_COLLECTION; + + if (RC_BAD( rc = pCollectionList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "Dictionary Definitions", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + pTmpRow->uiNameId = XFLM_DICT_COLLECTION; + + if (RC_BAD( rc = pCollectionList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "Maintenance", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + pTmpRow->uiNameId = XFLM_MAINT_COLLECTION; + + if (RC_BAD( rc = pCollectionList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + uiNextPos = 0; + while( RC_OK( rc = m_pNameTable->getNextTagTypeAndNameOrder( + ELM_COLLECTION_TAG, + &uiNextPos, uzItemName, NULL, sizeof( uzItemName), &uiId))) + { + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + pTmpRow->uiNameId = uiId; + + if (RC_BAD( rc = pCollectionList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + } + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + + + // Set the start row. + pCollectionList->setCurrentAtTop(); + + + if( RC_BAD( rc = pCollectionList->interactiveEdit( + m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pCollectionList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pCollectionList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pCollectionList->setControlFlags( pTmpRow, uiFlags); + if( puiCollection) + { + *puiCollection = pTmpRow->uiNameId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pCollectionList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pCollectionList->getLastKey(); + } + +Exit: + + if( pCollectionList) + { + pCollectionList->Release(); + pCollectionList = NULL; + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + return( rc); +} + +/**************************************************************************** +Desc: Creates a new DME_ROW_INFO structure and stores the puzValue and Id. + NOTE: puzValue must be a null terminated string. +*****************************************************************************/ +FSTATIC RCODE makeNewRow( + DME_ROW_INFO ** ppTmpRow, + FLMUNICODE * puzValue, + FLMUINT64 ui64Id, + FLMBOOL bUseValue + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + FLMUINT uiLength; + + if( RC_BAD( rc = f_alloc( sizeof( DME_ROW_INFO), &pTmpRow))) + { + goto Exit; + } + + f_memset( pTmpRow, 0, sizeof( DME_ROW_INFO)); + + if (bUseValue) + { + uiLength = unicodeStrLen( puzValue); + + if (RC_BAD( rc = f_calloc( (uiLength*2)+2, &pTmpRow->puzValue))) + { + goto Exit; + } + + f_memcpy( pTmpRow->puzValue, puzValue, (uiLength*2)+2); + + pTmpRow->uiLength = uiLength*2; + } + + pTmpRow->ui64NodeId = ui64Id; + pTmpRow->bUseValue = bUseValue; + + *ppTmpRow = pTmpRow; + pTmpRow = NULL; + +Exit: + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + + return rc; +} + +/**************************************************************************** +Desc: Calculates the length of a Unicode string. +*****************************************************************************/ +FSTATIC FLMUINT unicodeStrLen( + FLMUNICODE * puzStr + ) +{ + FLMUINT uiLength; + FLMUNICODE * puzTmp; + + if (!puzStr) + { + return 0; + } + + for (puzTmp = puzStr, uiLength = 0; + *puzTmp != (FLMUNICODE)0; + uiLength++, puzTmp++); + + return uiLength; +} + + +/**************************************************************************** +Name: getNumber +Desc: Converts a text string to a number +*****************************************************************************/ +RCODE F_DomEditor::getNumber( + char * pszBuf, + FLMUINT64 * pui64Value, + FLMINT64 * pi64Value) +{ + char * pszTmp = NULL; + FLMUINT64 ui64Value = 0; + FLMUINT uiDigits = 0; + FLMUINT uiHexOffset = 0; + FLMBOOL bNeg = FALSE; + FLMBOOL bHex = FALSE; + RCODE rc = NE_XFLM_OK; + + if( pui64Value) + { + *pui64Value = 0; + } + + if( pi64Value) + { + *pi64Value = 0; + } + + if( f_strnicmp( pszBuf, "0x", 2) == 0) + { + uiHexOffset = 2; + bHex = TRUE; + } + else if( *pszBuf == 'x' || *pszBuf == 'X') + { + uiHexOffset = 1; + bHex = TRUE; + } + else + { + pszTmp = pszBuf; + while( *pszTmp) + { + if( (*pszTmp >= '0' && *pszTmp <= '9') || + (*pszTmp >= 'A' && *pszTmp <= 'F') || + (*pszTmp >= 'a' && *pszTmp <= 'f') || + (*pszTmp == '-')) + { + if( (*pszTmp >= 'A' && *pszTmp <= 'F') || + (*pszTmp >= 'a' && *pszTmp <= 'f')) + { + bHex = TRUE; + } + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + pszTmp++; + } + uiHexOffset = 0; + } + + if( bHex) + { + pszTmp = &(pszBuf[ uiHexOffset]); + uiDigits = f_strlen( pszTmp); + if( !uiDigits) + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + while( *pszTmp) + { + ui64Value <<= 4; + if( *pszTmp >= '0' && *pszTmp <= '9') + { + ui64Value |= *pszTmp - '0'; + } + else if( *pszTmp >= 'a' && *pszTmp <= 'f') + { + ui64Value |= (*pszTmp - 'a') + 10; + } + else if( *pszTmp >= 'A' && *pszTmp <= 'F') + { + ui64Value |= (*pszTmp - 'A') + 10; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + pszTmp++; + } + } + else if( (*pszBuf >= '0' && *pszBuf <= '9') || *pszBuf == '-') + { + pszTmp = pszBuf; + if( *pszTmp == '-') + { + bNeg = TRUE; + pszTmp++; + } + uiDigits = f_strlen( pszTmp); + + while( *pszTmp) + { + if( *pszTmp >= '0' && *pszTmp <= '9') + { + FLMUINT64 ui64NewVal = ui64Value; + + if( ui64NewVal > (~((FLMUINT64)0) / 10)) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64NewVal *= 10; + ui64NewVal += *pszTmp - '0'; + + if( ui64NewVal < ui64Value) + { + rc = RC_SET( NE_XFLM_CONV_NUM_OVERFLOW); + goto Exit; + } + + ui64Value = ui64NewVal; + } + else + { + rc = RC_SET( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + pszTmp++; + } + } + else + { + rc = RC_SET( NE_XFLM_CONV_BAD_DIGIT); + goto Exit; + } + + if( bNeg) + { + if( pi64Value) + { +#ifdef FLM_LINUX + if( ui64Value > 0x7FFFFFFFFFFFFFFFULL) +#else + if( ui64Value > 0x7FFFFFFFFFFFFFFF) +#endif + { + rc = RC_SET( NE_XFLM_CONV_NUM_UNDERFLOW); + goto Exit; + } + *pi64Value = -((FLMINT64)ui64Value); + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + else + { + if( pui64Value) + { + *pui64Value = ui64Value; + } + else + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Shows a help screen +*****************************************************************************/ +RCODE F_DomEditor::showHelp( + FLMUINT * puiKeyRV + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pPrevRow = NULL; + DME_ROW_INFO * pTmpRow = NULL; + FLMUINT uiFlags; + F_DomEditor * pHelpList = NULL; + FLMUNICODE uzItemName[ 128]; + + flmAssert( m_bSetupCalled == TRUE); + + + + if( (pHelpList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pHelpList->Setup( m_pScreen))) + { + goto Exit; + } + + pHelpList->setParent( this); + pHelpList->setReadOnly( TRUE); + pHelpList->setShutdown( m_pbShutdown); + pHelpList->setTitle( "HELP"); + pHelpList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pHelpList->setSource( m_pDb, m_uiCollection); + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY); + + asciiToUnicode( "Keyboard Commands", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "? Help (this screen)", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)'?', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "# Database statistics", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], '#', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "UP Position cursor to the previous field", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_UP, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "DOWN Position cursor to the next field", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_DOWN, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "PG UP Position cursor to the previous page", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_PGUP, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "PG DOWN Position cursor to the next page", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_PGDN, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "HOME Position cursor to the top of the buffer", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_HOME, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "END Position cursor to the bottom of the buffer", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_END, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "DELETE Delete the current node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_DELETE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "A Display DOM Node attributes", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'A', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "C Clear all entries from the buffer", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'C', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + + asciiToUnicode( "D Display Node Information", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'C', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + + asciiToUnicode( "F Find nodes in the database via an XPATH query", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'F', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "I Show index keys and references", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'I', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "L List documents to select", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'L', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "N add a New node to the database", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'N', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "R Retrieve a node from the database", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'R', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "S Refresh the current display window", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'S', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "V View the current node value", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'V', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "X Export Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'C', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + + asciiToUnicode( "RIGHT Expand context one level", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_RIGHT, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "LEFT Collapse context", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_LEFT, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "PLUS Expand to full context", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_PLUS, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "MINUS collapse context", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_MINUS, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "ENTER Edit the current node's value", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_ENTER, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "F8 Index manager", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_F8, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "F9 Memory manager", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_F9, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "F10 Toggle display colors on/off", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], WPK_F10, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "ESC, Q, Exit", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], 'Q', TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pHelpList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + // Call the applications help hook to extend the help screen. + +// if( m_pHelpHook) +// { +// if( RC_BAD( rc = m_pHelpHook( this, m_pHelpHook))) +// { +// goto Exit; +// } +// } + + + pHelpList->setCurrentAtTop(); + + // Show the help screen + + if( RC_BAD( rc = pHelpList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, + m_uiLRY, TRUE, 0))) + { + goto Exit; + } + + if( pHelpList->getLastKey() != WPK_ENTER) + { + goto Exit; + } + + + if ( puiKeyRV) + { + *puiKeyRV = 0; + } + + // Find the selected item + if( (pTmpRow = pHelpList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pHelpList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + if( puiKeyRV) + { + *puiKeyRV = (FLMUINT)pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pHelpList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + +Exit: + + if( pHelpList) + { + pHelpList->Release(); + } + + return( rc); +} + + +/**************************************************************************** +Name: createStatusWindow +Desc: Creates a window for displaying an operation's status +*****************************************************************************/ +RCODE F_DomEditor::createStatusWindow( + char * pszTitle, + FLMUINT uiBack, + FLMUINT uiFore, + FLMUINT * puiCols, + FLMUINT * puiRows, + FTX_WINDOW ** ppWindow) +{ + FLMUINT uiNumRows; + FLMUINT uiNumCols; + FLMUINT uiNumWinRows = 0; + FLMUINT uiNumWinCols = 0; + FTX_WINDOW * pWindow = NULL; + RCODE rc = NE_XFLM_OK; + + *ppWindow = NULL; + + /* + Create a status window + */ + + if( FTXScreenGetSize( m_pScreen, + &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( puiCols) + { + uiNumWinCols = *puiCols; + } + + if( puiRows) + { + uiNumWinRows = *puiRows; + } + + if( uiNumWinCols <= 2 || uiNumWinRows < 3) + { + uiNumWinCols = uiNumCols - 2; + uiNumWinRows = uiNumRows - 2; + } + + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( puiCols) + { + *puiCols = uiNumWinCols; + } + + if( puiRows) + { + *puiRows = uiNumWinRows; + } + + if( FTXWinMove( pWindow, (FLMUINT)((uiNumCols - uiNumWinCols) / 2), + (FLMUINT)((uiNumRows - uiNumWinRows) / 2)) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetScroll( pWindow, TRUE) != FTXRC_SUCCESS) + { + goto Exit; + } + + if( FTXWinSetLineWrap( pWindow, TRUE) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetCursorType( pWindow, WPS_CURSOR_INVISIBLE); + + if( m_bMonochrome) + { + uiBack = WPS_LIGHTGRAY; + uiFore = WPS_BLACK; + if( FTXWinSetBackFore( pWindow, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + else + { + if( FTXWinSetBackFore( pWindow, + uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( pszTitle) + { + if( FTXWinSetTitle( pWindow, pszTitle, uiBack, uiFore) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + } + + *ppWindow = pWindow; + +Exit: + + return( rc); +} + + + +/**************************************************************************** +Name: ViewOnlyKeyHook +*****************************************************************************/ +FSTATIC RCODE f_ViewOnlyKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * //pvKeyData + ) +{ + RCODE rc = NE_XFLM_OK; + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_ESCAPE: + { + *puiKeyOut = uiKeyIn; + break; + } + + // Special case + case WPK_DELETE: + { + *puiKeyOut = 0; + if (pCurRow->uiFlags & F_DOMEDIT_FLAG_COMMENT) + { + if (RC_BAD( rc = pDomEditor->viewOnlyDeleteIxKey())) + { + goto Exit; + } + } + break; + } + + default: + { + *puiKeyOut = 0; + break; + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: f_KeyEditorKeyHook +*****************************************************************************/ +FSTATIC RCODE f_KeyEditorKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * //pvKeyData + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + FLMUINT uiCurRow; + + switch( uiKeyIn) + { + case WPK_UP: + { + (void)pDomEditor->getCurrentRow( &uiCurRow); + if (uiCurRow) + { + + if (RC_BAD( rc = pDomEditor->getPrevRow( pCurRow, + &pTmpRow, + FALSE))) + { + goto Exit; + } + + if (pTmpRow != NULL) + { + uiCurRow--; + if ( RC_BAD( rc = pDomEditor->setCurrentRow( pTmpRow, uiCurRow))) + { + goto Exit; + } + } + } + *puiKeyOut = 0; + break; + } + case WPK_DOWN: + { + (void)pDomEditor->getCurrentRow( &uiCurRow); + + if (RC_BAD( rc = pDomEditor->getNextRow( + pCurRow, &pTmpRow, FALSE))) + { + goto Exit; + } + + if (pTmpRow != NULL) + { + uiCurRow++; + if ( RC_BAD( rc = pDomEditor->setCurrentRow( pTmpRow, uiCurRow))) + { + goto Exit; + } + } + *puiKeyOut = 0; + break; + } + case WPK_ENTER: + { + if( !pDomEditor->canEditRow( pCurRow)) + { + pDomEditor->displayMessage( "The row cannot be edited", + RC_SET( NE_XFLM_ILLEGAL_OP), NULL, WPS_RED, WPS_WHITE); + } + else if( RC_BAD( rc = pDomEditor->editIndexRow( pCurRow))) + { + pDomEditor->displayMessage( "The field could not be edited", rc, + NULL, WPS_RED, WPS_WHITE); + } + *puiKeyOut = 0; + break; + } + case WPK_ESCAPE: /* Quit key editor */ +// case WPK_ALT_Q: /* Done editing keys */ + case WPK_ALT_Z: /* Done, but don't quit */ + { + *puiKeyOut = uiKeyIn; + break; + } + + case 'n': + case 'N': + { + // Option to edit the node id. + if( !pDomEditor->canEditRow( pCurRow)) + { + pDomEditor->displayMessage( "The row cannot be edited", + RC_SET( NE_XFLM_ILLEGAL_OP), NULL, WPS_RED, WPS_WHITE); + } + else if( RC_BAD( rc = pDomEditor->editIndexNode( pCurRow))) + { + pDomEditor->displayMessage( "The field could not be edited", rc, + NULL, WPS_RED, WPS_WHITE); + } + *puiKeyOut = 0; + break; + } + default: + { + *puiKeyOut = 0; + break; + } + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Name: SelectionOnlyKeyHook +*****************************************************************************/ +RCODE F_DomEditorSelectionKeyHook( + F_DomEditor *, //pDomEditor, + DME_ROW_INFO *, //pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * //pvKeyData + ) +{ + RCODE rc = NE_XFLM_OK; + + switch( uiKeyIn) + { + case WPK_HOME: + case WPK_END: + case WPK_UP: + case WPK_DOWN: + case WPK_PGUP: + case WPK_PGDN: + case WPK_ESCAPE: + case WPK_ENTER: + case WPK_DELETE: + { + *puiKeyOut = uiKeyIn; + break; + } + + default: + { + *puiKeyOut = 0; + break; + } + } + + return( rc); +} + +/*************************************************************************** +Name: +Desc: +****************************************************************************/ +RCODE F_DomEditor::globalConfig( + FLMUINT uiOption) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pDb == NULL) + { + rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + + switch( uiOption) + { + case F_DOMEDIT_CONFIG_STATS_START: + { + //eConfigOp = FLM_START_STATS; + break; + } + + case F_DOMEDIT_CONFIG_STATS_STOP: + { + //eConfigOp = FLM_STOP_STATS; + break; + } + + case F_DOMEDIT_CONFIG_STATS_RESET: + { + //eConfigOp = FLM_RESET_STATS; + break; + } + + default: + { + rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + } + +// if( RC_BAD( rc = FlmConfig( eConfigOp, 0, 0))) +// { +// goto Exit; +// } + +Exit: + + return( rc); +} + +/*============================================================================ +Name: asciiUCMixToUC +Desc: Accepts a buffer with regular ascii, and 4 ascii chars representing + unicode and converts all the chars to unicode. UC_MARKER defines the + char which marks the beginning of a unicode sequence. +============================================================================*/ +RCODE F_DomEditor::asciiUCMixToUC( + char * pszAscii, + FLMUNICODE * puzUnicode, + FLMUINT uiMaxUniChars) +{ + char * pszTmp; + char * pszTerm; + char szNumBuf[ 32]; + FLMUINT uiUniCount = 0; + FLMUINT64 ui64Value; + RCODE rc = NE_XFLM_OK; + + flmAssert( uiMaxUniChars > 0); + uiMaxUniChars--; // Leave space for the terminator + + while( uiUniCount < uiMaxUniChars && *pszAscii) + { + if( pszAscii[ 0] == '~' && pszAscii[ 1] == '[') + { + pszAscii += 2; + if( (pszTerm = f_strchr( pszAscii, ']')) == NULL) + { + rc = RC_SET( NE_XFLM_CONV_ILLEGAL); + goto Exit; + } + + while( *pszAscii && *pszAscii != ']') + { + pszTmp = f_strchr( pszAscii, ' '); + if( !pszTmp || pszTmp > pszTerm) + { + pszTmp = pszTerm; + } + + f_memcpy( szNumBuf, pszAscii, pszTmp - pszAscii); + szNumBuf[ pszTmp - pszAscii] = 0; + + if( RC_BAD( rc = getNumber( szNumBuf, &ui64Value, NULL))) + { + goto Exit; + } + + puzUnicode[ uiUniCount++] = (FLMUNICODE)ui64Value; + pszAscii += (pszTmp - pszAscii); + while( *pszAscii == ' ') + { + pszAscii++; + } + } + + if( *pszAscii == ']') + { + pszAscii++; + } + } + else + { + puzUnicode[ uiUniCount++] = (FLMUNICODE)(*pszAscii); + pszAscii++; + } + } + + puzUnicode[ uiUniCount] = 0; + +Exit: + + return rc; +} + + +/*============================================================================ +Name: UCToAsciiUCMix +============================================================================*/ +RCODE F_DomEditor::UCToAsciiUCMix( + FLMUNICODE * puzUnicode, + char * pszAscii, + FLMUINT uiMaxAsciiChars) +{ + char szTmpBuf[ 32]; + FLMUINT uiAsciiCount = 0; + FLMBOOL bEscaped = FALSE; + RCODE rc = NE_XFLM_OK; + + flmAssert( uiMaxAsciiChars > 0); + uiMaxAsciiChars--; // Leave space for the terminator + + while( uiAsciiCount < uiMaxAsciiChars && *puzUnicode) + { + if( *puzUnicode >= 0x0020 && *puzUnicode <= 0x007E) + { + if( bEscaped) + { + pszAscii[ uiAsciiCount++] = ']'; + bEscaped = FALSE; + } + + if( uiAsciiCount == uiMaxAsciiChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pszAscii[ uiAsciiCount++] = (char)*puzUnicode; + } + else + { + if( !bEscaped) + { + if( (uiAsciiCount + 2) >= uiMaxAsciiChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pszAscii[ uiAsciiCount++] = '~'; + pszAscii[ uiAsciiCount++] = '['; + bEscaped = TRUE; + } + else + { + pszAscii[ uiAsciiCount++] = ' '; + } + + pszAscii[ uiAsciiCount] = '\0'; + f_sprintf( szTmpBuf, "0x%04X", (unsigned)*puzUnicode); + + if( (uiAsciiCount +f_strlen( szTmpBuf)) >= uiMaxAsciiChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + f_strcat( &(pszAscii[ uiAsciiCount]), szTmpBuf); + uiAsciiCount += f_strlen( szTmpBuf); + } + + puzUnicode++; + } + + if( bEscaped) + { + if( uiAsciiCount == uiMaxAsciiChars) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + + pszAscii[ uiAsciiCount++] = ']'; + bEscaped = FALSE; + } + + pszAscii[ uiAsciiCount] = '\0'; + +Exit: + + return rc; +} + + +/*************************************************************************** +Name: domEditVerifyRun +Desc: Makes sure the utility is still allowed to run +*****************************************************************************/ +RCODE domEditVerifyRun( void) +{ + F_TMSTAMP curDate; + FLMUINT uiExpireYear; + FLMUINT uiExpireMonth; + FLMUINT uiExpireDay; + RCODE rc = NE_XFLM_OK; + char * pszDate = __DATE__; + + // Get the compilation date of this module. + // If cannot get it, return NE_XFLM_OK. + + if (!domeditStrToDate( pszDate, &uiExpireYear, + &uiExpireMonth, &uiExpireDay)) + { + goto Exit; + } + + // Add four months to it. + + if (uiExpireMonth <= 8) + { + uiExpireMonth += 4; + } + else + { + uiExpireMonth -= 8; + uiExpireYear++; + } + + // Adjust the day if necessary - we don't have to be too precise here + // because we don't really care if we give them a day or two extra + // for the expiration date. + + switch (uiExpireMonth) + { + case 2: + if (uiExpireDay > 28) + { + uiExpireDay = 28; + } + break; + case 4: + case 6: + case 9: + case 11: + if (uiExpireDay > 30) + { + uiExpireDay = 30; + } + break; + default: + break; + } + + f_timeGetTimeStamp( &curDate); + curDate.month++; + if(((FLMUINT)curDate.year > uiExpireYear) || + ((FLMUINT)curDate.year == uiExpireYear && + (FLMUINT)curDate.month > uiExpireMonth) || + ((FLMUINT)curDate.year == uiExpireYear && + (FLMUINT)curDate.month == uiExpireMonth && + (FLMUINT)curDate.day > uiExpireDay)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + +Exit: + + return( rc); +} + + + +/**************************************************************************** +Desc: This routine converts the passed in string to a year, month, and day. +****************************************************************************/ +FSTATIC FLMBOOL domeditStrToDate( + char * s, + FLMUINT * puiYear, + FLMUINT * puiMonth, + FLMUINT * puiDay + ) +{ + char szToken [80]; + FLMUINT uiLoop; + FLMBOOL bHaveNum = FALSE; + FLMBOOL bHaveDay = FALSE; + FLMBOOL bHaveMonth = FALSE; + FLMUINT uiNum1; + FLMUINT uiSaveNum = 0; + FLMBOOL bSlashFormat = FALSE; + FLMBOOL bDashFormat = FALSE; + FLMBOOL bNoFormat = FALSE; + + for (;;) + { + + // Get the next token from the string + + s = domeditSkipChars( s, " \t\n\r"); + uiLoop = 0; + while (((*s >= 'a') && (*s <= 'z')) || + ((*s >= 'A') && (*s <= 'Z')) || + ((*s >= '0') && (*s <= '9'))) + { + if (uiLoop < sizeof( szToken) - 1) + { + szToken [uiLoop++] = *s; + } + s++; + } + szToken [uiLoop] = 0; + + // See if it is a number + + if ((uiLoop) && + (domeditIsNum( szToken, &uiNum1))) + { + if ((bHaveMonth) && (bHaveDay)) + { + if (uiNum1 > 65535) + { + goto Year_Error; + } + *puiYear = uiNum1; + + // Make sure we have a valid day of the month. + // NOTE: The test for Feb. 29 will allow it in + // some cases where it shouldn't have, but at + // least it will never disallow it when it should + // have allowed it. + + switch (*puiMonth) + { + case 2: + if ((*puiDay > 29) || + ((*puiDay == 29) && + (*puiYear % 4 != 0))) + { + goto Day_Error; + } + break; + case 4: + case 6: + case 9: + case 11: + if (*puiDay > 30) + { + goto Day_Error; + } + break; + default: + break; + } + return( TRUE); + } + else if (bHaveMonth) + { + if ((uiNum1 < 1) || (uiNum1 > 31)) + { + goto Day_Error; + } + bHaveDay = TRUE; + *puiDay = uiNum1; + } + else if (bHaveDay) + { + if ((uiNum1 < 1) || (uiNum1 > 12)) + { + goto Month_Error; + } + bHaveMonth = TRUE; + *puiMonth = uiNum1; + } + else if (bHaveNum) + { + if ((uiNum1 >= 1) && (uiNum1 <= 12)) + { + flmAssert( 0); + return( FALSE); + } + else if ((uiNum1 < 1) || (uiNum1 > 31)) + { + goto MonthDay_Error; + } + else + { + bHaveDay = TRUE; + bHaveMonth = TRUE; + *puiMonth = uiSaveNum; + *puiDay = uiNum1; + } + } + else if ((uiNum1 < 1) || (uiNum1 > 31)) + { + goto MonthDay_Error; + } + else + { + if (uiNum1 <= 12) + { + bHaveNum = TRUE; + uiSaveNum = uiNum1; + } + else + { + bHaveDay = TRUE; + *puiDay = uiNum1; + } + } + } + else if (bHaveMonth) + { + if (bHaveDay) + { + goto Year_Error; + } + else + { + goto Day_Error; + } + } + else + { + + // See if it is a month string + + for (uiLoop = 0; uiLoop < 12; uiLoop++) + { + if (f_stricmp( pszDomeditMonths [uiLoop], szToken) == 0) + { + bHaveMonth = TRUE; + *puiMonth = (uiLoop+1); + break; + } + } + + if (uiLoop == 12) + { + for (uiLoop = 0; uiLoop < 12; uiLoop++) + { + if (f_stricmp( pszDomeditFullMonthNames [uiLoop], + szToken) == 0) + { + bHaveMonth = TRUE; + *puiMonth = (uiLoop+1); + break; + } + } + if (uiLoop == 12) + { + goto Month_Error; + } + } + } + s = domeditSkipChars( s, " \t\n\r"); + if (bNoFormat) + { + if ((bHaveMonth) && (bHaveDay) && (*s == ',')) + { + s++; + } + } + else if (bSlashFormat) + { + if (*s != '/') + { + goto Invalid_Format; + } + s++; + } + else if (bDashFormat) + { + if (*s != '-') + { + goto Invalid_Format; + } + s++; + } + else if (*s == '/') + { + bSlashFormat = TRUE; + s++; + } + else if (*s == '-') + { + bDashFormat = TRUE; + s++; + } + else + { + bNoFormat = TRUE; + } + } +Invalid_Format: +Year_Error: +Month_Error: +Day_Error: +MonthDay_Error: + flmAssert( 0); + return( FALSE); +} + + + +/**************************************************************************** +Desc: This routine determines if the passed in token is a number. If so, + the number is returned. +****************************************************************************/ +FSTATIC FLMBOOL domeditIsNum( + char * pszToken, // Token being checked + FLMUINT * puiNum // Returned number - if token is a number + ) +{ + FLMBOOL bIsNum = FALSE; + char * pszBuffer; + FLMUINT uiLen; + + // Make sure all characters are between 0 and 9 + + pszBuffer = pszToken; + + // See if it is a HEX number. + + if ((*pszBuffer == '0') && (*(pszBuffer + 1) == 'x')) + { + pszBuffer += 2; + uiLen = 0; + while ((*pszBuffer) && + (((*pszBuffer >= '0') && (*pszBuffer <= '9')) || + ((*pszBuffer >= 'a') && (*pszBuffer <= 'f')) || + ((*pszBuffer >= 'A') && (*pszBuffer <= 'F')))) + { + uiLen++; + pszBuffer++; + } + if ((!uiLen) || (uiLen > 8) || (*pszBuffer)) + { + goto Exit; + } + + // Convert the pszToken to a number + + *puiNum = 0; + pszBuffer = pszToken + 2; + while (*pszBuffer) + { + if ((*pszBuffer >= '0') && (*pszBuffer <= '9')) + { + *puiNum = (*puiNum << 4) + *pszBuffer - '0'; + } + else if ((*pszBuffer >= 'a') && (*pszBuffer <= 'f')) + { + *puiNum = (*puiNum << 4) + *pszBuffer - 'a' + 10; + } + else + { + *puiNum = (*puiNum << 4) + *pszBuffer - 'A' + 10; + } + pszBuffer++; + } + } + else + { + FLMUINT uiLeadingZeroes = 0; + + // Skip leading zeroes + + while (*pszBuffer == '0') + { + uiLeadingZeroes++; + pszBuffer++; + } + pszToken = pszBuffer; + uiLen = 0; + while ((*pszBuffer) && (*pszBuffer >= '0') && (*pszBuffer <= '9')) + { + uiLen++; + pszBuffer++; + } + if ((!uiLen) && (uiLeadingZeroes)) + { + *puiNum = 0; + bIsNum = TRUE; + goto Exit; + } + if ((!uiLen) || (uiLen > 10) || (*pszBuffer)) + { + goto Exit; + } + + // Make sure there are not more characters than we can handle + + if ((uiLen == 10) && (f_strcmp( pszToken, "4294967295") > 0)) + { + goto Exit; + } + + // Convert the pszToken to a number + + *puiNum = 0; + pszBuffer = pszToken; + while (*pszBuffer) + { + *puiNum = *puiNum * 10 + *pszBuffer - '0'; + pszBuffer++; + } + } + bIsNum = TRUE; +Exit: + return( bIsNum); +} + +/**************************************************************************** +Desc: This routine skips the characters in the string specified by + pszCharsToSkip. +****************************************************************************/ +FSTATIC char * domeditSkipChars( + char * pszStr, + char * pszCharsToSkip + ) +{ + char * pszTmp; + + while (*pszStr) + { + pszTmp = pszCharsToSkip; + while ((*pszTmp) && (*pszTmp != *pszStr)) + { + pszTmp++; + } + if (*pszTmp) + { + pszStr++; + } + else + { + break; + } + } + return( pszStr); +} + + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE _domEditBackgroundThread( + F_Thread * pThread) +{ + FLMUINT uiCount = 0; + + while( !pThread->getShutdownFlag()) + { + uiCount++; + if( !(uiCount % 50)) + { + if( RC_BAD( domEditVerifyRun())) + { + gv_bShutdown = TRUE; + break; + } + } + f_sleep( 1000); + } + + return( NE_XFLM_OK); +} + + +/**************************************************************************** +Desc: Updates the name table from the database +*****************************************************************************/ +RCODE F_DomEditor::refreshNameTable( void) +{ + DME_NAME_TABLE_INFO nametableInfo; + RCODE rc = NE_XFLM_OK; + + flmAssert( m_bSetupCalled == TRUE); + + if (m_pNameTable) + { + m_pNameTable->Release(); + m_pNameTable = NULL; + } + + /* + Call the callback to build the name table + */ + + f_memset( &nametableInfo, 0, sizeof( DME_NAME_TABLE_INFO)); + + if( m_pEventHook) + { + + if( RC_BAD( rc = m_pEventHook( this, F_DOMEDIT_EVENT_NAME_TABLE, + (void *)(&nametableInfo), m_EventData))) + { + goto Exit; + } + if (nametableInfo.bInitialized) + { + if ((m_pNameTable = nametableInfo.pNameTable) != NULL) + { + m_pNameTable->AddRef(); + } + } + } + + // Try the default initialization if no name table came back from + // the event hook. + + if (!m_pNameTable) + { + + // addRef is done by getNameTable. + + if (RC_BAD( rc = m_pDb->getNameTable( &m_pNameTable))) + { + if (rc != NE_XFLM_NO_NAME_TABLE) + { + goto Exit; + } + + // If there is no name table (could be that m_pDb == NULL), + // create one with at least the dictionary tags. + + if ((m_pNameTable = f_new F_NameTable) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + if (RC_BAD( rc = m_pNameTable->addReservedDictTags())) + { + goto Exit; + } + } + } + + +Exit: + + return( rc); +} + +/*============================================================================= +Desc: Convert an ascii string to unicode +*============================================================================*/ +FSTATIC void asciiToUnicode( + char * pszAsciiString, + FLMUNICODE * puzString) +{ + FLMUINT uiLoop = 0; + + // Convert to a unicode string. + for( uiLoop=0; *pszAsciiString; pszAsciiString++) + { + if (*pszAsciiString == '&') + { + char * pszTmp = pszAsciiString + 1; + if (*pszTmp == '#') + { + FLMUNICODE * puzTmpPtr = &puzString[ uiLoop]; + pszTmp++; + + *puzTmpPtr = (FLMUNICODE)f_atoud( pszTmp); + uiLoop++; + pszAsciiString += 6; + } + else + { + puzString[ uiLoop++] = (FLMUNICODE)*pszAsciiString; + } + } + else + { + puzString[ uiLoop++] = (FLMUNICODE)*pszAsciiString; + } + } + puzString[ uiLoop] = (FLMUNICODE)0; + return; +} + + +/*============================================================================= +Desc: Convert a unicode string to ascii - separate buffer +*============================================================================*/ +FSTATIC RCODE unicodeToAscii( + FLMUNICODE * puzString, + char * pszString, + FLMUINT uiLength + ) +{ + RCODE rc = NE_XFLM_OK; + char * pszDest; + FLMINT iLength = uiLength; + + pszDest = pszString; + while( iLength > 0 && *puzString) + { + + if (*puzString < 32 || *puzString > 126) + { + if (iLength >= 7) + { + f_sprintf( pszDest, "&#%04u;",(unsigned)*puzString); + pszDest += 7; + iLength -= 7; + } + else + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + } + else + { + *pszDest = (char)*puzString; + pszDest++; + iLength--; + } + puzString++; + } + + if (iLength < 0) + { + rc = RC_SET( NE_XFLM_CONV_DEST_OVERFLOW); + goto Exit; + } + if (pszDest && uiLength) + { + *pszDest = '\0'; + } + +Exit: + + return( rc); +} + + +/**************************************************************************** +Desc: Get's a DOM node's name, allocating memory as needed. +****************************************************************************/ +RCODE F_DomEditor::getNodeName( + F_DOMNode * pDOMNode, + DME_ROW_INFO * pRow, + FLMUNICODE ** ppuzName, + FLMUINT * puiBufSize + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen = 0; + FLMUINT uiPrefixLen = 0; + FLMUINT uiNameId; + FLMUINT uiPrefixId; + FLMUINT uiType; + FLMUNICODE * puzBuffer = NULL; + FLMUNICODE * puzPrefix = NULL; + FLMUNICODE * puzAppendBuffer = NULL; + + if (puiBufSize) + { + *puiBufSize = 0; + } + + if (ppuzName) + { + *ppuzName = NULL; + } + + if (!pDOMNode) + { + flmAssert( pRow); + } + + if (pDOMNode) + { + + // See if there is a prefix... + if (RC_BAD( rc = pDOMNode->getPrefixId( m_pDb, &uiPrefixId))) + { + goto Exit; + } + + + // First we get the name id, then we look up the value in the name table. + if (RC_BAD( rc = pDOMNode->getNameId( m_pDb, &uiNameId))) + { + goto Exit; + } + + switch (pDOMNode->getNodeType()) + { + case DOCUMENT_NODE: + case ELEMENT_NODE: + case DATA_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + { + uiType = ELM_ELEMENT_TAG; + break; + } + case ATTRIBUTE_NODE: + { + uiType = ELM_ATTRIBUTE_TAG; + break; + } + default: + uiType = 0; + flmAssert( 0); + break; + } + + if (uiPrefixId && ppuzName) + { + if( RC_BAD( rc = pDOMNode->getPrefix( m_pDb, + (FLMUNICODE *)NULL, 0, &uiPrefixLen))) + { + goto Exit; + } + + // Allocate a buffer to hold the data. + uiPrefixLen = (uiPrefixLen + 1) * sizeof( FLMUNICODE); + if (RC_BAD( rc = f_calloc( uiPrefixLen, &puzPrefix))) + { + goto Exit; + } + + if( RC_BAD( rc = pDOMNode->getPrefix( m_pDb, puzPrefix, + uiPrefixLen, NULL))) + { + goto Exit; + } + } + + } + else if (pRow->pVector) + { + uiNameId = pRow->pVector->getNameId( pRow->uiElementNumber); + uiPrefixLen = 0; + if (pRow->pVector->isAttr( pRow->uiElementNumber)) + { + uiType = ELM_ATTRIBUTE_TAG; + } + else + { + uiType = ELM_ELEMENT_TAG; + } + } + else + { + // No name that we can get... Should we return an error? + goto Exit; + } + + // Now get the name + if (RC_BAD( rc = m_pNameTable->getFromTagTypeAndNum( + m_pDb, uiType, uiNameId, NULL, NULL, &uiLen))) + { + goto Exit; + } + uiLen *= sizeof( FLMUNICODE); + + // The length returned is the number of characters. It does + // not count the null terminator. + // We must allow for them when allocating a buffer. + if (uiLen && ppuzName) + { + FLMUINT uiTmpLen = uiLen + sizeof( FLMUNICODE); + + // Allocate a buffer to hold the data. + uiLen = uiTmpLen; + if (RC_BAD( rc = f_calloc( uiTmpLen, &puzBuffer))) + { + goto Exit; + } + + + // Now get the value... + if (RC_BAD( rc = m_pNameTable->getFromTagTypeAndNum( + m_pDb, uiType, uiNameId, puzBuffer, NULL, &uiTmpLen))) + { + goto Exit; + } + } + + // Append the results + if (uiPrefixLen && uiLen && ppuzName) + { + if (RC_BAD( rc = f_calloc( uiLen + uiPrefixLen, &puzAppendBuffer))) + { + goto Exit; + } + f_memcpy( puzAppendBuffer, puzPrefix, uiPrefixLen - 2); + puzAppendBuffer[ (uiPrefixLen >> 1) - 1] = (FLMUNICODE)':'; + f_memcpy( &puzAppendBuffer[ uiPrefixLen >> 1], puzBuffer, uiLen - 2); + + f_free( &puzPrefix); + puzPrefix = NULL; + f_free( &puzBuffer); + puzBuffer = puzAppendBuffer; + puzAppendBuffer = NULL; + uiLen = uiLen + uiPrefixLen; + } + + if (puiBufSize) + { + *puiBufSize = uiLen; + } + + if (ppuzName) + { + *ppuzName = puzBuffer; + puzBuffer = NULL; + } + +Exit: + + if (puzBuffer) + { + f_free( &puzBuffer); + } + if (puzPrefix) + { + f_free( &puzPrefix); + } + if (puzAppendBuffer) + { + f_free( &puzAppendBuffer); + } + + return( rc); +} + +/**************************************************************************** +Desc: Get's a DOM node's value name, allocating memory as needed. +****************************************************************************/ +RCODE F_DomEditor::getNodeValue( + F_DOMNode * pDOMNode, + FLMUNICODE ** ppuzValue, + FLMUINT * puiBufSize, + FLMBOOL //bStartTrans + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLen = 0; + FLMUINT uiDataType; + + if (puiBufSize) + { + *puiBufSize = 0; + } + + *ppuzValue = NULL; + + if (RC_BAD( rc = pDOMNode->getDataType( m_pDb, &uiDataType))) + { + goto Exit; + } + + switch (uiDataType) + { + case XFLM_NODATA_TYPE: + { + break; + } + case XFLM_TEXT_TYPE: + case XFLM_NUMBER_TYPE: + case XFLM_BINARY_TYPE: + { + if (RC_BAD( rc = pDOMNode->getUnicodeChars(m_pDb, &uiLen))) + { + goto Exit; + } + + if (uiLen) + { + + // The length returned does not allow for 2 NULL terminators. + // We must allow for them when allocating a buffer. + + uiLen *= sizeof(FLMUNICODE); + uiLen += sizeof(FLMUNICODE); + if (RC_BAD( rc = f_calloc( uiLen, ppuzValue))) + { + goto Exit; + } + + if (RC_BAD( rc = pDOMNode->getUnicode( m_pDb, *ppuzValue, uiLen, 0, uiLen, &uiLen))) + { + goto Exit; + } + } + + break; + } + default: + break; + } + + if (puiBufSize) + { + *puiBufSize = uiLen; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Formats text. +****************************************************************************/ +FSTATIC RCODE formatText( + FLMUNICODE * puzBuf, + FLMBOOL bQuoted, + char * pszPreText, + char * pszPostText, + char ** ppszString + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzTmp = NULL; + F_DynamicBuffer * pBuffer = NULL; + FLMUNICODE uzNullChar; + char * pszString; + FLMUINT uiMaxStringLen = MAX_DISPLAY_SEGMENT; + FLMUINT uiStringLen = 0; + + flmAssert( ppszString); + + pszString = *ppszString; + + flmAssert( pszString); + + + // NOTE: puzBuf may be NULL - treat same as empty string. + + if (!puzBuf) + { + uzNullChar = 0; + puzTmp = &uzNullChar; + } + else + { + puzTmp = puzBuf; + } + if ((pBuffer = f_new F_DynamicBuffer) == NULL) + { + goto Exit; + } + + // If the first character is a carriage return, skip over it. + + if (*puzTmp == (FLMUNICODE)'\n') + { + puzTmp++; + } + if (pszPreText) + { + if (RC_BAD( rc = pBuffer->addString( pszPreText))) + { + goto Exit; + } + } + if (bQuoted) + { + if (RC_BAD( rc = pBuffer->addChar( '"'))) + { + goto Exit; + } + } + + while (*puzTmp) + { + + // Special handling for newline characters - want to add indent + // spaces to each new line. + + if (*puzTmp == (FLMUNICODE)'\n') + { + puzTmp++; + + // Skip newline if it is the last character in the buffer. + + if (*puzTmp == 0) + { + break; + } + + // Output whatever buffer we have built up. + + if (pBuffer->getBufferSize()) + { + f_sprintf( pszString, "%*.*s", + 0, (uiStringLen <= uiMaxStringLen ? uiMaxStringLen - uiStringLen : 0), pBuffer->printBuffer()); + pszString += pBuffer->getBufferSize(); + uiStringLen += pBuffer->getBufferSize(); + + if ((uiStringLen + 1) < uiMaxStringLen) + { + f_sprintf( pszString, " "); + pszString ++; + uiStringLen ++; + } + else + { + break; + } + + } + + // Output a newline to end this line + pBuffer->reset(); + + // Have already skipped over the newline, so we just continue. + + continue; + } + + // Add the character to the buffer. + + if (*puzTmp >= 32 && *puzTmp <= 126) + { + if (RC_BAD( rc = pBuffer->addChar( (char)*puzTmp))) + { + goto Exit; + } + } + else + { + + // Allow browser to render these - flush what we have in + // our buffer up to this point, then output. + + if (pBuffer->getBufferSize()) + { + f_sprintf( pszString, "%*.*s", + 0, (uiStringLen <= uiMaxStringLen ? uiMaxStringLen - uiStringLen : 0), pBuffer->printBuffer()); + pszString += pBuffer->getBufferSize(); + uiStringLen += pBuffer->getBufferSize(); + pBuffer->reset(); + } + + if (uiStringLen + 7 < uiMaxStringLen) + { + f_sprintf( pszString, "&#%04u;", (unsigned)(*puzTmp)); + pszString += 7; + uiStringLen += 7; + } + else + { + break; + } + + } + + puzTmp++; + } + + if (pBuffer->getBufferSize()) + { + f_sprintf( pszString, "%*.*s", + 0, (uiStringLen <= uiMaxStringLen ? uiMaxStringLen - uiStringLen : 0), pBuffer->printBuffer()); + pszString += pBuffer->getBufferSize(); + uiStringLen += pBuffer->getBufferSize(); + pBuffer->reset(); + } + + if (bQuoted) + { + if (RC_BAD( rc = pBuffer->addChar( '"'))) + { + goto Exit; + } + } + + if (pszPostText) + { + if (RC_BAD( rc = pBuffer->addString( pszPostText))) + { + goto Exit; + } + } + + if (pBuffer->getBufferSize()) + { + f_sprintf( pszString, "%*.*s", + 0, (uiStringLen <= uiMaxStringLen ? uiMaxStringLen - uiStringLen : 0), pBuffer->printBuffer()); + pszString += pBuffer->getBufferSize(); + pBuffer->reset(); + } + + *ppszString = pszString; + +Exit: + + if (pBuffer) + { + pBuffer->Release(); + } + + return( rc); +} + +/*============================================================================= +Desc: Format a Document Node +*============================================================================*/ +FSTATIC RCODE formatDocumentNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzTitle = NULL; + DME_DISP_COLUMN * pDispVals; + FLMUINT uiCol; + F_DOMNode * pAnnotation = NULL; + FLMUNICODE * puzAttrValue = NULL; + char * pszString = NULL; + FLMUINT uiForeground; + + if (pRow->pDomNode) + { + uiForeground = pRow->pDomNode->isQuarantined() + ? WPS_LIGHTCYAN + : WPS_YELLOW; + } + else + { + uiForeground = WPS_YELLOW; + } + + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + /* + Output the Expand/Collapse symbol - check the flags + */ + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : pRow->bHasChildren ? "+" : " "); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + (pRow->uiLevel * 2) + 1; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + + /* + Output the display value + */ + if ( !(uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, pRow->bExpanded ? "" : + !pRow->bHasChildren ? "" : ""); + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, ""); + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = pDomEditor->isMonochrome() ? WPS_WHITE : uiForeground; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + (pRow->uiLevel * 2) + 1; + (*puiNumVals)++; + + if (RC_OK( rc = pRow->pDomNode->getAnnotation( + pDomEditor->getDb(), (IF_DOMNode **)&pAnnotation))) + { + if (RC_OK( rc = pDomEditor->getNodeValue( pAnnotation, &puzAttrValue))) + { + pszString = &pDispVals[ *puiNumVals].szString[0]; + if (RC_BAD( rc = formatText( + puzAttrValue, FALSE, "", "", &pszString))) + { + goto Exit; + } + } + } + else + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = pDomEditor->isMonochrome() ? WPS_WHITE : uiForeground; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + (*puiNumVals)++; + +Exit: + + if (pAnnotation) + { + pAnnotation->Release(); + } + + f_free( &puzTitle); + f_free( &puzAttrValue); + + return rc; + +} + + +/*============================================================================= +Desc: Format an Element Node +*============================================================================*/ +FSTATIC RCODE formatElementNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzName = NULL; + FLMUNICODE * puzAttrName = NULL; + FLMUNICODE * puzAttrValue = NULL; + FLMUINT uiCol; + DME_DISP_COLUMN * pDispVals; + char * pszString; + F_DOMNode * pAttrNode = NULL; + F_DOMNode * pChildNode = NULL; + FLMBOOL bGotFirstAttr = FALSE; + F_Db * pDb = NULL; + FLMUINT uiAttrLen; + FLMUINT uiForeground; + FLMBOOL bHasLocalData; + + if (RC_BAD( rc = pRow->pDomNode->isDataLocalToNode( pDomEditor->getDb(), &bHasLocalData))) + { + goto Exit; + } + + if (pRow->pDomNode) + { + uiForeground = pRow->pDomNode->isQuarantined() + ? WPS_LIGHTCYAN + : WPS_WHITE; + } + else + { + uiForeground = WPS_WHITE; + } + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pDb = pDomEditor->getSource(); + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + /* + Output the Expand/Collapse symbol - check the flags + */ + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : (pRow->bHasChildren || bHasLocalData) ? "+" : " "); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() + ? WPS_BLACK + : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() + ? WPS_BLACK + : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + + if (RC_BAD( rc = pDomEditor->getNodeName( pRow->pDomNode, NULL, &puzName))) + { + goto Exit; + } + + pszString = &pDispVals[ *puiNumVals].szString[ + f_strlen(pDispVals[ *puiNumVals].szString)]; + + if ( !(uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + f_sprintf( pszString, "<"); + pszString++; + } + else + { + f_sprintf( pszString, "isMonochrome() + ? WPS_BLACK + : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString); + (*puiNumVals)++; + + if (!(uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + + pszString = &pDispVals[ *puiNumVals].szString[ + f_strlen(pDispVals[ *puiNumVals].szString)]; + + uiAttrLen = 0; + + for (;;) + { + if (!bGotFirstAttr) + { + // Get the first attribute + if (RC_BAD( + rc = pRow->pDomNode->getFirstAttribute( pDb, + (IF_DOMNode **)&pAttrNode))) + { + if ( rc == NE_XFLM_NOT_FOUND || + rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + bGotFirstAttr = TRUE; + } + else + { + if (RC_BAD( rc = pAttrNode->getNextSibling( + pDb, (IF_DOMNode **)&pAttrNode))) + { + if ( rc == NE_XFLM_NOT_FOUND || + rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + } + + if (RC_BAD( rc = pDomEditor->getNodeName( pAttrNode, NULL, &puzAttrName))) + { + goto Exit; + } + + if (RC_BAD( rc = formatText( + puzAttrName, FALSE, " ", "=", &pszString))) + { + goto Exit; + } + f_free( &puzAttrName); + + // Get the attribute value now. Look for it in the same node first. + if (RC_BAD( rc = pDomEditor->getNodeValue( pAttrNode, &puzAttrValue))) + { + f_sprintf( pszString, "** NO VALUE FOUND **"); + } + + if (RC_BAD( rc = formatText( + puzAttrValue, FALSE, "", "", &pszString))) + { + goto Exit; + } + + f_free( &puzAttrValue); + + if ((uiCol + (pszString - &pDispVals[ *puiNumVals].szString[0])) >= 78) + { + f_sprintf( &pDispVals[ *puiNumVals].szString[ 78 - uiCol - 4], "..."); + break; + } + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_LIGHTRED; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString); + (*puiNumVals)++; + } + + // Add the trailing '>' + f_sprintf( pDispVals[ *puiNumVals].szString, pRow->bExpanded ? ">" : + !pRow->bHasChildren ? ">" : "/>"); + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = uiForeground; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString); + (*puiNumVals)++; + +Exit: + + if (pAttrNode) + { + pAttrNode->Release(); + } + + if (pChildNode) + { + pChildNode->Release(); + } + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + f_free( &puzName); + f_free( &puzAttrName); + f_free( &puzAttrValue); + + return rc; +} + +/****************************************************************************** +Desc: Format an attribute for display. +******************************************************************************/ +FSTATIC RCODE formatAttributeNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzName = NULL; + FLMUNICODE * puzAttrName = NULL; + FLMUNICODE * puzAttrValue = NULL; + FLMUINT uiCol; + DME_DISP_COLUMN * pDispVals; + char * pszString; + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + /* + Output the Expand/Collapse symbol - check the flags + */ + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : pRow->bHasChildren ? "+" : " "); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + + if (RC_BAD( rc = pDomEditor->getNodeName( pRow->pDomNode, NULL, &puzName))) + { + goto Exit; + } + + pszString = &pDispVals[ *puiNumVals].szString[f_strlen(pDispVals[ *puiNumVals].szString)]; + + if ( !(uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + f_sprintf( pszString, "<"); + pszString++; + } + else + { + f_sprintf( pszString, "isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString); + (*puiNumVals)++; + + // Output the attributes, unless this is a terminator + + if (!(uiFlags & F_DOMEDIT_FLAG_ENDTAG)) + { + + pszString = &pDispVals[ *puiNumVals].szString[f_strlen(pDispVals[ *puiNumVals].szString)]; + + // Get the attribute value + if (RC_BAD( rc = pDomEditor->getNodeValue( pRow->pDomNode, &puzAttrValue))) + { + goto Exit; + } + + if (RC_BAD( rc = formatText( puzAttrValue, TRUE, "", "", &pszString))) + { + goto Exit; + } + f_free( &puzAttrValue); + + } + + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_LIGHTRED; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString)+1; + (*puiNumVals)++; + + + // Add the trailing '>' + f_sprintf( pDispVals[ *puiNumVals].szString, pRow->bHasChildren ? ">" : "/>"); + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + (*puiNumVals)++; + +Exit: + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + f_free( &puzName); + f_free( &puzAttrName); + f_free( &puzAttrValue); + + return rc; +} + + +/*============================================================================= +Desc: Format a Text Node +*============================================================================*/ +FSTATIC RCODE formatDataNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags, + char * pszPreText, + char * pszPostText + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzValue = NULL; + FLMBOOL bStringEmpty; + char * pszString; + FLMUINT uiCol; + DME_DISP_COLUMN * pDispVals; + FLMUINT uiForeground; + + if (pRow->pDomNode) + { + uiForeground = pRow->pDomNode->isQuarantined() + ? WPS_LIGHTCYAN + : WPS_WHITE; + } + else + { + uiForeground = WPS_WHITE; + } + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + + // Output the Expand/Collapse symbol - check the flags + + if (pRow->bHasChildren) + { + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : "+"); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + } + else + { + uiCol += 3; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + + if (RC_BAD( rc = pDomEditor->getNodeValue( pRow->pDomNode, &puzValue))) + { + goto Exit; + } + + bStringEmpty = FALSE; + if (!puzValue || !(*puzValue) || (*puzValue == '\n' && puzValue [1] == 0)) + { + bStringEmpty = TRUE; + } + + if (!bStringEmpty || pszPreText || pszPostText) + { + pszString = &pDispVals[ *puiNumVals].szString[ + f_strlen(pDispVals[ *puiNumVals].szString)]; + if (RC_BAD( rc = formatText( puzValue, FALSE, pszPreText, pszPostText, &pszString))) + { + goto Exit; + } + + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = uiForeground; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + (*puiNumVals)++; + } + +Exit: + + f_free( &puzValue); + return rc; +} + + +/*============================================================================= +Desc: Format a Processing Instruction Node +*============================================================================*/ +FSTATIC RCODE formatProcessingInstruction( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzName = NULL; + FLMUNICODE * puzValue = NULL; + char * pszString; + FLMUINT uiCol; + DME_DISP_COLUMN * pDispVals; + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + /* + Output the Expand/Collapse symbol - check the flags + */ + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : "+"); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + // Get the target name + + if (RC_BAD( rc = pDomEditor->getNodeName( pRow->pDomNode, NULL, &puzName))) + { + goto Exit; + } + + // Get the target data + + if (RC_BAD( rc = pDomEditor->getNodeValue( pRow->pDomNode, &puzValue))) + { + goto Exit; + } + + pszString = &pDispVals[ *puiNumVals].szString[ + f_strlen(pDispVals[ *puiNumVals].szString)]; + if (RC_BAD( formatText( puzName, FALSE, "isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + + pszString = &pDispVals[ *puiNumVals].szString[ + f_strlen(pDispVals[ *puiNumVals].szString)]; + if (RC_BAD( formatText( puzValue, FALSE, NULL, "?>", &pszString))) + { + goto Exit; + } + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + (*puiNumVals)++; + +Exit: + + f_free( &puzName); + f_free( &puzValue); + + return rc; +} + +/*============================================================================= +Desc: Format a generic node without using the DOM. +*============================================================================*/ +FSTATIC RCODE formatRow( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals, + FLMUINT uiFlags + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCol = 0; + DME_DISP_COLUMN * pDispVals; + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + uiCol = pRow->uiLevel * 2; + /* + Output the Expand/Collapse symbol - check the flags + */ + + if ( !(uiFlags & F_DOMEDIT_FLAG_HIDE_EXPAND)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%s", pRow->bExpanded ? "-" : "+"); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + /* + Output the level + */ + + if( !(uiFlags & F_DOMEDIT_FLAG_HIDE_LEVEL)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, + "%u", pRow->uiLevel); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + uiCol += 2; + } + + if (RC_BAD( rc = unicodeToAscii( + pRow->puzValue, pDispVals[ *puiNumVals].szString, unicodeStrLen( pRow->puzValue)))) + { + goto Exit; + } + + pDispVals[ *puiNumVals].uiCol = (uiFlags & F_DOMEDIT_FLAG_COMMENT ? 0 : uiCol); + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + (*puiNumVals)++; + +Exit: + return rc; +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +FSTATIC void printNodeType( + DME_ROW_INFO * pRow, + FTX_WINDOW * pStatusWin + ) +{ + + eDomNodeType eType = pRow->eType; + + switch (eType) + { + case INVALID_NODE : + FTXWinPrintf( pStatusWin, " | Type: Unknown"); + break; + case ELEMENT_NODE: + FTXWinPrintf( pStatusWin, " | Type: Element"); + break; + case DATA_NODE: + FTXWinPrintf( pStatusWin, " | Type: Text"); + break; + case CDATA_SECTION_NODE: + FTXWinPrintf( pStatusWin, " | Type: CDATA"); + break; + case PROCESSING_INSTRUCTION_NODE: + FTXWinPrintf( pStatusWin, " | Type: Processing Instruction"); + break; + case COMMENT_NODE: + FTXWinPrintf( pStatusWin, " | Type: Comment"); + break; + case DOCUMENT_NODE: + FTXWinPrintf( pStatusWin, " | Type: Document"); + break; + case ATTRIBUTE_NODE: + FTXWinPrintf( pStatusWin, " | Type: Attribute"); + break; + default: + break; + } +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +FSTATIC void releaseRow( + DME_ROW_INFO ** ppRow + ) +{ + DME_ROW_INFO * pRow = *ppRow; + + if (pRow->puzValue) + { + f_free( &pRow->puzValue); + } + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + } + if (pRow->pVector && pRow->uiElementNumber == 0) + { + pRow->pVector->reset(); + pRow->pVector->Release(); + } + *ppRow = pRow->pNext; + if (pRow->pPrev) + { + pRow->pPrev->pNext = pRow->pNext; + } + if (pRow->pNext) + { + pRow->pNext->pPrev = pRow->pPrev; + } + + f_free( &pRow); +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +void F_DomEditor::releaseLastRow() +{ + DME_ROW_INFO * pLastRow = m_pScrLastRow->pPrev; + + flmAssert( pLastRow); + releaseRow( &m_pScrLastRow); + m_pScrLastRow = pLastRow; + m_pScrLastRow->pNext = NULL; + m_uiNumRows--; +} + + + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::getNextTitle( + DME_ROW_INFO * pRow, + DME_ROW_INFO ** ppNewRow + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIndex = 0; + F_DOMNode * pDOMNode = NULL; + FLMUNICODE * puzTitle=NULL; + FLMUINT64 ui64DocumentID; + FLMUINT64 ui64NodeID; + DME_ROW_INFO * pTmpRow = NULL; + + if (RC_BAD( rc = getDomNode(pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRow->pDomNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pRow->pDomNode->getNextDocument( + m_pDb, (IF_DOMNode **)&pDOMNode))) + { + if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( RC_BAD( rc = pDOMNode->getNodeId( m_pDb, &ui64NodeID))) + { + goto Exit; + } + + if( RC_BAD( rc = pDOMNode->getDocumentId( m_pDb, &ui64DocumentID))) + { + goto Exit; + } + + if (RC_BAD( rc = makeNewRow( &pTmpRow, NULL, ui64NodeID))) + { + goto Exit; + } + + pTmpRow->ui64DocId = ui64DocumentID; + pTmpRow->uiIndex = uiIndex; + pTmpRow->uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | + F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | + F_DOMEDIT_FLAG_READ_ONLY); + + *ppNewRow = pTmpRow; + + pTmpRow = NULL; + +Exit: + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + if (pDOMNode) + { + pDOMNode->Release(); + } + f_free( &puzTitle); + + return rc; + +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::getPrevTitle( + DME_ROW_INFO * pRow, + DME_ROW_INFO ** ppNewRow + ) +{ + RCODE rc = NE_XFLM_OK; + F_DOMNode * pDOMNode = NULL; + FLMUNICODE * puzTitle=NULL; + FLMUINT64 ui64DocumentID; + DME_ROW_INFO * pTmpRow = NULL; + + if (RC_BAD( rc = getDomNode(pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRow->pDomNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pRow->pDomNode->getPreviousDocument( + m_pDb, (IF_DOMNode **)&pDOMNode))) + { + if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + goto Exit; + } + + if( RC_BAD( rc = pDOMNode->getNodeId( m_pDb, &ui64DocumentID))) + { + goto Exit; + } + + if (RC_BAD( rc = makeNewRow( &pTmpRow, NULL, ui64DocumentID))) + { + goto Exit; + } + + pTmpRow->uiIndex--; + + pTmpRow->uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | + F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | + F_DOMEDIT_FLAG_READ_ONLY); + + *ppNewRow = pTmpRow; + + pTmpRow = NULL; + +Exit: + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + if (pDOMNode) + { + pDOMNode->Release(); + } + f_free( &puzTitle); + + return rc; + +} + +/****************************************************************************** +Desc: Retrieve the dom node if not already present. +******************************************************************************/ +FSTATIC RCODE getDOMNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow + ) +{ + RCODE rc = NE_XFLM_OK; + + if (!pRow->pDomNode) + { + if (RC_BAD( rc = pDomEditor->getDomNode( pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRow->pDomNode))) + { + goto Exit; + } + pRow->eType = pRow->pDomNode->getNodeType(); + } + +Exit: + + return rc; +} + + +/**************************************************************************** +Desc: Edit a text buffer +*****************************************************************************/ +#define MIN_BUFSIZE 80 +RCODE F_DomEditor::editTextBuffer( + char ** ppszBuffer, + FLMUINT uiBufSize, + FLMUINT * puiTermChar + ) +{ + RCODE rc = NE_XFLM_OK; + char * pszBuffer = *ppszBuffer; + FLMUINT uiNumCols; + FLMUINT uiNumRows; + FLMUINT uiNumWinRows = 3; + FLMUINT uiNumWinCols; + FTX_WINDOW * pWindow = NULL; + F_FileHdl * pFileHdl = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( FTXScreenGetSize( m_pScreen, &uiNumCols, &uiNumRows) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + uiNumWinCols = uiNumCols - 8; + + if( FTXWinInit( m_pScreen, uiNumWinCols, + uiNumWinRows, &pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinSetScroll( pWindow, FALSE) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetCursorType( pWindow, WPS_CURSOR_UNDERLINE); + + if( FTXWinSetBackFore( pWindow, m_bMonochrome ? WPS_BLACK : WPS_CYAN, + WPS_WHITE) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinDrawBorder( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinMove( pWindow, (uiNumCols - uiNumWinCols) / 2, + (uiNumRows - uiNumWinRows) / 2) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinOpen( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( FTXWinClear( pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Adjust the buffer size if needed. + if (uiBufSize < MIN_BUFSIZE) + { + if (RC_BAD( rc = f_realloc( MIN_BUFSIZE, ppszBuffer))) + { + goto Exit; + } + pszBuffer = *ppszBuffer; + if (!uiBufSize) + { + f_memset( pszBuffer, 0, MIN_BUFSIZE); + } + uiBufSize = MIN_BUFSIZE; + } + + if( FTXLineEdit( pWindow, pszBuffer, uiBufSize, uiBufSize, + NULL, puiTermChar) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + if( pWindow) + { + FTXWinFree( &pWindow); + } + + return( rc); +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::editRow( + FLMUINT, //uiCurRow, + DME_ROW_INFO * pCurRow, + FLMBOOL bReadOnly + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzBuffer = NULL; + char * pszBuffer = NULL; + FLMUINT uiBufSize; + FLMUINT uiTermChar; + eDomNodeType eType; + FLMUINT uiDataType; + + // Get the dom node + if (RC_BAD( rc = getDOMNode( this, pCurRow))) + { + goto Exit; + } + + eType = pCurRow->pDomNode->getNodeType(); + switch (eType) + { + case ATTRIBUTE_NODE: + { + if (RC_BAD( rc = getNodeValue( pCurRow->pDomNode, &puzBuffer, &uiBufSize))) + { + goto Exit; + } + + if (bReadOnly) + { + if (RC_BAD( rc = f_calloc( uiBufSize + 2, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = f_calloc( (uiBufSize * 2) + 2, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + + if (bReadOnly) + { + FLMUINT uiRows; + + uiRows = uiBufSize / (m_uiEditCanvasCols - 4) + 3; + + if (RC_BAD( rc = FTXDisplayScrollWindow( m_pScreen->pFtxInfo, + m_pScreen, + "View", + pszBuffer, + m_uiEditCanvasCols - 4, + uiRows > 10 ? 10 : uiRows))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = editTextBuffer( &pszBuffer, uiBufSize * 2, &uiTermChar))) + { + goto Exit; + } + } + + // Save the results? + if (uiTermChar == WPK_ENTER && !bReadOnly) + { + + // Begin a transaction + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + // Need to know the attribute data type. + if (RC_BAD( pCurRow->pDomNode->getDataType( m_pDb, &uiDataType))) + { + goto Exit; + } + + switch (uiDataType) + { + case XFLM_TEXT_TYPE: + { + // Get a new unicode buffer.... + f_free( &puzBuffer); + if (RC_BAD( rc = f_calloc( (f_strlen( pszBuffer) * 2) + 2, &puzBuffer))) + { + goto Exit; + } + + asciiToUnicode( pszBuffer, puzBuffer); + if (RC_BAD( rc = pCurRow->pDomNode->setUnicode( + m_pDb, puzBuffer, unicodeStrLen(puzBuffer), TRUE))) + { + (void)abortTransaction(); + goto Exit; + } + break; + } + case XFLM_NUMBER_TYPE: + { + FLMUINT64 ui64Value; + + if( RC_BAD( rc = getNumber( pszBuffer, &ui64Value, NULL))) + { + displayMessage( "Invalid number", rc, + NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + if (RC_BAD( rc = pCurRow->pDomNode->setUINT64( m_pDb, ui64Value))) + { + goto Exit; + } + break; + } + case XFLM_BINARY_TYPE: + { + rc = RC_SET( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + } + + + // Commit the transaction + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + } + + break; + } + + case DATA_NODE: + case CDATA_SECTION_NODE: + case COMMENT_NODE: + { + + if (RC_BAD( rc = getNodeValue( pCurRow->pDomNode, &puzBuffer, &uiBufSize))) + { + goto Exit; + } + + if (bReadOnly) + { + if (RC_BAD( rc = f_calloc( uiBufSize + 2, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = f_calloc( (uiBufSize * 2) + 2, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + + if (bReadOnly) + { + FLMUINT uiRows; + + uiRows = uiBufSize / (m_uiEditCanvasCols - 4) + 3; + + if (RC_BAD( rc = FTXDisplayScrollWindow( m_pScreen->pFtxInfo, + m_pScreen, + "View", + pszBuffer, + m_uiEditCanvasCols - 4, + uiRows > 10 ? 10 : uiRows))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = editTextBuffer( &pszBuffer, uiBufSize, &uiTermChar))) + { + goto Exit; + } + } + + // Save the results? + if (uiTermChar == WPK_ENTER && !bReadOnly) + { + // Get a new unicode buffer.... + f_free( &puzBuffer); + if (RC_BAD( rc = f_calloc( (f_strlen( pszBuffer) * 2) + 2, &puzBuffer))) + { + goto Exit; + } + + asciiToUnicode( pszBuffer, puzBuffer); + + // Begin a transaction + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + if (RC_BAD( rc = pCurRow->pDomNode->setUnicode( + m_pDb, puzBuffer, unicodeStrLen( puzBuffer) * sizeof( FLMUNICODE), + TRUE))) + { + goto Exit; + } + + // Commit the transaction + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + } + break; + } + + case ELEMENT_NODE: + { + rc = selectElementAttribute( pCurRow, &uiTermChar); + goto Exit; + } + + default: + { + break; + } + } + + +Exit: + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + if (pCurRow->pDomNode) + { + pCurRow->pDomNode->Release(); + pCurRow->pDomNode = NULL; + } + + f_free( &puzBuffer); + f_free( &pszBuffer); + + return rc; +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::editIndexRow( + DME_ROW_INFO * pCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzBuffer = NULL; + char * pszBuffer = NULL; + FLMUINT uiBufSize = 0; + FLMUINT uiTermChar; + F_DataVector * pVector = pCurRow->pVector; + FLMUINT uiElementNumber = pCurRow->uiElementNumber; + + if (!pCurRow->bUseValue) + { + + flmAssert( pVector); + + // Get the current value in the buffer or display the one in the buffer. + switch( pVector->getDataType( uiElementNumber)) + { + case XFLM_TEXT_TYPE: + { + + if ((uiBufSize = pVector->getDataLength( uiElementNumber)) > 0) + { + + if (RC_BAD( rc = f_calloc( uiBufSize, &pszBuffer))) + { + goto Exit; + } + + if (RC_BAD( rc = pVector->getUTF8( uiElementNumber, + (FLMBYTE *)pszBuffer, &uiBufSize))) + { + goto Exit; + } + } + else + { + uiBufSize = 30; + if (RC_BAD( rc = f_calloc( uiBufSize, &pszBuffer))) + { + goto Exit; + } + *pszBuffer = 0; + } + break; + } + case XFLM_NUMBER_TYPE: + { + FLMUINT64 ui64Num; + uiBufSize = 23; // Largest number of charcters in a 64 bit number + 3. + if (RC_BAD( rc = f_calloc( uiBufSize, &pszBuffer))) + { + goto Exit; + } + if (RC_BAD( rc = pVector->getUINT64( uiElementNumber, &ui64Num))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + *pszBuffer = 0; + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + f_sprintf( pszBuffer, "0x%I64x", ui64Num); + } + break; + } + case XFLM_NODATA_TYPE: + case XFLM_BINARY_TYPE: + default: + { + rc = RC_SET( NE_XFLM_BAD_DATA_TYPE); + break; + } + } + } + else + { + puzBuffer = pCurRow->puzValue; + pCurRow->puzValue = NULL; + uiBufSize = unicodeStrLen( puzBuffer); + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + + if (RC_BAD( rc = editTextBuffer( &pszBuffer, uiBufSize * 2, &uiTermChar))) + { + goto Exit; + } + + // Save the results? + if (uiTermChar == WPK_ENTER) + { + switch( pVector->getDataType( uiElementNumber)) + { + case XFLM_UNKNOWN_TYPE: + case XFLM_NODATA_TYPE: + case XFLM_TEXT_TYPE: + { + if (RC_BAD( rc = pVector->setUTF8( uiElementNumber, + (FLMBYTE *)pszBuffer))) + { + goto Exit; + } + break; + } + case XFLM_NUMBER_TYPE: + { + FLMUINT64 ui64Num = f_atou64( pszBuffer); + + if (RC_BAD( rc = pVector->setUINT64( uiElementNumber, ui64Num))) + { + goto Exit; + } + break; + } + } + } + else + { + pCurRow->puzValue = puzBuffer; + puzBuffer = NULL; + } + + +Exit: + + f_free( &puzBuffer); + f_free( &pszBuffer); + + return rc; +} + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::editIndexNode( + DME_ROW_INFO * pCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUNICODE * puzBuffer = NULL; + char * pszBuffer = NULL; + FLMUINT uiBufSize = 0; + FLMUINT uiTermChar; + F_DataVector * pVector = pCurRow->pVector; + FLMUINT uiElementNumber = pCurRow->uiElementNumber; + FLMUINT64 ui64NodeId; + + if (!pCurRow->bUseValue) + { + + flmAssert( pVector); + + ui64NodeId = pVector->getID( uiElementNumber); + uiBufSize = 23; // Largest number of charcters in a 64 bit number + 3. + if (RC_BAD( rc = f_calloc( uiBufSize, &pszBuffer))) + { + goto Exit; + } + f_sprintf( pszBuffer, "%I64u", ui64NodeId); + } + else + { + puzBuffer = pCurRow->puzValue; + pCurRow->puzValue = NULL; + uiBufSize = unicodeStrLen( puzBuffer); + if (RC_BAD( rc = unicodeToAscii( puzBuffer, pszBuffer, uiBufSize))) + { + goto Exit; + } + } + + + if (RC_BAD( rc = editTextBuffer( &pszBuffer, uiBufSize * 2, &uiTermChar))) + { + goto Exit; + } + + // Save the results? + if (uiTermChar == WPK_ENTER) + { + FLMUINT64 ui64Num = f_atou64( pszBuffer); + + if (RC_BAD( rc = pVector->setID( uiElementNumber, ui64Num))) + { + goto Exit; + } + } + else + { + pCurRow->puzValue = puzBuffer; + puzBuffer = NULL; + } + + +Exit: + + f_free( &puzBuffer); + f_free( &pszBuffer); + + return rc; +} + + + +/**************************************************************************** +Desc: Allows the user to interactively select and edit the attributes of an + element node +*****************************************************************************/ +RCODE F_DomEditor::selectElementAttribute( + DME_ROW_INFO * pRow, + FLMUINT * puiTermChar) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiFlags; + F_DomEditor * pAttributeList = NULL; + F_DOMNode * pAttrNode = NULL; + FLMBOOL bGotFirstAttr; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if (pRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG) + { + rc = displayMessage( "Cannot edit Element end tag", + NE_XFLM_FAILURE, puiTermChar, WPS_RED, WPS_WHITE); + goto Exit; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pAttributeList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pAttributeList->Setup( m_pScreen))) + { + goto Exit; + } + + pAttributeList->setParent( this); + pAttributeList->setReadOnly( FALSE); + pAttributeList->setShutdown( m_pbShutdown); + pAttributeList->setTitle( "Attributes - Select One"); + pAttributeList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pAttributeList->setSource( m_pDb, m_uiCollection); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_NOPARENT | F_DOMEDIT_FLAG_NOCHILD); + + // Get the dom node for the selected row (element) + if (RC_BAD( rc = getDOMNode( this, pRow))) + { + goto Exit; + } + + // Verify that the node is an element node. + if (pRow->pDomNode->getNodeType() != ELEMENT_NODE) + { + rc = displayMessage( "Invalid node type", + NE_XFLM_FAILURE, puiTermChar, WPS_RED, WPS_WHITE); + goto Exit; + } + + // Get the attributes... + + bGotFirstAttr = FALSE; + for (;;) + { + if (!bGotFirstAttr) + { + // Get the first attribute + if (RC_BAD( rc = pRow->pDomNode->getFirstAttribute( m_pDb, + (IF_DOMNode **)&pAttrNode))) + { + if (rc == NE_XFLM_NOT_FOUND || rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + if (RC_BAD( rc = buildNewRow( 0, + pAttrNode, + &pTmpRow))) + { + goto Exit; + } + bGotFirstAttr = TRUE; + } + else + { + if (RC_BAD( rc = pAttrNode->getNextSibling( + m_pDb, (IF_DOMNode **)&pAttrNode))) + { + if (rc == NE_XFLM_NOT_FOUND || + rc == NE_XFLM_EOF_HIT || + rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + break; + } + else + { + goto Exit; + } + } + if (RC_BAD( rc = buildNewRow( 0, + pAttrNode, + &pTmpRow))) + { + goto Exit; + } + } + + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pAttributeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + } + + if (pAttrNode) + { + pAttrNode->Release(); + pAttrNode = NULL; + } + + + // Set the start row. + pAttributeList->setCurrentAtTop(); + + if( RC_BAD( rc = pAttributeList->interactiveEdit( + m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pAttributeList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pAttributeList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pAttributeList->setControlFlags( pTmpRow, uiFlags); + break; + } + if (RC_BAD( rc = pAttributeList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if (pTmpRow) + { + if (RC_BAD( rc = editRow( 0, pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pAttributeList->getLastKey(); + } + +Exit: + + if (pAttrNode) + { + pAttrNode->Release(); + } + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + if( pAttributeList) + { + pAttributeList->Release(); + pAttributeList = NULL; + } + + return( rc); +} + + +/****************************************************************************** +Desc: Method to delete a node (row) +******************************************************************************/ +RCODE F_DomEditor::deleteRow( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pRow = *ppCurRow; + FLMBOOL bNextRow = TRUE; + FLMUINT uiTermChar; + char szMessage[ 100]; + char szResponse[ 3]; + + if (!canDeleteRow(pRow)) + { + rc = RC_SET( NE_XFLM_ILLEGAL_OP); + goto Exit; + } + + // Confirm this action before deleting... + switch (pRow->eType) + { + case DOCUMENT_NODE: + f_sprintf( szMessage, "Delete DOCUMENT_NODE [%,I64u]? ", + pRow->ui64DocId); + break; + case ELEMENT_NODE: + f_sprintf( szMessage, "Delete ELEMENT_NODE [%,I64u]? ", + pRow->ui64NodeId); + break; + case ATTRIBUTE_NODE: + f_sprintf( szMessage, "Delete ATTRIBUTE_NODE [%,I64u]? ", + pRow->ui64NodeId); + break; + case DATA_NODE: + f_sprintf( szMessage, "Delete DATA_NODE [%,I64u]? ", + pRow->ui64NodeId); + break; + default: + f_sprintf( szMessage, "Delete UNKNOWN_NODE [%,I64u]? ", + pRow->ui64NodeId); + break; + } + + f_memset( szResponse, 0, sizeof(szResponse)); + + requestInput( szMessage, + &szResponse[ 0], sizeof( szResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE || szResponse[ 0] == 'N' || szResponse[ 0] == 'n') + { + goto Exit; + } + + if ( szResponse[ 0] != 'Y' && szResponse[ 0] != 'y') + { + goto Exit; + } + + // If the current row is expanded, we first need to collapse it. + if (pRow->bExpanded) + { + if (RC_BAD( rc = collapseRow( &pRow))) + { + goto Exit; + } + } + + // Start an update transaction + if( RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + // Get the dom for this row but don't start a new transaction. + if (RC_BAD( rc = getDOMNode( this, pRow))) + { + goto Exit; + } + + if (RC_BAD( rc = pRow->pDomNode->deleteNode( m_pDb))) + { + (void)abortTransaction(); + goto Exit; + } + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + + + + // return the next or previous row. + if (RC_BAD( rc = getNextRow( pRow, ppCurRow, TRUE))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + + if (*ppCurRow == NULL) + { + if (RC_BAD( rc = getPrevRow( pRow, ppCurRow, TRUE))) + { + if (rc == NE_XFLM_DOM_NODE_NOT_FOUND) + { + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + bNextRow = FALSE; + } + + if (m_pScrFirstRow == pRow) + { + m_pScrFirstRow = *ppCurRow; + } + + if (m_pScrLastRow == pRow) + { + m_pScrLastRow = *ppCurRow; + } + + + if (pRow->ui64NodeId == pRow->ui64DocId) + { + DME_ROW_INFO * pDoc = m_pCurDoc; + // will need to remove this Document from the DocList. + checkDocument( &pDoc, pRow); + + if (pDoc) + { + + if (pDoc->pPrev) + { + pDoc->pPrev->pNext = pDoc->pNext; + } + if (pDoc->pNext) + { + pDoc->pNext->pPrev = pDoc->pPrev; + } + + if (m_pDocList == pDoc) + { + m_pDocList = pDoc->pNext; + } + + m_pCurDoc = pDoc->pNext; + f_free( &pDoc); + } + } + + releaseRow( &pRow); + m_uiNumRows--; + + // See if we can get a new last row. + if ( *ppCurRow) + { + while (m_uiNumRows < m_uiEditCanvasRows) + { + if (RC_BAD( rc = getNextRow( pRow, &pRow, TRUE))) + { + goto Exit; + } + if (pRow == NULL) + { + break; + } + } + } + + if (pRow) + { + m_pScrLastRow = pRow; + pRow = NULL; + } + + // If the new current row is null, then point to the last row - it too may be null + // but in that case, there is nothing to display. + if (*ppCurRow == NULL) + { + *ppCurRow = m_pScrLastRow; + bNextRow = FALSE; + } + + // If the new cursor row is a parent of the node we just deleted, + // we need to re-set the bHasChildren flag. There may not be anymore + // children, so we want this to display correctly. + pRow = *ppCurRow; + + if (pRow) + { + if (pRow->bExpanded && + ((bNextRow && pRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG) || + (!bNextRow && !(pRow->uiFlags & F_DOMEDIT_FLAG_ENDTAG)))) + { + if (RC_BAD( rc = collapseRow( &pRow))) + { + goto Exit; + } + } + if (RC_BAD( rc = getDOMNode( this, pRow))) + { + goto Exit; + } + + if (RC_BAD( rc = pRow->pDomNode->hasChildren( + m_pDb, &pRow->bHasChildren))) + { + goto Exit; + } + } + + // Sync the document list to the row. + checkDocument(&m_pCurDoc, pRow); + +Exit: + + if (pRow) + { + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + } + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return rc; +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::addSomething( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + eDomNodeType eNodeType; + FLMUINT uiTermChar; + + // Select the type of Node that we are going to add. + if (RC_BAD( rc = selectNodeType( &eNodeType, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + switch (eNodeType) + { + case ELEMENT_NODE: + { + if (*ppCurRow) + { + if (RC_BAD( rc = createElementNode( ppCurRow))) + { + goto Exit; + } + } + else + { + if (RC_BAD( rc = createRootElementNode( ppCurRow))) + { + goto Exit; + } + } + break; + } + case ATTRIBUTE_NODE: + { + if (RC_BAD( rc = createAttributeNode( ppCurRow))) + { + goto Exit; + } + break; + } + case DATA_NODE: + case COMMENT_NODE: + case CDATA_SECTION_NODE: + { + if (RC_BAD( rc = createTextNode( ppCurRow, eNodeType))) + { + goto Exit; + } + break; + } + case DOCUMENT_NODE: + { + if (RC_BAD( rc = createDocumentNode( ppCurRow))) + { + goto Exit; + } + if (RC_BAD( rc = addDocumentToList( + m_uiCollection, (*ppCurRow)->ui64DocId))) + { + goto Exit; + } + + break; + } + case PROCESSING_INSTRUCTION_NODE: + default: + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + } + +Exit: + + return rc; +} + + +/****************************************************************************** +Desc: +******************************************************************************/ +RCODE F_DomEditor::createDocumentNode( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + char szResponse[ 128]; + FLMUNICODE * puzTitle = NULL; + FLMUINT uiTitleLen; + FLMUINT uiTermChar; + F_DOMNode * pDocNode = NULL; + DME_ROW_INFO * pNewRow = NULL; + IF_DOMNode * pSource = NULL; + + f_memset( szResponse, 0, sizeof(szResponse)); + + requestInput( "Document Title / Comment", + szResponse, sizeof( szResponse), &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + uiTitleLen = f_strlen( szResponse); + + if (RC_BAD( rc = f_calloc( (uiTitleLen * 2) + 2, &puzTitle))) + { + goto Exit; + } + + asciiToUnicode( szResponse, puzTitle); + + + // Begin a transaction + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + // Create a new document node. + if (RC_BAD( m_pDb->createDocument( + m_uiCollection, (IF_DOMNode **)&pDocNode))) + { + goto Exit; + } + + // save the source + + if( RC_BAD( rc = pDocNode->createAttribute( + m_pDb, ATTR_SOURCE_TAG, &pSource))) + { + goto Exit; + } + + if (RC_BAD( rc = pSource->setUnicode( + (IF_Db *)m_pDb, puzTitle, unicodeStrLen(puzTitle), TRUE))) + { + goto Exit; + } + + if (m_uiNumRows < m_uiEditCanvasRows) + { + // Create a new row and link it to the end of the list. + if (RC_BAD( rc = buildNewRow( 0, + (F_DOMNode *)pDocNode, + &pNewRow))) + { + goto Exit; + } + + if (RC_BAD( rc = insertRow( pNewRow, m_pScrLastRow))) + { + goto Exit; + } + + // May Need to remove the first row. + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseRow( &m_pScrFirstRow); + m_uiNumRows--; + } + + *ppCurRow = pNewRow; + pNewRow = NULL; + } + + if (RC_BAD( rc = m_pDb->documentDone( pDocNode))) + { + goto Exit; + } + + // Commit... + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + +Exit: + + if (pNewRow) + { + releaseRow( &pNewRow); + } + + if (pDocNode) + { + pDocNode->Release(); + } + + if (pSource) + { + pSource->Release(); + } + + f_free( &puzTitle); + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return rc; +} + + +/*============================================================================= +Desc: Method to create a root element node rather than a document node. +=============================================================================*/ +RCODE F_DomEditor::createRootElementNode( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + char szResponse[ 128]; + FLMUNICODE * puzTitle = NULL; + FLMUINT uiTitleLen; + FLMUINT uiTermChar; + F_DOMNode * pRootNode = NULL; + DME_ROW_INFO * pNewRow = NULL; + IF_DOMNode * pSource = NULL; + FLMUINT uiTag; + RCODE tmpRc; + FLMUINT uiCollection; + + if( RC_BAD( tmpRc = selectCollection( &uiCollection, &uiTermChar))) + { + displayMessage( "Error getting collection", tmpRc, + NULL, WPS_RED, WPS_WHITE); + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + f_memset( szResponse, 0, sizeof(szResponse)); + + requestInput( "Element Title / Comment", + szResponse, + sizeof( szResponse), + &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + uiTitleLen = f_strlen( szResponse); + + if (RC_BAD( rc = f_calloc( (uiTitleLen * 2) + 2, &puzTitle))) + { + goto Exit; + } + + asciiToUnicode( szResponse, puzTitle); + + + // Select the name tag for the element node + if (RC_BAD( rc = selectTag( ELM_ELEMENT_TAG, &uiTag, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + // Begin a transaction + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + // Create a new document node. + if (RC_BAD( m_pDb->createRootElement( uiCollection, + uiTag, + (IF_DOMNode **)&pRootNode))) + { + goto Exit; + } + + // save the source + + if (RC_BAD( rc = pRootNode->createAttribute( + m_pDb, ATTR_SOURCE_TAG, &pSource))) + { + goto Exit; + } + + + if (RC_BAD( rc = pSource->setUnicode( (IF_Db *)m_pDb, + puzTitle, + unicodeStrLen(puzTitle), + TRUE))) + { + goto Exit; + } + + if (uiCollection == m_uiCollection) + { + if (m_uiNumRows < m_uiEditCanvasRows) + { + // Create a new row and link it to the end of the list. + if (RC_BAD( rc = buildNewRow( 0, + (F_DOMNode *)pRootNode, + &pNewRow))) + { + goto Exit; + } + + if (RC_BAD( rc = insertRow( pNewRow, + m_pScrLastRow))) + { + goto Exit; + } + + // May Need to remove the first row. + if (m_uiNumRows > m_uiEditCanvasRows) + { + releaseRow( &m_pScrFirstRow); + m_uiNumRows--; + } + + *ppCurRow = pNewRow; + pNewRow = NULL; + } + + if (RC_BAD( rc = addDocumentToList( m_uiCollection, + (*ppCurRow)->ui64DocId))) + { + goto Exit; + } + } + + if (RC_BAD( rc = m_pDb->documentDone( pRootNode))) + { + goto Exit; + } + + // Commit... + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + +Exit: + + if (pNewRow) + { + releaseRow( &pNewRow); + } + + if (pRootNode) + { + pRootNode->Release(); + } + + if (pSource) + { + pSource->Release(); + } + + f_free( &puzTitle); + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return rc; +} + + +/**************************************************************************** +Desc: Create an ELEMENT_ NODE Dom node. +*****************************************************************************/ +RCODE F_DomEditor::createElementNode( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pRow = *ppCurRow; + char szResponse[ 5]; + FLMUINT uiTermChar; + FLMUINT uiTag; + F_DOMNode * pRefNode = NULL; + F_DOMNode * pElementNode = NULL; + FLMUINT64 ui64RootId; + FLMBOOL bAddSibling = FALSE; + + f_memset( szResponse, 0, sizeof(szResponse)); + + requestInput( "Create a Root Element?", + szResponse, + sizeof( szResponse), + &uiTermChar); + + if( uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + if (szResponse[0] == 'Y' || szResponse[0] == 'y') + { + rc = createRootElementNode( ppCurRow); + goto Exit; + } + + if (!pRow) + { + displayMessage( "No DOM Node to add to.", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + // Verify that the node we are looking at can take an element node. Some + // dom node types can't take a sibling or a child element node. + if (pRow->eType != DOCUMENT_NODE && + pRow->eType != ELEMENT_NODE) + { + displayMessage( "Invalid DOM node type", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + // Select the name tag for the element node + if (RC_BAD( rc = selectTag( ELM_ELEMENT_TAG, &uiTag, &uiTermChar))) + { + goto Exit; + } + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + // Get the dom node for this row. + if (RC_BAD( rc = getDomNode( pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRefNode))) + { + goto Exit; + } + + // Get the root node. + + if( RC_BAD( rc = pRefNode->getDocumentId( m_pDb, &ui64RootId))) + { + goto Exit; + } + + // We need to make sure we have the right root node. + switch (pRow->eType) + { + case DOCUMENT_NODE: + { + pRow->bHasChildren = TRUE; + // Got it already + break; + } + case ELEMENT_NODE: + { + // If we are inserting as a sibling to this node, we need to get the parent node. + // Do we want to add this element as a child or a sibling to the current node. + f_memset( szResponse, 0, sizeof( szResponse)); + requestInput( "Insert as Sibling node?", + szResponse, sizeof( szResponse), &uiTermChar); + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + if ( szResponse[ 0] == 'Y' || szResponse[ 0] == 'y') + { + // Need to get the parent node. + if (RC_BAD( rc = pRefNode->getParentNode( + m_pDb, (IF_DOMNode **)&pRefNode))) + { + goto Exit; + } + bAddSibling = TRUE; + } + } + default: + { + break; + } + } + + if (RC_BAD( rc = pRefNode->createNode( + m_pDb, ELEMENT_NODE, uiTag, XFLM_LAST_CHILD, + (IF_DOMNode **)&pElementNode))) + { + goto Exit; + } + + if (bAddSibling) + { + DME_ROW_INFO * pNewRow = NULL; + FLMUINT uiLevel = pRow->uiLevel; + + // We need to insert the new row into the list + if (RC_BAD( rc = buildNewRow( (FLMINT)pRow->uiLevel, + pElementNode, + &pNewRow))) + { + goto Exit; + } + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + while (pRow && pRow->pNext && pRow->pNext->uiLevel >= uiLevel) + { + pRow = pRow->pNext; + } + + if (pRow && (pRow != m_pScrLastRow || m_uiNumRows < m_uiEditCanvasRows)) + { + if (RC_BAD( rc = insertRow( pNewRow, pRow))) + { + goto Exit; + } + } + + } + else + { + pRow->bHasChildren = TRUE; + + if (pRow->bExpanded) + { + // Collapse the row, then expand it. + if (RC_BAD( rc = collapseRow( &pRow))) + { + goto Exit; + } + if (RC_BAD( rc = expandRow( pRow, TRUE, NULL))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = m_pDb->documentDone( pRefNode))) + { + goto Exit; + } + + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + + +Exit: + + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + if (pRefNode) + { + pRefNode->Release(); + } + + if (pElementNode) + { + pElementNode->Release(); + } + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return rc; +} + +/**************************************************************************** +Desc: Method to create text type nodes. +*****************************************************************************/ +RCODE F_DomEditor::createTextNode( + DME_ROW_INFO ** ppCurRow, + eDomNodeType eNodeType + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pRow = *ppCurRow; + DME_ROW_INFO * pNewRow = NULL; + char szResponse[ 5]; + FLMUINT uiTermChar; + F_DOMNode * pRefNode = NULL; + F_DOMNode * pTextNode = NULL; + FLMUINT64 ui64RootId; + FLMBOOL bAddSibling = FALSE; + char szTextBuffer[ 120]; + FLMUINT uiTextBufLen = sizeof(szTextBuffer); + FLMBOOL bHasChildren = pRow->bHasChildren; + + // Verify that the node we are looking at can take the type of + // node either as a child or sibling... + if (pRow->eType != DOCUMENT_NODE && + pRow->eType != ATTRIBUTE_NODE && + pRow->eType != ELEMENT_NODE) + { + displayMessage( "Invalid DOM node type", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + f_memset( szTextBuffer, 0, uiTextBufLen); + requestInput( "Text", + szTextBuffer, uiTextBufLen, &uiTermChar); + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + // Get the dom node for this row. + if (RC_BAD( rc = getDomNode( pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRefNode))) + { + goto Exit; + } + + // Get the root node. + + if( RC_BAD( rc = pRefNode->getDocumentId( m_pDb, &ui64RootId))) + { + goto Exit; + } + + // We need to make sure we have the right root node. + switch (pRow->eType) + { + case DOCUMENT_NODE: + { + pRow->bHasChildren = TRUE; + // No parent node for these + break; + } + default: + { + // If we are inserting as a sibling to this node, we need to get the parent node. + // Do we want to add this element as a child or a sibling to the current node. + f_memset( szResponse, 0, sizeof( szResponse)); + requestInput( "Insert as Sibling node?", + szResponse, sizeof( szResponse), &uiTermChar); + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + if ( szResponse[ 0] == 'Y' || szResponse[ 0] == 'y') + { + // Need to get the parent node. + if (RC_BAD( pRefNode->getParentNode( + m_pDb, (IF_DOMNode **)&pRefNode))) + { + goto Exit; + } + bAddSibling = TRUE; + } + } + } + + if (RC_BAD( rc = pRefNode->createNode( + m_pDb, eNodeType, 0, XFLM_LAST_CHILD, + (IF_DOMNode **)&pTextNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pTextNode->setUTF8( m_pDb, (FLMBYTE *)szTextBuffer))) + { + goto Exit; + } + + if (bAddSibling) + { + FLMUINT uiLevel = pRow->uiLevel; + + // We need to insert the new row into the list + if (RC_BAD( rc = buildNewRow( (FLMINT)pRow->uiLevel, + pTextNode, + &pNewRow))) + { + goto Exit; + } + + pTextNode->AddRef(); + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + while (pRow && pRow->pNext && pRow->pNext->uiLevel >= uiLevel) + { + pRow = pRow->pNext; + } + + if (pRow && (pRow != m_pScrLastRow || m_uiNumRows < m_uiEditCanvasRows)) + { + if (RC_BAD( rc = insertRow( pNewRow, pRow))) + { + goto Exit; + } + } + + } + else + { + pRow->bHasChildren = TRUE; + + if (pRow->bExpanded) + { + // Collapse the row, then expand it. + if (RC_BAD( rc = collapseRow( &pRow))) + { + goto Exit; + } + if (RC_BAD( rc = expandRow( pRow, TRUE, NULL))) + { + goto Exit; + } + } + } + + if (RC_BAD( rc = m_pDb->documentDone( pRefNode))) + { + goto Exit; + } + + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + +Exit: + + // Restore the bHasChildren flag on error. + if (RC_BAD( rc)) + { + pRow->bHasChildren = bHasChildren; + } + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + if (pRefNode) + { + pRefNode->Release(); + } + + if (pTextNode) + { + pTextNode->Release(); + } + + if (pNewRow && pNewRow->pDomNode) + { + pNewRow->pDomNode->Release(); + pNewRow->pDomNode = NULL; + } + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + + return rc; +} + +/**************************************************************************** +Desc: Create an ATTRIBUTE_NODE Dom node. +*****************************************************************************/ +RCODE F_DomEditor::createAttributeNode( + DME_ROW_INFO ** ppCurRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pRow = *ppCurRow; + FLMUINT uiTermChar; + FLMUINT uiTag; + F_DOMNode * pRefNode = NULL; + F_DOMNode * pAttributeNode = NULL; + char szAttrValue[ 128]; + FLMUNICODE * puzValue = NULL; + FLMUINT uiValueLen; + F_Dict * pDict; + FLMUINT64 ui64Value; + F_AttrElmInfo defInfo; + FLMUINT uiEncDefId = 0; + + // Verify that the node we are looking at can take an element node. Some + // dom node types can't take a sibling or a child element node. + if (pRow->eType != ELEMENT_NODE) + { + displayMessage( "Invalid DOM node type", + NE_XFLM_FAILURE, NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + // Select the name tag for the element node + if (RC_BAD( rc = selectTag( ELM_ATTRIBUTE_TAG, &uiTag, &uiTermChar))) + { + goto Exit; + } + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + f_memset( szAttrValue, 0, sizeof( szAttrValue)); + requestInput( "Attribute Value", + szAttrValue, sizeof(szAttrValue), &uiTermChar); + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + // Select the encryption algorithm (if any) + if (RC_BAD( rc = selectEncDef( ELM_ENCDEF_TAG, &uiEncDefId, &uiTermChar))) + { + goto Exit; + } + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + + // Determine the data type for the selected attribute tag. + if (RC_BAD( rc = m_pDb->getDictionary( &pDict))) + { + goto Exit; + } + + if (RC_BAD( rc = pDict->getAttribute( m_pDb, uiTag, &defInfo))) + { + goto Exit; + } + + // Get the dom node for this row. + if (RC_BAD( rc = getDomNode( pRow->ui64NodeId, + pRow->eType == ATTRIBUTE_NODE + ? pRow->uiNameId + : 0, + &pRefNode))) + { + goto Exit; + } + + if (RC_BAD( rc = pRefNode->createAttribute( + m_pDb, uiTag, (IF_DOMNode **)&pAttributeNode))) + { + goto Exit; + } + + switch (defInfo.getDataType()) + { + case XFLM_TEXT_TYPE: + { + uiValueLen = (f_strlen( szAttrValue) * 2) + 2; + + if (RC_BAD( f_calloc( uiValueLen, &puzValue))) + { + goto Exit; + } + + asciiToUnicode( &szAttrValue[0], puzValue); + + if (RC_BAD( rc = pAttributeNode->setUnicode( m_pDb, + puzValue, + uiValueLen, + TRUE, + uiEncDefId))) + { + goto Exit; + } + + break; + } + case XFLM_NUMBER_TYPE: + { + if( RC_BAD( rc = getNumber( &szAttrValue[ 0], &ui64Value, NULL))) + { + displayMessage( "Invalid node number", rc, + NULL, WPS_RED, WPS_WHITE); + goto Exit; + } + + if (RC_BAD( rc = pAttributeNode->setUINT64( m_pDb, ui64Value, uiEncDefId))) + { + goto Exit; + } + break; + } + case XFLM_BINARY_TYPE: + default: + rc = RC_SET( NE_XFLM_BAD_DATA_TYPE); + goto Exit; + } + + pRow->bHasAttributes = TRUE; + + if (RC_BAD( rc = m_pDb->documentDone( pRefNode))) + { + goto Exit; + } + + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + + +Exit: + + f_free( &puzValue); + + if (pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + + if (pRefNode) + { + pRefNode->Release(); + } + + if (pAttributeNode) + { + pAttributeNode->Release(); + } + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return rc; +} + + + +/**************************************************************************** +Desc: Allows the user to interactively select a Dom Node type +*****************************************************************************/ +RCODE F_DomEditor::selectNodeType( + eDomNodeType * peNodeType, + FLMUINT * puiTermChar + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiFlags; + FLMUNICODE uzItemName[ 128]; + F_DomEditor * pNodeTypeList = NULL; + + flmAssert( m_bSetupCalled == TRUE); + + if( peNodeType) + { + *peNodeType = INVALID_NODE; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pNodeTypeList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pNodeTypeList->Setup( m_pScreen))) + { + goto Exit; + } + + pNodeTypeList->setParent( this); + pNodeTypeList->setReadOnly( TRUE); + pNodeTypeList->setShutdown( m_pbShutdown); + pNodeTypeList->setTitle( "Node Types - Select One"); + pNodeTypeList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pNodeTypeList->setSource( m_pDb, XFLM_DATA_COLLECTION); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NODOM); + + asciiToUnicode( "Element Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)ELEMENT_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "Attribute Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)ATTRIBUTE_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "Text Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)DATA_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "CData Section Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)CDATA_SECTION_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "Processing Instruction Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)PROCESSING_INSTRUCTION_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "Comment Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)COMMENT_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + + asciiToUnicode( "Document Node", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], (FLMUINT)DOCUMENT_NODE, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pNodeTypeList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + // Set the start row. + + pNodeTypeList->setCurrentAtTop(); + + + if( RC_BAD( rc = pNodeTypeList->interactiveEdit( + m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pNodeTypeList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pNodeTypeList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pNodeTypeList->setControlFlags( pTmpRow, uiFlags); + if( peNodeType) + { + *peNodeType = (eDomNodeType)pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pNodeTypeList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pNodeTypeList->getLastKey(); + } + +Exit: + + if( pNodeTypeList) + { + pNodeTypeList->Release(); + pNodeTypeList = NULL; + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + return( rc); +} + +/*============================================================================= +Desc: +=============================================================================*/ +RCODE F_DomEditor::beginTransaction( + eDbTransType eTransType) +{ + RCODE rc = NE_XFLM_OK; + + if (RC_BAD( rc = m_pDb->transBegin( eTransType))) + { + if (rc == NE_XFLM_TRANS_ACTIVE) + { + if (eTransType != m_pDb->getTransType()) + { + if (RC_BAD( rc = m_pDb->transCommit())) + { + (void)m_pDb->transAbort(); + } + + if (RC_BAD( rc = m_pDb->transBegin( eTransType))) + { + goto Exit; + } + } + } + else + { + goto Exit; + } + rc = NE_XFLM_OK; + } + +Exit: + + return rc; +} + + +/*============================================================================= +Desc: +=============================================================================*/ +RCODE F_DomEditor::commitTransaction( void) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + return rc; +} + +/*============================================================================= +Desc: +=============================================================================*/ +RCODE F_DomEditor::abortTransaction( void) +{ + RCODE rc = NE_XFLM_OK; + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + if (RC_BAD( rc = m_pDb->transAbort())) + { + goto Exit; + } + // We need to clear the Abort status flag in the pDb. + if (RC_BAD( rc = m_pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + if (RC_BAD( rc = m_pDb->transCommit())) + { + goto Exit; + } + } + +Exit: + + return rc; +} + + + +/*============================================================================= +Desc: Utility method to make sure the row and document list docId match. +=============================================================================*/ +void F_DomEditor::checkDocument( + DME_ROW_INFO ** ppDocRow, + DME_ROW_INFO * pRow + ) +{ + DME_ROW_INFO * pDocList = *ppDocRow; + + // If there is no current row, then we don't care about the document list. + if (!pRow) + { + return; + } + + if (!pDocList) + { + return; + } + + // If they already match, no need to test. + if ( pRow->ui64DocId == pDocList->ui64DocId) + { + return; + } + + // Start from the beginning. + pDocList = m_pDocList; + while (pDocList && pRow->ui64DocId != pDocList->ui64DocId) + { + pDocList = pDocList->pNext; + } + + flmAssert( pDocList); + flmAssert( pDocList->ui64DocId == pRow->ui64DocId); + + *ppDocRow = pDocList; + +} + + +/*============================================================================= +Desc: Method to ask the user to select which tag they want. +=============================================================================*/ +RCODE F_DomEditor::selectTag( + FLMUINT uiTagType, + FLMUINT * puiTag, + FLMUINT * puiTermChar + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiFlags; + FLMUNICODE uzItemName[ 128]; + FLMUINT uiId; + FLMUINT uiNextPos; + F_DomEditor * pTagList = NULL; + FLMUINT uiDataType; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiTag) + { + *puiTag = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pTagList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pTagList->Setup( m_pScreen))) + { + goto Exit; + } + + pTagList->setParent( this); + pTagList->setReadOnly( TRUE); + pTagList->setShutdown( m_pbShutdown); + pTagList->setTitle( "Tag Names - Select One"); + pTagList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pTagList->setSource( m_pDb, XFLM_DICT_COLLECTION); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NODOM); + + + uiNextPos = 0; + while( RC_OK( rc = m_pNameTable->getNextTagTypeAndNameOrder( + uiTagType, &uiNextPos, uzItemName, NULL, sizeof( uzItemName), &uiId, &uiDataType))) + { + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], uiId, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pTagList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + } + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + + + // Set the start row. + pTagList->setCurrentAtTop(); + + + if( RC_BAD( rc = pTagList->interactiveEdit( + m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pTagList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pTagList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pTagList->setControlFlags( pTmpRow, uiFlags); + if( puiTag) + { + *puiTag = (FLMUINT)pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pTagList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pTagList->getLastKey(); + } + +Exit: + + if( pTagList) + { + pTagList->Release(); + pTagList = NULL; + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + return( rc); +} + +/**************************************************************************** +Name: selectEncDef +*****************************************************************************/ +RCODE F_DomEditor::selectEncDef( + FLMUINT uiTagType, + FLMUINT * puiEncDefId, + FLMUINT * puiTermChar) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiFlags; + FLMUNICODE uzItemName[ 128]; + FLMUNICODE uzNone[] = + {'<','N','o',' ','e','n','c','r','y','p','t','i','o','n','>','\0'}; + FLMUINT uiId; + FLMUINT uiNextPos; + F_DomEditor * pEncDefList = NULL; + FLMBOOL bFoundEncDef = FALSE; + + flmAssert( m_bSetupCalled == TRUE); + + if( puiEncDefId) + { + *puiEncDefId = 0; + } + + if( puiTermChar) + { + *puiTermChar = 0; + } + + + // Initialize the name table. + + + if( !m_pNameTable) + { + if( RC_BAD( rc = refreshNameTable())) + { + goto Exit; + } + } + + if( (pEncDefList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pEncDefList->Setup( m_pScreen))) + { + goto Exit; + } + + pEncDefList->setParent( this); + pEncDefList->setReadOnly( TRUE); + pEncDefList->setShutdown( m_pbShutdown); + pEncDefList->setTitle( "Encryption Algorithm Names - Select One"); + pEncDefList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pEncDefList->setSource( m_pDb, XFLM_DICT_COLLECTION); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NODOM); + + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzNone[0], 0, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pEncDefList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + uiNextPos = 0; + while( RC_OK( rc = m_pNameTable->getNextTagTypeAndNameOrder( + uiTagType, &uiNextPos, uzItemName, NULL, sizeof( uzItemName), &uiId))) + { + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], uiId, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pEncDefList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + bFoundEncDef = TRUE; + + } + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + + // Don't bother to ask if there are no encryption algorithms defined. + + if (!bFoundEncDef) + { + goto Exit; + } + + // Set the start row. + pEncDefList->setCurrentAtTop(); + + + if( RC_BAD( rc = pEncDefList->interactiveEdit( + m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pEncDefList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pEncDefList->getControlFlags( pTmpRow, &uiFlags); + if( uiFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pEncDefList->setControlFlags( pTmpRow, uiFlags); + if( puiEncDefId) + { + *puiEncDefId = (FLMUINT)pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pEncDefList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pEncDefList->getLastKey(); + } + +Exit: + + if( pEncDefList) + { + pEncDefList->Release(); + pEncDefList = NULL; + } + + if (pTmpRow) + { + releaseRow( &pTmpRow); + } + return( rc); +} + + +/**************************************************************************** +Name: selectIndex +Desc: Allows the user to interactively select an index +*****************************************************************************/ +RCODE F_DomEditor::selectIndex( + FLMUINT uiFlags, + FLMUINT * puiIndex, + FLMUINT * puiTermChar + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FLMUINT uiDispFlags; + F_DomEditor * pIndexList = NULL; + FLMUNICODE uzItemName[ 80]; + FLMUINT uiNextPos; + FLMUINT uiId; + + flmAssert( m_bSetupCalled == TRUE); + + *puiIndex = 0; + + if( puiTermChar) + { + *puiTermChar = 0; + } + + if( (pIndexList = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pIndexList->Setup( m_pScreen))) + { + goto Exit; + } + + pIndexList->setParent( this); + pIndexList->setReadOnly( TRUE); + pIndexList->setShutdown( m_pbShutdown); + pIndexList->setTitle( "Indexes - Select One"); + pIndexList->setKeyHook( F_DomEditorSelectionKeyHook, 0); + pIndexList->setSource( m_pDb, XFLM_DICT_COLLECTION); + + if( m_pDb == NULL) + { + goto Exit; + } + + uiFlags = (F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_LIST_ITEM | F_DOMEDIT_FLAG_READ_ONLY | F_DOMEDIT_FLAG_NODOM); + + + asciiToUnicode( "Dictionary Number Index", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], XFLM_DICT_NUMBER_INDEX, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pIndexList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + asciiToUnicode( "Dictionary Name Index", &uzItemName[0]); + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], XFLM_DICT_NAME_INDEX, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pIndexList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + uiNextPos = 0; + while( RC_OK( rc = m_pNameTable->getNextTagTypeAndNameOrder( + ELM_INDEX_TAG, &uiNextPos, uzItemName, + NULL, sizeof( uzItemName), &uiId))) + { + if (RC_BAD( rc = makeNewRow( &pTmpRow, &uzItemName[0], uiId, TRUE))) + { + goto Exit; + } + + pTmpRow->uiFlags = uiFlags; + + if (RC_BAD( rc = pIndexList->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + + } + if (rc != NE_XFLM_EOF_HIT) + { + goto Exit; + } + rc = NE_XFLM_OK; + + + // Set the start row. + pIndexList->setCurrentAtTop(); + + if( RC_BAD( rc = pIndexList->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( (pTmpRow = pIndexList->getScrFirstRow()) == NULL) + { + goto Exit; + } + + while( pTmpRow) + { + pIndexList->getControlFlags( pTmpRow, &uiDispFlags); + if( uiDispFlags & F_DOMEDIT_FLAG_SELECTED) + { + uiDispFlags &= ~F_DOMEDIT_FLAG_SELECTED; + pIndexList->setControlFlags( pTmpRow, uiDispFlags); + if (puiIndex) + { + *puiIndex = (FLMUINT)pTmpRow->ui64NodeId; + } + pTmpRow = NULL; + break; + } + if (RC_BAD( rc = pIndexList->getNextRow( pTmpRow, &pTmpRow))) + { + goto Exit; + } + } + + if( puiTermChar) + { + *puiTermChar = pIndexList->getLastKey(); + } + + if( pIndexList->getLastKey() == WPK_ESCAPE) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + +Exit: + + if( pIndexList) + { + pIndexList->Release(); + pIndexList = NULL; + } + + return( rc); +} + + +/**************************************************************************** +Name: indexList +Desc: Allows listing of index keys (and references) by having the user + interactively build from and until keys. +*****************************************************************************/ +RCODE F_DomEditor::indexList( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiIndex; + FLMUINT uiTermChar; + F_DomEditor * pKeyEditor = NULL; + F_DataVector * pFromKeyV = NULL; + F_DataVector * pUntilKeyV = NULL; + F_DataVector * pFoundKeyV = NULL; + DME_ROW_INFO * pTmpRow = NULL; + DME_ROW_INFO * pPrevRow = NULL; + FTX_WINDOW * pStatusWindow = NULL; + FLMBYTE * pucUntilKeyBuf = NULL; + FLMBYTE * pucFoundKeyBuf = NULL; + FLMUINT uiUntilKeyLen; + FLMUINT uiFoundKeyLen; + FLMUINT uiSrchFlag; + FLMUINT uiKeyCount = 0; + FLMBOOL bNewKey; + FLMUINT uiElementNumber; + FLMINT iCmp; + FLMBOOL bFirst; + IXD * pIxd; + + flmAssert( m_bSetupCalled == TRUE); + + if( RC_BAD( rc = selectIndex( 0, &uiIndex, &uiTermChar))) + { + goto Exit; + } + + if( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + // Initialize the key editor + + if( (pKeyEditor = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pKeyEditor->Setup( m_pScreen))) + { + goto Exit; + } + + + // Configure the editor + + pKeyEditor->setParent( this); + pKeyEditor->setShutdown( m_pbShutdown); + pKeyEditor->setTitle( "Index List [ALT-Z to accept]"); + pKeyEditor->setSource( m_pDb, uiIndex); + pKeyEditor->setKeyHook( f_KeyEditorKeyHook, NULL); + pKeyEditor->setDisplayHook( f_IndexRangeDispHook, NULL); + + // Begin a transaction + if (RC_BAD( rc = beginTransaction( XFLM_READ_TRANS))) + { + goto Exit; + } + + if (RC_BAD( rc = m_pDb->getDict()->getIndex( uiIndex, NULL, &pIxd, TRUE))) + { + goto Exit; + } + + // Get the first key in the index + + if( (pFromKeyV = f_new F_DataVector) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pFromKeyV->reset(); + + if (RC_BAD( rc = m_pDb->keyRetrieve( uiIndex, NULL, XFLM_FIRST, pFromKeyV))) + { + goto Exit; + } + + if( RC_BAD( rc = pKeyEditor->addComment( + &pPrevRow, "This is the first key in the index"))) + { + goto Exit; + } + + pPrevRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + uiElementNumber = 0; + while( pFromKeyV->getNameId( uiElementNumber)) + { + if (RC_BAD( rc = setupIndexRow( + pFromKeyV, uiElementNumber, uiIndex, F_DOMEDIT_FLAG_KEY_FROM, &pTmpRow))) + { + goto Exit; + } + + pTmpRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + uiElementNumber++; + + if (RC_BAD( rc = pKeyEditor->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + } + pFromKeyV = NULL; // Vector is now in the row list + + + + // Get the last key in the index + + if( (pUntilKeyV = f_new F_DataVector) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + pUntilKeyV->reset(); + + if (RC_BAD( rc = m_pDb->keyRetrieve( uiIndex, NULL, XFLM_LAST, pUntilKeyV))) + { + goto Exit; + } + + if( RC_BAD( rc = pKeyEditor->addComment( + &pPrevRow, "This is the last key in the index"))) + { + goto Exit; + } + + pPrevRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + uiElementNumber = 0; + while ( pUntilKeyV->getNameId( uiElementNumber)) + { + if (RC_BAD( rc = setupIndexRow( + pUntilKeyV, uiElementNumber, uiIndex, F_DOMEDIT_FLAG_KEY_UNTIL, &pTmpRow))) + { + goto Exit; + } + + pTmpRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + uiElementNumber++; + + if (RC_BAD( rc = pKeyEditor->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + } + pUntilKeyV = NULL; // Vector is now in the row list. + + // Show the keys and allow them to be edited + pKeyEditor->setCurrentAtTop(); + +ix_list_retry: + + // Allow the user to edit the from / until keys before we start to look for the + // keys in the index. + if( RC_BAD( rc = pKeyEditor->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + + if( pKeyEditor->getLastKey() == WPK_ESCAPE) + { + goto Exit; + } + + if( RC_BAD( rc = createStatusWindow( + " Key Retrieval Status (Press ESC to Interrupt) ", + WPS_GREEN, WPS_WHITE, NULL, NULL, &pStatusWindow))) + { + goto Exit; + } + FTXWinOpen( pStatusWindow); + + // Get the FROM Key. It may have been changed, so we need to rebuild the vector and do + // another keyRetrieve in order to get the search parameters setup. The display function will + // make sure that any new value will be stored in the puzValue buffer. + + + pTmpRow = pKeyEditor->m_pScrFirstRow; + + while (pTmpRow) + { + if (pTmpRow->uiFlags & F_DOMEDIT_FLAG_KEY_FROM) + { + pFromKeyV = pTmpRow->pVector; + pTmpRow->pVector = NULL; + break; + } + pTmpRow = pTmpRow->pNext; + } + + flmAssert( pTmpRow); + + + // Get the UNTIL Key. It may have been changed, so we need to rebuild the vector and do + // another keyRetrieve in order to get the search parameters setup. + + pTmpRow = pKeyEditor->m_pScrFirstRow; + + while (pTmpRow) + { + if (pTmpRow->uiFlags & F_DOMEDIT_FLAG_KEY_UNTIL) + { + pUntilKeyV = pTmpRow->pVector; + pTmpRow->pVector = NULL; + break; + } + pTmpRow = pTmpRow->pNext; + } + + flmAssert( pTmpRow); + + pKeyEditor->releaseAllRows(); + + bNewKey = TRUE; + + // Allocate key buffers for the from & until key so we + // can do comparisons. + + if (RC_BAD( rc = f_calloc( MAX_KEY_SIZ * 2, &pucUntilKeyBuf))) + { + goto Exit; + } + pucFoundKeyBuf = &pucUntilKeyBuf [MAX_KEY_SIZ]; + + // Get the collated until key. + + if (RC_BAD( rc = pUntilKeyV->outputKey( + m_pDb, uiIndex, 0, pucUntilKeyBuf, MAX_KEY_SIZ, &uiUntilKeyLen))) + { + goto Exit; + } + + // Read the keys + uiSrchFlag = XFLM_INCL | XFLM_MATCH_IDS; + + pTmpRow = pPrevRow = NULL; + bFirst = TRUE; + + while( !isExiting()) + { + + // Update the display + + FTXWinSetCursorPos( pStatusWindow, 0, 1); + FTXWinPrintf( pStatusWindow, "Keys Retrieved : %u", + (unsigned)uiKeyCount); + FTXWinClearToEOL( pStatusWindow); + FTXWinSetCursorPos( pStatusWindow, 0, 2); + FTXWinClearToEOL( pStatusWindow); + + // Test for the escape key + + if( FTXWinTestKB( pStatusWindow) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + FTXWinInputChar( pStatusWindow, &uiChar); + if( uiChar == WPK_ESCAPE) + { + break; + } + } + + if ((pFoundKeyV = f_new F_DataVector) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + // Look for the next key. + if (RC_BAD( rc = m_pDb->keyRetrieve( + uiIndex, pFromKeyV, uiSrchFlag, pFoundKeyV))) + { + if (rc == NE_XFLM_EOF_HIT) + { + break; + } + goto Exit; + } + + uiSrchFlag = XFLM_EXCL | XFLM_MATCH_IDS; + + // See if we have gone past the until key. + + if (RC_BAD( rc = pFoundKeyV->outputKey( + m_pDb, uiIndex, 0, pucFoundKeyBuf, MAX_KEY_SIZ, &uiFoundKeyLen))) + { + goto Exit; + } + + if (RC_BAD( rc = ixKeyCompare( m_pDb, pIxd, NULL, NULL, NULL, + TRUE, TRUE, pucFoundKeyBuf, uiFoundKeyLen, + pucUntilKeyBuf, uiUntilKeyLen, &iCmp))) + { + goto Exit; + } + if (iCmp > 0) + { + break; + } + uiKeyCount++; + + // Make a separator row to display the key number + if (RC_BAD( rc = pKeyEditor->addComment( + &pPrevRow, "Key [%,10u] Document [%,10I64u]", + uiKeyCount, pFoundKeyV->getDocumentID()))) + { + goto Exit; + } + + pPrevRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + // Display the key. + + uiElementNumber = 0; + while (pFoundKeyV->getNameId( uiElementNumber)) + { + if (RC_BAD( rc = setupIndexRow( pFoundKeyV, uiElementNumber, uiIndex, 0, &pTmpRow))) + { + goto Exit; + } + + pTmpRow->uiFlags |= F_DOMEDIT_FLAG_LIST_ITEM; + + uiElementNumber++; + + if (RC_BAD( rc = pKeyEditor->insertRow( pTmpRow, pPrevRow))) + { + goto Exit; + } + pPrevRow = pTmpRow; + pTmpRow = NULL; + } + if (bFirst) + { + pFromKeyV->Release(); + bFirst = FALSE; + } + pFromKeyV = pFoundKeyV; + pFoundKeyV = NULL; + + f_yieldCPU(); +#ifdef FLM_WIN + f_sleep( 0); +#endif + } + + pFromKeyV = NULL; // s/b in a row + if ( pFoundKeyV) + { + pFoundKeyV->Release(); + pFoundKeyV = NULL; + } + + FTXWinFree( &pStatusWindow); + + /* + Display the results + */ + + pKeyEditor->setCurrentAtTop(); + + if( uiKeyCount) + { + pKeyEditor->setTitle( "Index List"); + pKeyEditor->setKeyHook( f_ViewOnlyKeyHook, 0); + if( RC_BAD( rc = pKeyEditor->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY))) + { + goto Exit; + } + } + else + { + displayMessage( + "No Keys Found Within Specified Range", rc, + NULL, WPS_RED, WPS_WHITE); + pKeyEditor->setCurrentAtTop(); + goto ix_list_retry; + } + + if (RC_BAD( rc = commitTransaction())) + { + goto Exit; + } + +Exit: + + f_free( &pucUntilKeyBuf); + + if( pFromKeyV) + { + pFromKeyV->Release(); + } + + if( pFoundKeyV) + { + pFoundKeyV->Release(); + } + + if( pUntilKeyV) + { + pUntilKeyV->Release(); + } + + if( pStatusWindow) + { + FTXWinFree( &pStatusWindow); + } + + if( pKeyEditor) + { + pKeyEditor->releaseAllRows(); + pKeyEditor->Release(); + } + + if( RC_BAD( rc)) + { + if( rc == NE_XFLM_EOF_HIT) + { + displayMessage( "The index is empty", rc, + NULL, WPS_RED, WPS_WHITE); + rc = NE_XFLM_OK; + } + } + + if (m_pDb->getTransType() != XFLM_NO_TRANS) + { + (void)abortTransaction(); + } + + return( rc); +} + +typedef struct QueryDataTag +{ + FLMUINT uiCollection; + FLMUINT uiNodeArraySize; + FLMUINT64 * pui64Nodes; + FLMUINT * puiAttrNameIds; + FLMUINT uiNodeCount; + FLMUINT uiCurrNode; + FLMBOOL bShowingData; + char * pszQuery; +} QUERY_DATA; + +/**************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC RCODE f_QueryEditorKeyHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO *, // pCurRow, + FLMUINT uiKeyIn, + FLMUINT * puiKeyOut, + void * pvKeyData + ) +{ + RCODE rc = NE_XFLM_OK; + char szTitle [100]; + QUERY_DATA * pQueryData = (QUERY_DATA *)pvKeyData; + + switch (uiKeyIn) + { + case 'P': + case 'p': + if (pQueryData->uiCurrNode) + { + pQueryData->uiCurrNode--; + } + +Retrieve_Node: + *puiKeyOut = 0; + + // Clear the current buffer. + + pDomEditor->setScrFirstRow( NULL); + pDomEditor->setCurrentRow(NULL, 0); + if (RC_BAD( rc = pDomEditor->retrieveNodeFromDb( pQueryData->uiCollection, + pQueryData->pui64Nodes [pQueryData->uiCurrNode], + pQueryData->puiAttrNameIds [pQueryData->uiCurrNode]))) + { + goto Exit; + } + f_sprintf( szTitle, "#%u of %u (N,SPACE=Next, P=Prev)", + (unsigned)(pQueryData->uiCurrNode + 1), + (unsigned)pQueryData->uiNodeCount); + pDomEditor->setTitle( szTitle); + + // Show the query + + if (!pQueryData->bShowingData) + { + FTX_WINDOW * pStatusWin = pDomEditor->getStatusWindow(); + + FTXWinSetCursorPos( pStatusWin, 0, 1); + FTXWinPrintf( pStatusWin, "%s", pQueryData->pszQuery); + pQueryData->bShowingData = TRUE; + } + break; + + case 'n': + case 'N': + case ' ': + if (pQueryData->uiCurrNode < pQueryData->uiNodeCount - 1) + { + pQueryData->uiCurrNode++; + } + goto Retrieve_Node; + + case 'F': + case 'f': + case WPK_ALT_F: + + // Don't allow to start another query. + *puiKeyOut = 0; + break; + + default: + + *puiKeyOut = uiKeyIn; + if (!pQueryData->bShowingData) + { + goto Retrieve_Node; + } + + break; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: Allows doing a query and having the user expand the results of + each returned node. +*****************************************************************************/ +void F_DomEditor::doQuery( + char * pszQuery, + FLMUINT uiQueryBufSize + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCollection; + FLMUINT uiTermChar; + IF_Query * pQuery = NULL; + F_XPath xpath; + IF_DOMNode * pNode = NULL; + FLMBOOL bStartedTrans = FALSE; + XFLM_OPT_INFO * pOptInfo; + FLMUINT uiOptInfoCnt; + EditQueryStatus queryStatus; + F_DomEditor * pQueryEditor = NULL; + QUERY_DATA QueryData; + FLMUINT uiQueryStrLen; + F_DbSystem dbSystem; + + f_memset( &QueryData, 0, sizeof( QueryData)); + + requestInput( + "XPATH Query", pszQuery, uiQueryBufSize, &uiTermChar); + + if (uiTermChar == WPK_ESCAPE) + { + goto Exit; + } + + // Create a window for displaying query progress. + + queryStatus.createQueryStatusWindow( m_pScreen, + WPS_GREEN, WPS_WHITE, pszQuery); + + if (RC_BAD( rc = dbSystem.createIFQuery( &pQuery))) + { + goto Exit; + } + + if( RC_BAD( rc = beginTransaction( XFLM_READ_TRANS))) + { + goto Exit; + } + + bStartedTrans = TRUE; + + if (RC_BAD( rc = selectCollection( &uiCollection, &uiTermChar))) + { + goto Exit; + } + + if (uiTermChar != WPK_ENTER) + { + goto Exit; + } + + m_uiCollection = uiCollection; + + if (RC_BAD( rc = pQuery->setCollection( uiCollection))) + { + goto Exit; + } + + if (RC_BAD( rc = xpath.parseQuery( m_pDb, pszQuery, pQuery))) + { + goto Exit; + } + + pQuery->setQueryStatusObject( &queryStatus); + for (;;) + { + if (RC_BAD( rc = pQuery->getNext( m_pDb, &pNode, 0, 0))) + { + if (rc == NE_XFLM_EOF_HIT || rc == NE_XFLM_USER_ABORT) + { + if (!queryStatus.keepResults()) + { + QueryData.uiNodeCount = 0; + } + rc = NE_XFLM_OK; + break; + } + goto Exit; + } + + // Save the node ID - don't collect any more than 30000 + + if (QueryData.uiNodeCount < 30000) + { + if (QueryData.uiNodeCount == QueryData.uiNodeArraySize) + { + FLMUINT uiNewSize = QueryData.uiNodeArraySize + 500; + + if (RC_BAD( rc = f_realloc( sizeof( FLMUINT64) * uiNewSize, + &QueryData.pui64Nodes))) + { + goto Exit; + } + if (RC_BAD( rc = f_realloc( sizeof( FLMUINT) * uiNewSize, + &QueryData.puiAttrNameIds))) + { + goto Exit; + } + QueryData.uiNodeArraySize = uiNewSize; + } + + if (pNode->getNodeType() == ATTRIBUTE_NODE) + { + if( RC_BAD( rc = pNode->getParentId( m_pDb, + &QueryData.pui64Nodes [QueryData.uiNodeCount]))) + { + goto Exit; + } + if (RC_BAD( rc = pNode->getNameId( m_pDb, + &QueryData.puiAttrNameIds [QueryData.uiNodeCount]))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = pNode->getNodeId( m_pDb, + &QueryData.pui64Nodes [QueryData.uiNodeCount]))) + { + goto Exit; + } + QueryData.puiAttrNameIds [QueryData.uiNodeCount] = 0; + } + + QueryData.uiNodeCount++; + } + } + + if (RC_BAD( rc = pQuery->getStatsAndOptInfo( &uiOptInfoCnt, + &pOptInfo))) + { + goto Exit; + } + + if (uiOptInfoCnt) + { + FLMUINT uiOptToShow = 0; + FLMUINT uiChar; + + for (;;) + { + queryStatus.refreshStatus( TRUE, uiOptToShow + 1, + uiOptInfoCnt, &pOptInfo [uiOptToShow]); + queryStatus.testEscape( uiOptInfoCnt, &uiChar); + if (uiChar == 'N' || uiChar == 'n') + { + if (uiOptToShow < uiOptInfoCnt - 1) + { + uiOptToShow++; + } + } + else if (uiChar == 'P' || uiChar == 'P') + { + if (uiOptToShow) + { + uiOptToShow--; + } + } + else if (uiChar == ' ') + { + if (uiOptToShow < uiOptInfoCnt - 1) + { + uiOptToShow++; + } + else + { + break; + } + } + else + { + break; + } + } + dbSystem.freeMem( (void **)&pOptInfo); + } + if (!QueryData.uiNodeCount) + { + goto Exit; + } + + // Initialize the query editor + + if ((pQueryEditor = f_new F_DomEditor) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if (RC_BAD( rc = pQueryEditor->Setup( m_pScreen))) + { + goto Exit; + } + + // Configure the editor + + QueryData.uiCollection = uiCollection; + pQueryEditor->setParent( this); + pQueryEditor->setShutdown( m_pbShutdown); + pQueryEditor->setSource( m_pDb, uiCollection); + + uiQueryStrLen = f_strlen( pszQuery); + if (RC_BAD( rc = f_alloc( uiQueryStrLen + 1, &QueryData.pszQuery))) + { + goto Exit; + } + f_memcpy( QueryData.pszQuery, pszQuery, uiQueryStrLen + 1); + + if (uiQueryStrLen > 75) + { + f_strcpy( &QueryData.pszQuery [72], "..."); + } + pQueryEditor->setKeyHook( f_QueryEditorKeyHook, &QueryData); + if (RC_BAD( rc = pQueryEditor->interactiveEdit( m_uiULX, m_uiULY, m_uiLRX, m_uiLRY, + TRUE, 2, (FLMUINT)'P'))) + { + goto Exit; + } + +Exit: + + if (RC_BAD( rc)) + { + displayMessage( "Error", RC_SET( rc), + NULL, WPS_RED, WPS_WHITE); + } + + if (pQuery) + { + pQuery->Release(); + } + + if (pNode) + { + pNode->Release(); + } + + if (bStartedTrans) + { + commitTransaction(); + } + + if (pQueryEditor) + { + pQueryEditor->releaseAllRows(); + pQueryEditor->Release(); + } + + if (QueryData.pui64Nodes) + { + f_free( &QueryData.pui64Nodes); + } + if (QueryData.puiAttrNameIds) + { + f_free( &QueryData.puiAttrNameIds); + } + if (QueryData.pszQuery) + { + f_free( &QueryData.pszQuery); + } +} + +/**************************************************************************** +Desc: Adds a comment line subordinate to the current node +*****************************************************************************/ +RCODE F_DomEditor::addComment( + DME_ROW_INFO ** ppCurRow, + const char * pszFormat, ... + ) +{ + RCODE rc = NE_XFLM_OK; + char szBuffer[ 512]; + FLMUNICODE * puzValue = NULL; + DME_ROW_INFO * pCommentRow = NULL; + f_va_list args; + + flmAssert( m_bSetupCalled == TRUE); + + f_va_start( args, pszFormat); + f_vsprintf( szBuffer, pszFormat, &args); + f_va_end( args); + + if (RC_BAD( rc = f_calloc( (f_strlen(szBuffer) * 2) + 2, &puzValue))) + { + goto Exit; + } + + asciiToUnicode( szBuffer, puzValue); + + if (RC_BAD( rc = makeNewRow( &pCommentRow, puzValue, 0, TRUE))) + { + goto Exit; + } + + pCommentRow->uiFlags = F_DOMEDIT_FLAG_COMMENT | F_DOMEDIT_FLAG_READ_ONLY | + F_DOMEDIT_FLAG_HIDE_LEVEL | F_DOMEDIT_FLAG_NO_DELETE | F_DOMEDIT_FLAG_HIDE_EXPAND | + F_DOMEDIT_FLAG_NOPARENT | F_DOMEDIT_FLAG_NOCHILD | F_DOMEDIT_FLAG_NODOM; + + if (RC_BAD( rc = insertRow( pCommentRow, *ppCurRow))) + { + goto Exit; + } + + // Return the new row. + *ppCurRow = pCommentRow; + +Exit: + + f_free( &puzValue); + + return( rc); +} + +/*============================================================================= +Desc: +=============================================================================*/ +FSTATIC RCODE setupIndexRow( + F_DataVector * pKey, + FLMUINT uiElementNumber, + FLMUINT uiIndex, + FLMUINT uiFlag, + DME_ROW_INFO ** ppTmpRow + ) +{ + RCODE rc = NE_XFLM_OK; + DME_ROW_INFO * pTmpRow = NULL; + + // Create the new row. Make sure we indicate that the display value comes + // from the local buffer. + if (RC_BAD( rc = makeNewRow( + &pTmpRow, NULL, 0, FALSE))) + { + goto Exit; + } + + pTmpRow->pVector = pKey; + + // uiIndex represents the current element number in the vector when there is + // a vector present. + pTmpRow->uiIndex = uiIndex; + pTmpRow->uiElementNumber = uiElementNumber; + pTmpRow->pDomNode = NULL; + + pTmpRow->uiFlags = uiFlag | F_DOMEDIT_FLAG_HIDE_EXPAND | F_DOMEDIT_FLAG_NODOM; + + *ppTmpRow = pTmpRow; + +Exit: + + return rc; +} + +/*============================================================================= +Desc: Function to display the Index range selection page +=============================================================================*/ +FSTATIC RCODE f_IndexRangeDispHook( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiFlags = 0; + + if( !pRow) + { + goto Exit; + } + + pDomEditor->getControlFlags( pRow, &uiFlags); + + // Are we just displaying a value, or should we use the Vector? + if (pRow->bUseValue && uiFlags & F_DOMEDIT_FLAG_COMMENT) + { + rc = formatRow( pDomEditor, pRow, puiNumVals, uiFlags); + } + else + { + flmAssert( pRow->pVector); + flmAssert( !pRow->pDomNode); + if (RC_BAD( rc = formatIndexKeyNode( pDomEditor, pRow, puiNumVals))) + { + goto Exit; + } + } + + +Exit: + // Don't hold on to the Dom node when finished. + if ( pRow->pDomNode) + { + pRow->pDomNode->Release(); + pRow->pDomNode = NULL; + } + return( rc); +} + + +/*============================================================================= +Desc: Function to format the presentation of an index key row +=============================================================================*/ +FSTATIC RCODE formatIndexKeyNode( + F_DomEditor * pDomEditor, + DME_ROW_INFO * pRow, + FLMUINT * puiNumVals + ) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiCol = 4; + DME_DISP_COLUMN * pDispVals; + FLMUNICODE * puzBuffer = NULL; + FLMUINT uiBufSize; + F_DataVector * pVector = pRow->pVector; + FLMUINT uiElementNumber = pRow->uiElementNumber; + FLMUINT uiLen; + char * pszData = NULL; + FLMUINT64 ui64NodeId; + + flmAssert( pVector); + + if ((pDispVals = pDomEditor->getDispColumns()) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + f_memset( pDispVals, 0, sizeof(DME_DISP_COLUMN)); + + + // Prepare the key component number + if (pVector->isDataComponent( uiElementNumber)) + { + f_sprintf( pDispVals[ *puiNumVals].szString, "D)"); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_CYAN; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, "K)"); + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_CYAN; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + } + + // Get the name for this element + if (RC_BAD( rc = pDomEditor->getNodeName( pRow->pDomNode, pRow, &puzBuffer, &uiBufSize))) + { + goto Exit; + } + + if (RC_BAD( rc = unicodeToAscii( + puzBuffer, pDispVals[ *puiNumVals].szString, uiBufSize))) + { + goto Exit; + } + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_WHITE; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + + // Get the current value in the buffer or display the one in the buffer. + switch( pVector->getDataType( uiElementNumber)) + { + case XFLM_NODATA_TYPE: + { + f_sprintf( pDispVals[ *puiNumVals].szString, "no data"); + break; + } + case XFLM_TEXT_TYPE: + { + + if ((uiLen = pVector->getDataLength( uiElementNumber)) > 0) + { + if (RC_BAD( rc = f_calloc( uiLen, &pszData))) + { + goto Exit; + } + + if (RC_BAD( rc = pVector->getUTF8( uiElementNumber, + (FLMBYTE *)pszData, &uiLen))) + { + goto Exit; + } + f_sprintf( pDispVals[ *puiNumVals].szString, "%*s", uiLen, pszData); + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, ""); + } + break; + } + case XFLM_NUMBER_TYPE: + { + FLMUINT64 ui64Num; + if (RC_BAD( rc = pVector->getUINT64( uiElementNumber, &ui64Num))) + { + if (rc == NE_XFLM_NOT_FOUND) + { + f_sprintf( pDispVals[ *puiNumVals].szString, ""); + rc = NE_XFLM_OK; + } + else + { + goto Exit; + } + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, "0x%I64x", ui64Num); + } + break; + } + case XFLM_BINARY_TYPE: + { + if ((uiLen = pVector->getDataLength( uiElementNumber)) > 0) + { + f_sprintf( pDispVals[ *puiNumVals].szString, "binary data type, len=%u", + (unsigned)uiLen); + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, ""); + } + break; + } + case XFLM_UNKNOWN_TYPE: + { + f_sprintf( pDispVals[ *puiNumVals].szString, "unknown data type"); + break; + } + default: + { + f_sprintf( pDispVals[ *puiNumVals].szString, "invalid data type"); + break; + } + } + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_YELLOW; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + + // Display the node Id. + if ((ui64NodeId = pVector->getID( uiElementNumber)) != 0) + { + f_sprintf( pDispVals[ *puiNumVals].szString, "%I64u", ui64NodeId); + } + else + { + f_sprintf( pDispVals[ *puiNumVals].szString, "NULL"); + } + pDispVals[ *puiNumVals].uiCol = uiCol; + pDispVals[ *puiNumVals].uiForeground = WPS_LIGHTRED; + pDispVals[ *puiNumVals].uiBackground = pDomEditor->isMonochrome() ? WPS_BLACK : WPS_BLUE; + uiCol += f_strlen( pDispVals[ *puiNumVals].szString) + 2; + (*puiNumVals)++; + + // Check to make sure that the node value does not push the nodeId off the screen. + if (uiCol > pDomEditor->getCanvasCols()) + { + FLMUINT uiValueIdx = *puiNumVals - 2; + FLMUINT uiStrOfs = f_strlen(pDispVals[ uiValueIdx].szString) - + (uiCol - pDomEditor->getCanvasCols()); + + f_sprintf(( char *)&pDispVals[ uiValueIdx].szString[ uiStrOfs], "... "); + + + } + +Exit: + + f_free( &puzBuffer); + f_free( &pszData); + + return rc; +} + +/*============================================================================= +Desc: Release all of the rows in the editor. +=============================================================================*/ +void F_DomEditor::releaseAllRows() +{ + + if (m_pScrFirstRow) + { + + // Make sure we release from the very beginning of the list of rows. + // If we do backup, we must increment the row count for each row + while (m_pScrFirstRow->pPrev != NULL) + { + m_pScrFirstRow = m_pScrFirstRow->pPrev; + } + + while (m_pScrFirstRow) + { + releaseRow( &m_pScrFirstRow); + } + m_uiNumRows = 0; + m_pScrLastRow = m_pCurRow = m_pScrFirstRow; + } +} + +/*============================================================================= +Desc: Deletes an entry from an index. +=============================================================================*/ +RCODE F_DomEditor::viewOnlyDeleteIxKey( void ) +{ + RCODE rc = NE_XFLM_OK; + F_DataVector * pVector = NULL; + DME_ROW_INFO * pFirstDeleteRow = NULL; + FLMBYTE pucKeyBuf[ MAX_KEY_SIZ]; + FLMUINT uiKeyLen; + F_Btree * pBtree = NULL; + IXD * pIxd; + LFILE * pLFile = NULL; + F_Dict * pDict; + FLMBOOL bHaveCounts; + FLMBOOL bHaveData; + FLMBOOL bUpdateTranStarted = FALSE; + IXKeyCompare compareObject; + + if (m_pCurRow->pVector) + { + // Return NE_XFLM_OK; + goto Exit; + } + + if (!m_pCurRow->pNext) + { + // return NE_XFLM_OK; + goto Exit; + } + + if (m_pCurRow->pVector) + { + // return NE_XFLM_OK; + goto Exit; + } + + if ((m_pCurRow->uiFlags & F_DOMEDIT_FLAG_COMMENT) == 0) + { + // return NE_XFLM_OK; + goto Exit; + } + + + // Mark the first row to delete. + + pFirstDeleteRow = m_pCurRow; + + // Get the vector + + pVector = m_pCurRow->pNext->pVector; + + if (!pVector) + { + flmAssert( 0); + goto Exit; + } + + // Start an update transaction. + if (RC_BAD( rc = beginTransaction( XFLM_UPDATE_TRANS))) + { + goto Exit; + } + bUpdateTranStarted = TRUE; + + // Get the index, then open a btree to the index. + if (RC_BAD( rc = m_pDb->getDictionary( &pDict))) + { + goto Exit; + } + + if (RC_BAD( rc = pVector->outputKey( m_pDb, + m_pCurRow->pNext->uiIndex, + 0, + pucKeyBuf, + MAX_KEY_SIZ, + &uiKeyLen))) + { + goto Exit; + } + + + if (RC_BAD( rc = pDict->getIndex( m_pCurRow->pNext->uiIndex, + &pLFile, + &pIxd))) + { + goto Exit; + } + + compareObject.setIxInfo( m_pDb, pIxd); + compareObject.setCompareNodeIds( TRUE); + compareObject.setCompareDocId( TRUE); + compareObject.setSearchKey( NULL); + + if ((pBtree = f_new F_Btree) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + bHaveCounts = (pIxd->uiFlags & IXD_ABS_POS) ? TRUE : FALSE; + bHaveData = (pIxd->pFirstData) ? TRUE : FALSE; + + if (RC_BAD( rc = pBtree->btOpen( m_pDb, + pLFile, + bHaveCounts, + bHaveData, &compareObject))) + { + goto Exit; + } + + + // Delete the key. + if (RC_BAD( rc = pBtree->btRemoveEntry( pucKeyBuf, + MAX_KEY_SIZ, + uiKeyLen))) + { + goto Exit; + } + + // Now remove the rows from the display list. Release the comment row. + releaseRow( &m_pCurRow); + m_uiNumRows--; + + while (m_pCurRow && + (m_pCurRow->uiFlags & F_DOMEDIT_FLAG_COMMENT) == 0) + { + releaseRow( &m_pCurRow); + m_uiNumRows--; + } + + +Exit: + + if (pBtree) + { + pBtree->btClose(); + pBtree->Release(); + } + + if (bUpdateTranStarted) + { + if (RC_OK( rc)) + { + if (RC_BAD( rc = commitTransaction())) + { + (void) abortTransaction(); + } + } + else + { + (void) abortTransaction(); + } + } + + return rc; +} + + diff --git a/version5/util/flm_dlst.cpp b/version5/util/flm_dlst.cpp new file mode 100644 index 0000000..b1534e8 --- /dev/null +++ b/version5/util/flm_dlst.cpp @@ -0,0 +1,731 @@ +//------------------------------------------------------------------------------ +// Desc: Dynamic, interactive list manager +// +// 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: flm_dlst.cpp 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "ftx.h" +#include "flm_dlst.h" +#include "sharutil.h" + +/**************************************************************************** +Name: N/A +Desc: Default constructor +*****************************************************************************/ +F_DynamicList::F_DynamicList( void) +{ + m_pFirst = NULL; + m_pLast = NULL; + m_pCur = NULL; + m_pListWin = NULL; + m_uiRow = 0; + m_uiListRows = 0; + m_uiListCols = 0; + m_bChanged = TRUE; + m_bShowHorizontalSelector = TRUE; +} + +/**************************************************************************** +Name: N/A +Desc: Default destructor +*****************************************************************************/ +F_DynamicList::~F_DynamicList( void) +{ + DLIST_NODE * pTmp = m_pFirst; + DLIST_NODE * pTmp2; + + while( pTmp) + { + pTmp2 = pTmp; + pTmp = pTmp->pNext; + freeNode( pTmp2); + } + + if( m_pListWin) + { + FTXWinFree( &m_pListWin); + } +} + +/**************************************************************************** +Name: setup +Desc: Allocates the list window and prepares the object for use +*****************************************************************************/ +RCODE F_DynamicList::setup( FTX_WINDOW * pInitializedWindow) +{ + RCODE rc = NE_XFLM_OK; + + flmAssert( pInitializedWindow != NULL); + m_pListWin = pInitializedWindow; + + /* + Create the list window + */ + FTXWinClear( m_pListWin); + + FTXWinSetCursorType( m_pListWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( m_pListWin, FALSE); + FTXWinSetLineWrap( m_pListWin, FALSE); + FTXWinGetCanvasSize( m_pListWin, &m_uiListCols, &m_uiListRows); + + return( rc); +} + +/**************************************************************************** +Name: insert +Desc: +*****************************************************************************/ +RCODE F_DynamicList::insert( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen) +{ + RCODE rc = NE_XFLM_OK; + DLIST_NODE * pTmp; + DLIST_NODE * pNew = NULL; + + if( getNode( uiKey) != NULL) + { + rc = RC_SET( NE_XFLM_EXISTS); + goto Exit; + } + + // Allocate the new node + + if( RC_BAD( rc = f_alloc( sizeof( DLIST_NODE), &pNew))) + { + goto Exit; + } + + f_memset( pNew, 0, sizeof( DLIST_NODE)); + + // Set the members of the new node + + pNew->uiKey = uiKey; + + if( pDisplayHook) + { + pNew->pDispHook = pDisplayHook; + } + else + { + pNew->pDispHook = dlistDefaultDisplayHook; + } + + if( uiDataLen) + { + if( RC_BAD( rc = f_alloc( uiDataLen, &pNew->pvData))) + { + goto Exit; + } + + f_memcpy( pNew->pvData, pvData, uiDataLen); + pNew->uiDataLen = uiDataLen; + } + + // Find the insertion point + + if( !m_pFirst) + { + m_pFirst = m_pLast = m_pCur = pNew; + } + else + { + pTmp = m_pFirst; + while( pTmp && pTmp->uiKey < uiKey) + { + pTmp = pTmp->pNext; + } + + if( pTmp) + { + if( pTmp == m_pFirst) + { + pNew->pNext = m_pFirst; + m_pFirst->pPrev = pNew; + m_pFirst = pNew; + } + else + { + pNew->pNext = pTmp; + pNew->pPrev = pTmp->pPrev; + + if( pTmp->pPrev) + { + pTmp->pPrev->pNext = pNew; + } + + pTmp->pPrev = pNew; + } + } + else + { + // Insert at end + m_pLast->pNext = pNew; + pNew->pPrev = m_pLast; + m_pLast = pNew; + } + } + + m_bChanged = TRUE; + +Exit: + + if( RC_BAD( rc)) + { + if( pNew) + { + freeNode( pNew); + } + } + + return( rc); +} + +/**************************************************************************** +Name: update +Desc: +*****************************************************************************/ +RCODE F_DynamicList::update( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen) +{ + DLIST_NODE * pTmp; + RCODE rc = NE_XFLM_OK; + + if( (pTmp = getNode( uiKey)) == NULL) + { + rc = insert( uiKey, pDisplayHook, pvData, uiDataLen); + goto Exit; + } + + if( !pTmp->pvData || pTmp->uiDataLen != uiDataLen) + { + if( pTmp->pvData) + { + f_free( &pTmp->pvData); + pTmp->uiDataLen = 0; + } + + if( uiDataLen) + { + if( RC_BAD( rc = f_alloc( uiDataLen, &pTmp->pvData))) + { + goto Exit; + } + } + } + + if( uiDataLen) + { + f_memcpy( pTmp->pvData, pvData, uiDataLen); + pTmp->uiDataLen = uiDataLen; + } + + m_bChanged = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Name: remove +Desc: +*****************************************************************************/ +RCODE F_DynamicList::remove( + FLMUINT uiKey) +{ + DLIST_NODE * pTmp; + RCODE rc = NE_XFLM_OK; + + if( (pTmp = getNode( uiKey)) == NULL) + { + rc = RC_SET( NE_XFLM_NOT_FOUND); + goto Exit; + } + + if( pTmp->pPrev) + { + pTmp->pPrev->pNext = pTmp->pNext; + } + + if( pTmp->pNext) + { + pTmp->pNext->pPrev = pTmp->pPrev; + } + + if( m_pCur == pTmp) + { + if( pTmp->pNext) + { + m_pCur = pTmp->pNext; + } + else + { + m_pCur = pTmp->pPrev; + } + } + + if( m_pFirst == pTmp) + { + m_pFirst = pTmp->pNext; + } + + if( m_pLast == pTmp) + { + m_pLast = pTmp->pPrev; + if( m_uiRow) + { + m_uiRow--; + } + } + + freeNode( pTmp); + m_bChanged = TRUE; + +Exit: + + return( rc); +} + +/**************************************************************************** +Name: refresh +Desc: +*****************************************************************************/ +void F_DynamicList::refresh( void) +{ + DLIST_NODE * pTmp; + FLMUINT uiLoop; + FTX_SCREEN * pScreen = NULL; + + if( !m_bChanged) + { + return; + } + + if ( FTXWinGetScreen( m_pListWin, &pScreen) != FTXRC_SUCCESS) + { + flmAssert( 0); + goto Exit; + } + + FTXSetRefreshState( pScreen->pFtxInfo, TRUE); + pTmp = m_pCur; + uiLoop = m_uiRow; + while( pTmp && uiLoop > 0) + { + pTmp = pTmp->pPrev; + uiLoop--; + } + + if( !pTmp) + { + pTmp = m_pFirst; + } + + uiLoop = 0; + while( pTmp && uiLoop < m_uiListRows) + { + pTmp->pDispHook( m_pListWin, + (FLMBOOL)(pTmp == m_pCur ? TRUE : FALSE), + uiLoop, pTmp->uiKey, pTmp->pvData, pTmp->uiDataLen, this); + pTmp = pTmp->pNext; + uiLoop++; + f_yieldCPU(); + } + + if( uiLoop < m_uiListRows) + { + FTXWinClearXY( m_pListWin, 0, uiLoop); + } + FTXSetRefreshState( pScreen->pFtxInfo, FALSE); + m_bChanged = FALSE; +Exit: + ; +} + +/**************************************************************************** +Name: cursorUp +Desc: +*****************************************************************************/ +void F_DynamicList::cursorUp( void) +{ + if( m_pCur && m_pCur->pPrev) + { + m_pCur = m_pCur->pPrev; + if( m_uiRow > 0) + { + m_uiRow--; + } + m_bChanged = TRUE; + } +} + +/**************************************************************************** +Name: cursorDown +Desc: +*****************************************************************************/ +void F_DynamicList::cursorDown( void) +{ + if( m_pCur && m_pCur->pNext) + { + m_pCur = m_pCur->pNext; + if( m_uiRow < (m_uiListRows - 1)) + { + m_uiRow++; + } + m_bChanged = TRUE; + } +} + +/**************************************************************************** +Name: pageUp +Desc: +*****************************************************************************/ +void F_DynamicList::pageUp( void) +{ + FLMUINT uiLoop = 0; + DLIST_NODE * pTmp; + + while( m_pCur && uiLoop < m_uiListRows) + { + m_pCur = m_pCur->pPrev; + uiLoop++; + } + + if( m_pCur == NULL) + { + m_pCur = m_pFirst; + m_uiRow = 0; + } + else + { + pTmp = m_pCur->pPrev; + uiLoop = 0; + while( pTmp && uiLoop < m_uiRow) + { + pTmp = pTmp->pPrev; + uiLoop++; + } + + if( uiLoop < m_uiRow) + { + m_uiRow = uiLoop; + } + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: pageDown +Desc: +*****************************************************************************/ +void F_DynamicList::pageDown( void) +{ + DLIST_NODE * pOldCur = m_pCur; + FLMUINT uiLoop; + + if( m_pCur == m_pLast) + { + return; + } + + uiLoop = 0; + while( m_pCur && uiLoop < m_uiListRows) + { + m_pCur = m_pCur->pNext; + uiLoop++; + } + + if( m_pCur == NULL) + { + m_pCur = m_pLast; + while( m_pCur != pOldCur && m_uiRow < (m_uiListRows - 1)) + { + pOldCur = pOldCur->pNext; + m_uiRow++; + } + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: home +Desc: +*****************************************************************************/ +void F_DynamicList::home( void) +{ + m_pCur = m_pFirst; + m_uiRow = 0; + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: end +Desc: +*****************************************************************************/ +void F_DynamicList::end( void) +{ + DLIST_NODE * pTmp; + + pTmp = m_pCur = m_pLast; + m_uiRow = 0; + while( pTmp && m_uiRow < m_uiListRows) + { + pTmp = pTmp->pPrev; + m_uiRow++; + } + + if( m_uiRow) + { + m_uiRow--; + } + + m_bChanged = TRUE; +} + +/**************************************************************************** +Name: defaultKeyAction +Desc: +*****************************************************************************/ +void F_DynamicList::defaultKeyAction( + FLMUINT uiKey) +{ + switch( uiKey) + { + case WPK_UP: + cursorUp(); + break; + case WPK_DOWN: + cursorDown(); + break; + case WPK_PGUP: + pageUp(); + break; + case WPK_PGDN: + pageDown(); + break; + case WPK_HOME: + home(); + break; + case WPK_END: + end(); + break; + case 'd': + case 'D': + { + RCODE rc = NE_XFLM_OK; + rc = this->dumpToFile(); + break; + } + } + refresh(); +} + +/**************************************************************************** +Name: getNode +Desc: +*****************************************************************************/ +DLIST_NODE * F_DynamicList::getNode( + FLMUINT uiKey) +{ + DLIST_NODE * pTmp = m_pFirst; + + while( pTmp) + { + if( pTmp->uiKey == uiKey) + { + break; + } + pTmp = pTmp->pNext; + } + + return( pTmp); +} + +/**************************************************************************** +Name: freeNode +Desc: +*****************************************************************************/ +void F_DynamicList::freeNode( + DLIST_NODE * pNode) +{ + if( pNode->pvData) + { + f_free( &pNode->pvData); + f_free( &pNode); + } +} + +/**************************************************************************** +Name: dlistDefaultDisplayHook +Desc: +*****************************************************************************/ +RCODE dlistDefaultDisplayHook( + FTX_WINDOW * pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList) +{ + FLMUINT uiBack = WPS_CYAN; + FLMUINT uiFore = WPS_WHITE; + + F_UNREFERENCED_PARM( uiKey); + F_UNREFERENCED_PARM( uiDataLen); + + FTXWinSetCursorPos( pWin, 0, uiRow); + FTXWinClearToEOL( pWin); + FTXWinPrintf( pWin, "%s", + (FLMBYTE *) + (pvData ? + pvData : + //the following cast is required by gcc 2.96 + (FLMBYTE*)"Unknown")); + if( bSelected && pDynamicList->getShowHorizontalSelector()) + { + FTXWinPaintRow( pWin, &uiBack, &uiFore, uiRow); + } + return( NE_XFLM_OK); +} + +RCODE F_DynamicList::dumpToFile() +{ + DLIST_NODE * pTmp; + FLMUINT uiLoop; + IF_FileHdl * pFileHdl = NULL; + RCODE rc = NE_XFLM_OK; + F_FileSystem FileSystem; +#define DLST_RESP_SIZE 256 + char szResponse[ DLST_RESP_SIZE]; + FLMUINT uiTermChar; + FTX_SCREEN * pScreen; + + f_strcpy( szResponse, DLIST_DUMPFILE_PATH); + + FTXWinGetScreen( m_pListWin, &pScreen); + FTXGetInput( + pScreen, + "enter filename to dump to", + szResponse, + DLST_RESP_SIZE-1, + &uiTermChar); + + if ( uiTermChar != WPK_ENTER) + { + goto Exit; + } + + if (RC_BAD( rc = FileSystem.Exists( szResponse))) + { + //create file if it doesn't already exist + if ( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + rc = FileSystem.Create( szResponse, XFLM_IO_RDWR, &pFileHdl); + } + else + { + goto Exit_local; + } + } + else + { + rc = FileSystem.Open( szResponse, XFLM_IO_RDWR, &pFileHdl); + } + + TEST_RC_LOCAL( rc); + + { + FLMUINT64 ui64FileSize = 0; + FLMUINT uiBytesWritten = 0; + + //figure out size of file currently, so you can append to it + + pFileHdl->Size( &ui64FileSize); + pTmp = m_pFirst; + + uiLoop = 0; + while( pTmp) + { + FLMBYTE * pszNextLine = (FLMBYTE*)(pTmp->pvData); + + TEST_RC_LOCAL( rc = pFileHdl->Write( + ui64FileSize, //offset to current file size + f_strlen( pszNextLine), + pszNextLine, + &uiBytesWritten)); + + ui64FileSize += uiBytesWritten; + + TEST_RC_LOCAL( rc = pFileHdl->Write( + ui64FileSize, //add in newline + 1, + (FLMBYTE*)"\n", + &uiBytesWritten)); + + ui64FileSize += uiBytesWritten; + pTmp = pTmp->pNext; + } + + (void)pFileHdl->Close(); + + } + +Exit_local: + {//give success/fail message + + char szMessage[ 256]; + FLMUINT uiChar; + + FTXWinGetScreen( m_pListWin, &pScreen); + if ( RC_OK( rc)) + { + f_sprintf( szMessage, + "contents of focused list appended to %s", DLIST_DUMPFILE_PATH); + } + else + { + f_sprintf( szMessage, "error rc=%u dumping to file %s", + (unsigned)rc, DLIST_DUMPFILE_PATH); + } + FTXDisplayMessage( pScreen, WPS_RED, WPS_WHITE, szMessage, + "press ESC or ENTER to close dialog", &uiChar); + } + + +Exit: + if (pFileHdl) + { + pFileHdl->Release(); + pFileHdl = NULL; + } + return rc; +} diff --git a/version5/util/flm_dlst.h b/version5/util/flm_dlst.h new file mode 100644 index 0000000..ea763be --- /dev/null +++ b/version5/util/flm_dlst.h @@ -0,0 +1,168 @@ +//------------------------------------------------------------------------------ +// Desc: FLAIM's dynamic, interactive list manager +// +// 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: flm_dlst.h 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#ifndef FLM_DLST_HPP +#define FLM_DLST_HPP + +class F_DynaList; +class F_DynamicList; + +#ifdef FLM_NLM + #define DLIST_DUMPFILE_PATH (FLMBYTE*)"sys:/system/dlstdump.txt" +#else + #define DLIST_DUMPFILE_PATH (FLMBYTE*)"dlstdump.txt" +#endif + +typedef RCODE (* F_DLIST_DISP_HOOK)( + FTX_WINDOW * pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +RCODE dlistDefaultDisplayHook( + FTX_WINDOW * pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +/* +Types, enums, etc. +*/ + +typedef struct dlist_node +{ + FLMUINT uiKey; + dlist_node * pPrev; + dlist_node * pNext; + void * pvData; + FLMUINT uiDataLen; + F_DLIST_DISP_HOOK pDispHook; +} DLIST_NODE; + +/* +Class definitions +*/ + +#ifdef __cplusplus + +class F_DynamicList : public XF_RefCount, public XF_Base +{ +private: + + /* + Data + */ + + DLIST_NODE * m_pFirst; + DLIST_NODE * m_pLast; + DLIST_NODE * m_pCur; + FTX_WINDOW * m_pListWin; + FLMUINT m_uiListRows; + FLMUINT m_uiListCols; + FLMUINT m_uiRow; + FLMBOOL m_bChanged; + FLMBOOL m_bShowHorizontalSelector; + + /* + Methods + */ + + DLIST_NODE * getNode( FLMUINT uiKey); + void freeNode( DLIST_NODE * pNode); + +public: + + /* + Methods + */ + + F_DynamicList( void); + ~F_DynamicList( void); + + RCODE setup( FTX_WINDOW * pInitializedWindow); + + void refresh( void); + + RCODE insert( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen); + + RCODE update( + FLMUINT uiKey, + F_DLIST_DISP_HOOK pDisplayHook, + void * pvData, + FLMUINT uiDataLen); + + RCODE remove( + FLMUINT uiKey); + + FINLINE DLIST_NODE * getFirst( void) + { + return( m_pFirst); + } + + FINLINE DLIST_NODE * getCurrent( void) + { + return( m_pCur); + } + + FINLINE FTX_WINDOW * getListWin( void) + { + return( m_pListWin); + } + + void defaultKeyAction( FLMUINT uiKey); + + void setShowHorizontalSelector( FLMBOOL bShow) + { + m_bShowHorizontalSelector = bShow; + m_bChanged = TRUE; + } + FLMBOOL getShowHorizontalSelector() { return m_bShowHorizontalSelector;} + + RCODE dumpToFile(); + + /* + Navigational Methods + */ + + void cursorUp( void); + void cursorDown( void); + void pageUp( void); + void pageDown( void); + void home( void); + void end( void); +}; + +#endif // __cplusplus +#endif // FLM_EDIT_HPP diff --git a/version5/util/flm_lutl.cpp b/version5/util/flm_lutl.cpp new file mode 100644 index 0000000..5b0cbc9 --- /dev/null +++ b/version5/util/flm_lutl.cpp @@ -0,0 +1,1122 @@ +//------------------------------------------------------------------------------ +// Desc: Utility routines for presenting selection and statistics lists +// +// 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: flm_lutl.cpp 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flaimsys.h" +#include "flm_lutl.h" +#include "flm_dlst.h" + +extern FLMBOOL gv_bShutdown; + +FSTATIC RCODE ixDisplayHook( + FTX_WINDOW * pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList); + +#define MAX_VALS_TO_SAVE 10 + +typedef struct IxDisplayInfo +{ + char szName [256]; + XFLM_INDEX_STATUS IndexStatus; + FLMUINT64 ui64SaveDocsProcessed [MAX_VALS_TO_SAVE]; + FLMUINT uiDocSaveTime [MAX_VALS_TO_SAVE]; + FLMUINT uiOldestSaved; + FLMUINT uiIndexingRate; + FLMBOOL bShowTime; + F_MUTEX hScreenMutex; + FTX_SCREEN * pScreen; + FTX_WINDOW * pLogWin; + F_Db * pDb; +} IX_DISPLAY_INFO; + +FSTATIC RCODE ixDisplayHook( + FTX_WINDOW * pWin, + FLMBOOL bSelected, + FLMUINT uiRow, + FLMUINT uiKey, + void * pvData, + FLMUINT uiDataLen, + F_DynamicList* pDynamicList + ) +{ + FLMUINT uiBack = WPS_CYAN; + FLMUINT uiFore = WPS_WHITE; + IX_DISPLAY_INFO * pDispInfo = (IX_DISPLAY_INFO *)pvData; + char szTmpBuf [100]; + const char * pszState; + FLMUINT uiGMT; + + F_UNREFERENCED_PARM( uiKey); + F_UNREFERENCED_PARM( uiDataLen); + + flmAssert( uiDataLen == sizeof( IX_DISPLAY_INFO)); + f_timeGetSeconds( &uiGMT ); + + if( pDispInfo->IndexStatus.eState == XFLM_INDEX_SUSPENDED) + { + pszState = "Susp"; + } + else if( pDispInfo->IndexStatus.eState == XFLM_INDEX_ONLINE) + { + pszState = "Onln"; + } + else + { + pszState = "Offln"; + } + + pDispInfo->szName [15] = 0; + if( pDispInfo->IndexStatus.ui64LastDocumentIndexed != (FLMUINT64)~0) + { + f_sprintf( szTmpBuf, "%5u %-15s %-5s %-10u %-10u %-10u %-10u %-10u", + (unsigned)uiKey, pDispInfo->szName, pszState, + (unsigned)pDispInfo->IndexStatus.ui64LastDocumentIndexed, + (unsigned)pDispInfo->uiIndexingRate, + (unsigned)pDispInfo->IndexStatus.ui64KeysProcessed, + (unsigned)pDispInfo->IndexStatus.ui64DocumentsProcessed, + (unsigned)(pDispInfo->bShowTime + ? pDispInfo->IndexStatus.uiStartTime + ? uiGMT - pDispInfo->IndexStatus.uiStartTime + : 0 + : pDispInfo->IndexStatus.ui64Transactions)); + + } + else + { + f_sprintf( szTmpBuf, + "%5u %-15s %-5s %-10u", + (unsigned)uiKey, pDispInfo->szName, pszState, + (unsigned)(pDispInfo->bShowTime + ? pDispInfo->IndexStatus.uiStartTime + ? uiGMT - pDispInfo->IndexStatus.uiStartTime + : 0 + : pDispInfo->IndexStatus.ui64Transactions)); + } + + FTXWinSetCursorPos( pWin, 0, uiRow); + FTXWinClearToEOL( pWin); + FTXWinPrintf( pWin, "%s", szTmpBuf); + if( bSelected && pDynamicList->getShowHorizontalSelector()) + { + FTXWinPaintRow( pWin, &uiBack, &uiFore, uiRow); + } + return( NE_XFLM_OK); +} + +/*************************************************************************** +Desc: Event catching object. +***************************************************************************/ +class IX_Event : public IF_EventClient +{ +public: + + IX_Event() + { + m_pDispInfo = NULL; + } + + virtual ~IX_Event() + { + } + + void XFLMAPI catchEvent( + eEventType eEvent, + IF_Db * pDb, + FLMUINT uiThreadId, + FLMUINT64 ui64TransID, + FLMUINT uiIndexOrCollection, + FLMUINT64 ui64NodeId, + RCODE rc); + + FINLINE void setDispInfo( + IX_DISPLAY_INFO * pDispInfo + ) + { + m_pDispInfo = pDispInfo; + } + +private: + + IX_DISPLAY_INFO * m_pDispInfo; +}; + +/**************************************************************************** +Desc: Event callback +*****************************************************************************/ +void IX_Event::catchEvent( + eEventType eEvent, + IF_Db * pDb, + FLMUINT, // uiThreadId, + FLMUINT64, // ui64TransID, + FLMUINT uiIndexOrCollection, + FLMUINT64 ui64NodeId, + RCODE // rc + ) +{ + XFLM_INDEX_STATUS ixStatus; + FLMUINT uiGMT; + + if (eEvent == XFLM_EVENT_INDEXING_PROGRESS && !ui64NodeId && pDb) + { + if (RC_OK( ((F_Db *)pDb)->indexStatus( uiIndexOrCollection, &ixStatus))) + { + f_timeGetSeconds( &uiGMT ); + + f_mutexLock( m_pDispInfo->hScreenMutex); + FTXWinPrintf( m_pDispInfo->pLogWin, + "Index %u came on-line. Elapsed time = %u second(s)\n", + uiIndexOrCollection, uiGMT - ixStatus.uiStartTime); + f_mutexUnlock( m_pDispInfo->hScreenMutex); + } + } +} + +/**************************************************************************** +Name: flstIndexManagerThread +Desc: Thread that displays the current status of all indexes in a database +Note: The caller must open the database and pass a handle to the thread. + The handle will be closed when the thread exits. +*****************************************************************************/ +RCODE flstIndexManagerThread( + F_Thread * pThread) +{ + RCODE rc = NE_XFLM_OK; + F_DynamicList * pList = f_new F_DynamicList; + FTX_WINDOW * pTitleWin; + FTX_WINDOW * pListWin; + FTX_WINDOW * pHeaderWin; + FTX_WINDOW * pMsgWin; + FLMUINT uiIterations = 0; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + FLMUINT uiIndex; + FLMUINT uiUpdateInterval; + FLMUINT uiLastUpdateTime; + IX_DISPLAY_INFO IxDispInfo; + IX_DISPLAY_INFO * pDispInfo; + DLIST_NODE * pTmpNd; + FLMUINT uiKey; + FLMBOOL bShowOnline = TRUE; + F_Db * pDb = (F_Db *)pThread->getParm1(); + FLMUINT uiOneSec; + FLMBOOL bScreenLocked = FALSE; + IX_Event event; + FLMBOOL bRegisteredForEvent = FALSE; + F_DbSystem dbSystem; + + event.setDispInfo( &IxDispInfo); + +#define FIMT_TITLE_HEIGHT 1 +#define FIMT_HEADER_HEIGHT 4 +#define FIMT_LOG_HEIGHT 10 + + f_memset( &IxDispInfo, 0, sizeof( IX_DISPLAY_INFO)); + IxDispInfo.hScreenMutex = F_MUTEX_NULL; + IxDispInfo.pDb = (F_Db *)pDb; + IxDispInfo.bShowTime = TRUE; + + if( RC_BAD( f_mutexCreate( &IxDispInfo.hScreenMutex))) + { + goto Exit; + } + + if( FTXScreenInit( _getGlobalFtxInfo(), + "Index Manager", &IxDispInfo.pScreen) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXScreenGetSize( IxDispInfo.pScreen, &uiScreenCols, &uiScreenRows); + FTXScreenDisplay( IxDispInfo.pScreen); + + if( FTXWinInit( IxDispInfo.pScreen, 0, + FIMT_TITLE_HEIGHT, &pTitleWin) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetBackFore( pTitleWin, WPS_RED, WPS_WHITE); + FTXWinClear( pTitleWin); + FTXWinPrintStr( pTitleWin, "FLAIM Index Manager"); + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + FTXWinOpen( pTitleWin); + + if( (FTXWinInit( IxDispInfo.pScreen, + uiScreenCols, FIMT_HEADER_HEIGHT, + &pHeaderWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinMove( pHeaderWin, 0, FIMT_TITLE_HEIGHT); + FTXWinSetBackFore( pHeaderWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( pHeaderWin); + FTXWinSetCursorType( pHeaderWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHeaderWin, FALSE); + FTXWinSetLineWrap( pHeaderWin, FALSE); + FTXWinOpen( pHeaderWin); + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + uiScreenRows - FIMT_TITLE_HEIGHT - FIMT_HEADER_HEIGHT - FIMT_LOG_HEIGHT, + &pListWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + FTXWinMove( pListWin, 0, FIMT_TITLE_HEIGHT + FIMT_HEADER_HEIGHT); + FTXWinOpen( pListWin); + pList->setup( pListWin); + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, FIMT_LOG_HEIGHT, + &IxDispInfo.pLogWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinDrawBorder( IxDispInfo.pLogWin); + FTXWinMove( IxDispInfo.pLogWin, 0, uiScreenRows - FIMT_LOG_HEIGHT); + FTXWinSetBackFore( IxDispInfo.pLogWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( IxDispInfo.pLogWin); + FTXWinSetCursorType( IxDispInfo.pLogWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( IxDispInfo.pLogWin, TRUE); + FTXWinSetLineWrap( IxDispInfo.pLogWin, FALSE); + FTXWinOpen( IxDispInfo.pLogWin); + + if (RC_BAD( rc = dbSystem.registerForEvent( + XFLM_EVENT_UPDATES, &event))) + { + goto Exit; + } + bRegisteredForEvent = TRUE; + + FTXWinSetFocus( pListWin); + uiIterations = 0; + FLM_SECS_TO_TIMER_UNITS( 1, uiUpdateInterval); + FLM_SECS_TO_TIMER_UNITS( 1, uiOneSec); + uiLastUpdateTime = 0; + while( !gv_bShutdown) + { + FLMUINT uiCurrTime = FLM_GET_TIMER(); + + if( bScreenLocked) + { + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + } + + if( FLM_ELAPSED_TIME( uiCurrTime, uiLastUpdateTime) >= uiUpdateInterval) + { +Update_Screen: + + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + FTXWinSetCursorPos( pHeaderWin, 0, 1); + if( IxDispInfo.bShowTime) + { + FTXWinPrintf( pHeaderWin, "Index Index State Last Rate Keys Documents Time"); + } + else + { + FTXWinPrintf( pHeaderWin, "Index Index State Last Rate Keys Documents Trans"); + } + FTXWinClearToEOL( pHeaderWin); + FTXWinPrintf( pHeaderWin, "\n"); + FTXWinPrintf( pHeaderWin, "Num. Name DOC"); + + if (RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + + pTmpNd = pList->getFirst(); + uiIndex = 0; + for( ;;) + { + if( RC_BAD( pDb->indexGetNext( &uiIndex))) + { + break; + } + + // Remove all invalid entries + + while( pTmpNd && pTmpNd->uiKey < uiIndex) + { + uiKey = pTmpNd->uiKey; + pTmpNd = pTmpNd->pNext; + pList->remove( uiKey); + } + + if (RC_BAD( rc = pDb->indexStatus( uiIndex, &IxDispInfo.IndexStatus))) + { + goto Exit; + } + + if( !bShowOnline && + IxDispInfo.IndexStatus.eState == XFLM_INDEX_ONLINE) + { + if( pTmpNd && pTmpNd->uiKey == uiIndex) + { + uiKey = pTmpNd->uiKey; + pTmpNd = pTmpNd->pNext; + pList->remove( uiKey); + } + continue; + } + + if( pTmpNd && pTmpNd->uiKey == uiIndex) + { + FLMUINT uiOldest; + FLMUINT uiElapsed; + + pDispInfo = (IX_DISPLAY_INFO *)pTmpNd->pvData; + f_strcpy( IxDispInfo.szName, pDispInfo->szName); + + // Copy the saved information. + + f_memcpy( &IxDispInfo.ui64SaveDocsProcessed [0], + &pDispInfo->ui64SaveDocsProcessed [0], + sizeof( FLMUINT) * MAX_VALS_TO_SAVE); + f_memcpy( &IxDispInfo.uiDocSaveTime [0], + &pDispInfo->uiDocSaveTime [0], + sizeof( FLMUINT) * MAX_VALS_TO_SAVE); + uiOldest = IxDispInfo.uiOldestSaved = pDispInfo->uiOldestSaved; + + // Recalculate the indexing rate. + + uiCurrTime = FLM_GET_TIMER(); + uiElapsed = (uiCurrTime - IxDispInfo.uiDocSaveTime [uiOldest]) / + uiOneSec; + if (uiElapsed && IxDispInfo.IndexStatus.ui64DocumentsProcessed) + { + if( IxDispInfo.ui64SaveDocsProcessed[ uiOldest] < + IxDispInfo.IndexStatus.ui64DocumentsProcessed) + { + IxDispInfo.uiIndexingRate = + // Records processed in time period + (FLMUINT)((IxDispInfo.IndexStatus.ui64DocumentsProcessed - + IxDispInfo.ui64SaveDocsProcessed [uiOldest]) / uiElapsed); + } + else + { + IxDispInfo.uiIndexingRate = 0; + } + } + else + { + IxDispInfo.uiIndexingRate = 0; + } + + // Overwrite the oldest with the current data. + + IxDispInfo.uiDocSaveTime [uiOldest] = uiCurrTime; + IxDispInfo.ui64SaveDocsProcessed [uiOldest] = + IxDispInfo.IndexStatus.ui64DocumentsProcessed; + + // Move oldest pointer for next update. + + if (++IxDispInfo.uiOldestSaved == MAX_VALS_TO_SAVE) + { + IxDispInfo.uiOldestSaved = 0; + } + } + else + { + FLMUINT uiLoop; + FLMUINT uiBufLen; + F_DataVector srchKey; + + uiCurrTime = FLM_GET_TIMER(); + IxDispInfo.uiIndexingRate = 0; + for (uiLoop = 0; uiLoop < MAX_VALS_TO_SAVE; uiLoop++) + { + IxDispInfo.ui64SaveDocsProcessed [uiLoop] = + IxDispInfo.IndexStatus.ui64DocumentsProcessed; + IxDispInfo.uiDocSaveTime [uiLoop] = uiCurrTime; + } + IxDispInfo.uiOldestSaved = 0; + + // Retrieve index name + + if (RC_BAD( srchKey.setUINT( 0, ELM_INDEX_TAG))) + { + break; + } + if (RC_BAD( srchKey.setUINT( 1, uiIndex))) + { + break; + } + + if (RC_BAD( rc = pDb->keyRetrieve( XFLM_DICT_NUMBER_INDEX, + &srchKey, XFLM_EXACT, &srchKey))) + { + if (rc != NE_XFLM_NOT_FOUND) + { + break; + } + } + else + { + F_DOMNode * pNode = NULL; + + if (RC_BAD( rc = pDb->getNode( XFLM_DICT_COLLECTION, + srchKey.getDocumentID(), &pNode))) + { + if (rc != NE_XFLM_DOM_NODE_NOT_FOUND) + { + break; + } + } + else + { + uiBufLen = sizeof( IxDispInfo.szName); + rc = pNode->getAttributeValueUTF8( pDb, + ATTR_NAME_TAG, (FLMBYTE *)IxDispInfo.szName, uiBufLen); + pNode->Release(); + if (rc != NE_XFLM_OK && + rc != NE_XFLM_DOM_NODE_NOT_FOUND && + rc != NE_XFLM_CONV_DEST_OVERFLOW) + { + break; + } + } + } + } + + pList->update( uiIndex, ixDisplayHook, &IxDispInfo, sizeof( IxDispInfo)); + pList->refresh(); + + if( pTmpNd && pTmpNd->uiKey == uiIndex) + { + pTmpNd = pTmpNd->pNext; + } + } + pDb->transAbort(); + uiLastUpdateTime = FLM_GET_TIMER(); + pList->refresh(); + } + + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + if( FTXWinTestKB( pListWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pListWin, &uiChar); + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + + switch( uiChar) + { + case 'O': + case 'o': + { + bShowOnline = !bShowOnline; + goto Update_Screen; + } + + case '+': + case 'r': + { + if( (pTmpNd = pList->getCurrent()) != NULL) + { + if (RC_BAD( rc = pDb->indexResume( pTmpNd->uiKey))) + { + goto Exit; + } + goto Update_Screen; + } + break; + } + + case 's': + { + if( (pTmpNd = pList->getCurrent()) != NULL) + { + if (RC_BAD( rc = pDb->indexSuspend( pTmpNd->uiKey))) + { + goto Exit; + } + goto Update_Screen; + } + break; + } + + case WPK_ALT_S: + case 'S': + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXMessageWindow( IxDispInfo.pScreen, WPS_RED, WPS_WHITE, + "Suspending all indexes ....", + NULL, &pMsgWin); + + f_mutexUnlock( IxDispInfo.hScreenMutex); + + if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS))) + { + uiIndex = 0; + for( ;;) + { + if( RC_BAD( pDb->indexGetNext( &uiIndex))) + { + break; + } + if (RC_BAD( pDb->indexSuspend( uiIndex))) + { + break; + } + } + if (RC_BAD( pDb->transCommit())) + { + (void)pDb->transAbort(); + } + } + + if( pMsgWin) + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXWinFree( &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + goto Update_Screen; + } + + case 'R': + case WPK_ALT_R: + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXMessageWindow( IxDispInfo.pScreen, WPS_RED, WPS_WHITE, + "Resuming all indexes ", + NULL, + &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + + if (RC_OK( pDb->transBegin( XFLM_UPDATE_TRANS))) + { + uiIndex = 0; + for( ;;) + { + if( RC_BAD( pDb->indexGetNext( &uiIndex))) + { + break; + } + + if (RC_BAD( pDb->indexResume( uiIndex))) + { + break; + } + } + if (RC_BAD( pDb->transCommit())) + { + (void)pDb->transAbort(); + break; + } + } + if( pMsgWin) + { + f_mutexLock( IxDispInfo.hScreenMutex); + FTXWinFree( &pMsgWin); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + goto Update_Screen; + } + + case 'T': + case 't': + { + IxDispInfo.bShowTime = !IxDispInfo.bShowTime; + goto Update_Screen; + } + + case '?': + { + FTX_WINDOW * pHelpWin = NULL; + FTX_WINDOW * pHelpTitle = NULL; + F_DynamicList * pHelpList = NULL; + FLMUINT uiItem = 0; + char szTmpBuf [100]; + + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + + if( (pHelpList = f_new F_DynamicList) == NULL) + { + goto Help_Exit; + } + + if( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + 1, &pHelpTitle)) != FTXRC_SUCCESS) + { + goto Help_Exit; + } + + FTXWinSetBackFore( pHelpTitle, WPS_RED, WPS_WHITE); + FTXWinClear( pHelpTitle); + FTXWinSetCursorType( pHelpTitle, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHelpTitle, FALSE); + FTXWinSetLineWrap( pHelpTitle, FALSE); + FTXWinPrintf( pHelpTitle, "FLAIM Index Manager - Help"); + FTXWinOpen( pHelpTitle); + + if ( (FTXWinInit( IxDispInfo.pScreen, uiScreenCols, + uiScreenRows - 1, &pHelpWin)) != FTXRC_SUCCESS) + { + goto Help_Exit; + } + FTXWinDrawBorder( pHelpWin); + FTXWinOpen( pHelpWin); + pHelpList->setup( pHelpWin); + + f_sprintf( szTmpBuf, "R, ALT_R Resume all indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "S, ALT_S Suspend all indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "o, O Toggle display of on-line indexes"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "+, r Resume selected index with auto on-line option"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, "s Suspend selected index"); + pHelpList->update( ++uiItem, NULL, szTmpBuf, sizeof( szTmpBuf)); + + pHelpList->refresh(); + pHelpWin = pHelpList->getListWin(); + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + + while( !gv_bShutdown) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + + if( FTXWinTestKB( pHelpWin) == FTXRC_SUCCESS) + { + FLMUINT uiTmpChar; + + FTXWinInputChar( pHelpWin, &uiTmpChar); + if( uiTmpChar == WPK_ESCAPE) + { + break; + } + pHelpList->defaultKeyAction( uiTmpChar); + } + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + f_sleep( 10); + } + +Help_Exit: + if( !bScreenLocked) + { + f_mutexLock( IxDispInfo.hScreenMutex); + bScreenLocked = TRUE; + } + + if( pHelpList) + { + pHelpList->Release(); + } + + if( pHelpTitle) + { + FTXWinFree( &pHelpTitle); + } + + f_mutexUnlock( IxDispInfo.hScreenMutex); + bScreenLocked = FALSE; + break; + } + + case WPK_ESCAPE: + { + goto Exit; + } + + default: + { + f_mutexLock( IxDispInfo.hScreenMutex); + pList->defaultKeyAction( uiChar); + f_mutexUnlock( IxDispInfo.hScreenMutex); + break; + } + } + f_mutexLock( IxDispInfo.hScreenMutex); + pList->refresh(); + f_mutexUnlock( IxDispInfo.hScreenMutex); + } + + uiIterations++; + + if( pThread->getShutdownFlag()) + { + break; + } + + f_sleep( 1); + } + +Exit: + + if( pList) + { + pList->Release(); + } + + if (bRegisteredForEvent) + { + dbSystem.deregisterForEvent( XFLM_EVENT_UPDATES, &event); + } + + if( IxDispInfo.pScreen) + { + FTXScreenFree( &IxDispInfo.pScreen); + } + + if( IxDispInfo.hScreenMutex != F_MUTEX_NULL) + { + f_mutexDestroy( &IxDispInfo.hScreenMutex); + } +#if 0 + if( pRec) + { + pRec->release( &pRec); + } +#endif + if( pDb != NULL) + { + pDb->Release(); + } + + return( rc); +} + + +/**************************************************************************** +Name: flstMemoryManagerThread +Desc: Thread that displays the current status of a database's cache +Note: The caller must pass a valid share handle to the thread on startup. +*****************************************************************************/ +RCODE flstMemoryManagerThread( + F_Thread * pThread) +{ + F_DynamicList * pList = f_new F_DynamicList; + FTX_SCREEN * pScreen; + FTX_WINDOW * pTitleWin; + FTX_WINDOW * pListWin; + FTX_WINDOW * pHeaderWin; + char szTmpBuf[ 80]; + FLMUINT uiLoop; + FLMUINT uiIteration = 0; + FLMUINT uiScreenCols; + FLMUINT uiScreenRows; + XFLM_CACHE_INFO CacheInfo; + F_DbSystem dbSystem; + +#define FMMT_TITLE_HEIGHT 1 +#define FMMT_HEADER_HEIGHT 3 + + if( FTXScreenInit( _getGlobalFtxInfo(), + "XFlaim Memory Manager", &pScreen) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXScreenGetSize( pScreen, &uiScreenCols, &uiScreenRows); + FTXScreenDisplay( pScreen); + + if( FTXWinInit( pScreen, 0, FMMT_TITLE_HEIGHT, &pTitleWin) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinPaintBackground( pTitleWin, WPS_RED); + FTXWinPrintStr( pTitleWin, "XFlaim Memory Manager"); + FTXWinSetCursorType( pTitleWin, WPS_CURSOR_INVISIBLE); + FTXWinOpen( pTitleWin); + + if( (FTXWinInit( pScreen, uiScreenCols, FMMT_HEADER_HEIGHT, + &pHeaderWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + + FTXWinSetBackFore( pHeaderWin, WPS_BLUE, WPS_WHITE); + FTXWinClear( pHeaderWin); + FTXWinPrintf( pHeaderWin, + "\n Block Cache Node Cache"); + FTXWinSetCursorType( pHeaderWin, WPS_CURSOR_INVISIBLE); + FTXWinSetScroll( pHeaderWin, FALSE); + FTXWinSetLineWrap( pHeaderWin, FALSE); + FTXWinMove( pHeaderWin, 0, FMMT_TITLE_HEIGHT); + FTXWinOpen( pHeaderWin); + + if ( (FTXWinInit( pScreen, uiScreenCols, + uiScreenRows - FMMT_TITLE_HEIGHT - FMMT_HEADER_HEIGHT, + &pListWin)) != FTXRC_SUCCESS) + { + goto Exit; + } + FTXWinMove( pListWin, 0, FMMT_TITLE_HEIGHT + FMMT_HEADER_HEIGHT); + FTXWinOpen( pListWin); + pList->setup( pListWin); + + uiLoop = 0; + while( !gv_bShutdown) + { + if( !(uiIteration % 100)) + { + FLMUINT uiKey = 0; + XFLM_CACHE_USAGE * pBlkCacheUse = &CacheInfo.BlockCache; + XFLM_CACHE_USAGE * pNodeCacheUse = &CacheInfo.NodeCache; + + dbSystem.getCacheInfo( &CacheInfo); + f_sprintf( szTmpBuf, + " Maximum Cache Bytes............... %10u", + (unsigned)CacheInfo.uiMaxBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Total Bytes Allocated ............ %10u", + (unsigned)CacheInfo.uiTotalBytesAllocated); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Total Bytes....................... %10u %10u", + (unsigned)pBlkCacheUse->uiByteCount, + (unsigned)pNodeCacheUse->uiByteCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Count ............................ %10u %10u", + (unsigned)pBlkCacheUse->uiCount, + (unsigned)pNodeCacheUse->uiCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Hits ....................... %10u %10u", + (unsigned)pBlkCacheUse->uiCacheHits, + (unsigned)pNodeCacheUse->uiCacheHits); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Hit Looks .................. %10u %10u", + (unsigned)pBlkCacheUse->uiCacheHitLooks, + (unsigned)pNodeCacheUse->uiCacheHitLooks); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Faults ..................... %10u %10u", + (unsigned)pBlkCacheUse->uiCacheFaults, + (unsigned)pNodeCacheUse->uiCacheFaults); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Fault Looks ................ %10u %10u", + (unsigned)pBlkCacheUse->uiCacheFaultLooks, + (unsigned)pNodeCacheUse->uiCacheFaultLooks); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dirty Count ...................... %10u", + (unsigned)CacheInfo.uiDirtyCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dirty Bytes ...................... %10u", + (unsigned)CacheInfo.uiDirtyBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " New Count ........................ %10u", + (unsigned)CacheInfo.uiNewCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " New Bytes ........................ %10u", + (unsigned)CacheInfo.uiNewBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Log Count ........................ %10u", + (unsigned)CacheInfo.uiLogCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Log Bytes ........................ %10u", + (unsigned)CacheInfo.uiLogBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Old Version Count ................ %10u %10u", + (unsigned)pBlkCacheUse->uiOldVerCount, + (unsigned)pNodeCacheUse->uiOldVerCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Old Version Bytes ................ %10u %10u", + (unsigned)pBlkCacheUse->uiOldVerBytes, + (unsigned)pNodeCacheUse->uiOldVerBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Free Count ....................... %10u", + (unsigned)CacheInfo.uiFreeCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Free Bytes ....................... %10u", + (unsigned)CacheInfo.uiFreeBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Replaceable Count ................ %10u", + (unsigned)CacheInfo.uiReplaceableCount); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Replaceable Bytes ................ %10u", + (unsigned)CacheInfo.uiReplaceableBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Dynamic Cache Adjust ............. %s", + (char *)(CacheInfo.bDynamicCacheAdjust ? "YES" : "NO")); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Percentage .......... %10u", + (unsigned)CacheInfo.uiCacheAdjustPercent); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Min ................. %10u", + (unsigned)CacheInfo.uiCacheAdjustMin); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Cache Adjust Min To Leave ........ %10u", + (unsigned)CacheInfo.uiCacheAdjustMinToLeave); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Slabs ............................ %10u %10u", + (unsigned)pBlkCacheUse->slabUsage.ui64Slabs, + (unsigned)pNodeCacheUse->slabUsage.ui64Slabs); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Slab Bytes ....................... %10u %10u", + (unsigned)pBlkCacheUse->slabUsage.ui64SlabBytes, + (unsigned)pNodeCacheUse->slabUsage.ui64SlabBytes); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Allocated Cells .................. %10u %10u", + (unsigned)pBlkCacheUse->slabUsage.ui64AllocatedCells, + (unsigned)pNodeCacheUse->slabUsage.ui64AllocatedCells); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + f_sprintf( szTmpBuf, + " Free Cells ....................... %10u %10u", + (unsigned)pBlkCacheUse->slabUsage.ui64FreeCells, + (unsigned)pNodeCacheUse->slabUsage.ui64FreeCells); + pList->update( uiKey++, NULL, szTmpBuf, sizeof( szTmpBuf)); + + pList->refresh(); + } + + if( FTXWinTestKB( pListWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pListWin, &uiChar); + switch( uiChar) + { + case 'R': + case 'r': + dbSystem.resetStats(); + break; + case WPK_UP: + pList->cursorUp(); + break; + case WPK_DOWN: + pList->cursorDown(); + break; + case WPK_PGUP: + pList->pageUp(); + break; + case WPK_PGDN: + pList->pageDown(); + break; + case WPK_HOME: + pList->home(); + break; + case WPK_END: + pList->end(); + break; + case WPK_ESCAPE: + goto Exit; + } + pList->refresh(); + } + + if( pThread->getShutdownFlag()) + { + break; + } + + f_sleep( 10); + uiIteration++; + } + +Exit: + + if( pList) + { + pList->Release(); + } + + if( pScreen) + { + FTXScreenFree( &pScreen); + } + + return( NE_XFLM_OK); +} diff --git a/version5/util/flm_lutl.h b/version5/util/flm_lutl.h new file mode 100644 index 0000000..85dc721 --- /dev/null +++ b/version5/util/flm_lutl.h @@ -0,0 +1,32 @@ +//------------------------------------------------------------------------------ +// Desc: FLAIM's utility routines for presenting selection and statistics lists +// +// Tabs: 3 +// +// Copyright (c) 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: flm_lutl.h 3117 2006-01-19 13:34:36 -0700 (Thu, 19 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "ftx.h" + +RCODE flstIndexManagerThread( + F_Thread * pThread); + +RCODE flstMemoryManagerThread( + F_Thread * pThread); diff --git a/version5/util/flmunittest.cpp b/version5/util/flmunittest.cpp new file mode 100644 index 0000000..b37b757 --- /dev/null +++ b/version5/util/flmunittest.cpp @@ -0,0 +1,2058 @@ +//------------------------------------------------------------------------------ +// Desc: Unit test driver +// +// 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: flmunittest.cpp 3138 2006-01-25 12:27:05 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "flmunittest.h" + +#if defined( FLM_WIN) + #if defined( FLM_64BIT) + #define PLATPROC_STR "w64ia64" + #else + #define PLATPROC_STR "w32x86" + #endif +#elif defined( FLM_NLM) + #define PLATPROC_STR "nwx86" +#elif defined( FLM_LINUX) + #define PLATPROC_STR "lxx86" +#elif defined( FLM_MACOSX) + #define PLATPROC_STR "osx" +#elif defined( FLM_SOLARIS) + #define PLATPROC_STR "solaris" +#endif + +FLMBOOL gv_bShutdown = FALSE; +extern RCODE getTest( IFlmTest ** ppTest); + +struct TEST_INFO +{ + bool bLog; + char pszLogfile[ 256]; + bool bDisplay; + bool bVerboseDisplay; + char pszEnvironment[ 32]; + char pszBuild[ 32]; + char pszUser[ 32]; + char pszConfig[ 256]; + TEST_INFO * pNext; + + + TEST_INFO() + { + bLog = FALSE; + bDisplay = FALSE; + bVerboseDisplay = FALSE; + pNext = NULL; + pszLogfile[0] = '\0'; + strcpy( pszEnvironment, PLATPROC_STR); + strcpy( pszBuild, __DATE__); + strcpy( pszUser, "defaultUser"); + pszConfig [0] = 0; + } +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ITestReporter::init( + const char * configFile, + const char * buildNum, + const char * environment, + const char * userName) +{ + RCODE rc = NE_XFLM_OK; + + if( (rc = createUnitTest( configFile, buildNum, environment, userName, + &(this->m_uTD))) != 0) + { + goto Exit; + } + + m_bInitialized = true; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ITestReporter::~ITestReporter() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ITestReporter::recordUnitTestResults( + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails, + const char * elapsedTime) +{ + return ::recordUnitTestResults( &(this->m_uTD), testName, testDescr, + steps, status, resultDetails, elapsedTime); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestLogger::init( + const char * pszFilename) +{ + f_strcpy( m_szFilename, pszFilename); + m_bInitialized = TRUE; + return (NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestLogger::appendString( + const char * pszString) +{ + RCODE rc = NE_XFLM_OK; + char * pszTemp = NULL; + + if ( RC_BAD( rc = f_alloc( f_strlen( pszString) + 3, &pszTemp))) + { + goto Exit; + } + + f_sprintf( pszTemp, "%s\n", pszString); + + if ( RC_BAD( rc = f_filecat( m_szFilename, pszString))) + { + goto Exit; + } + +Exit: + + if ( pszTemp) + { + f_free( &pszTemp); + } + + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +IFlmTestDisplayer::IFlmTestDisplayer() +{ +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE IFlmTestDisplayer::init( void) +{ + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void IFlmTestDisplayer::appendString( + const char * pszString) +{ + printf( pszString); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void printHelp() +{ + printf( "\nCommand-line usage:"); + printf( "\n\n[-l] [-d]"); + printf( "[-c] [-b] [-u]"); + printf( "\n-l - Specifies a log file to print to"); + printf( "\n-d - Display output"); + printf( "\n-t - Specifies configuration file for reporting"); + printf( "\n-b - Specifies the build number"); + printf( "\n-u - Specifies the user running the unit test"); + printf( "\n-i - Interactive mode (pause before exit)"); + printf( "\n-h - Shows this screen"); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +int main( int argc, char ** argv) +{ + RCODE rc = NE_XFLM_OK; + IFlmTest * pTest = NULL; + unsigned int i = 1; + ArgList args; + TEST_INFO testInfo; + FLMBOOL bPauseBeforeExit = FALSE; + + //parse the command line + //format: + //[-l] [-d] [-c] [-b] [-u] + /* + if ( argc < 2) + { + printf("You must specify at least one test to run"); + printHelp(); + goto Exit; + } + */ + + if ( argc > 1) + { + if ( ( strcmp( argv[1], "--help") == 0)|| + ( strcmp( argv[1], "-h") == 0)) + { + printHelp(); + goto Exit; + } + } + + args.expandArgs( argv, argc); + + while( i < args.getNumEntries()) + { + if ( args[i][0] != '-') + { + goto Exit; + } + + if ( ( args[i][1] == 'l') || ( args[i][1] == 'L')) + { + testInfo.bLog = true; + strcpy( testInfo.pszLogfile, &args[i][2]); + } + else if ( ( args[i][1] == 'd') || ( args[i][1] == 'D')) + { + testInfo.bDisplay = true; + } + else if ( ( args[i][1] == 'c') || ( args[i][1] == 'C')) + { + //config file + strcpy( testInfo.pszConfig, &args[i][2]); + } + else if ( ( args[i][1] == 'b') || ( args[i][1] == 'B')) + { + //build + strcpy( testInfo.pszBuild, &args[i][2]); + } + else if ( ( args[i][1] == 'u') || ( args[i][1] == 'U')) + { + //user + strcpy( testInfo.pszUser, &args[i][2]); + } + else if ( ( args[i][1] == 'v') || ( args[i][1] == 'V')) + { + //verbose + testInfo.bVerboseDisplay = TRUE; + } + else if ( ( args[i][1] == 'i') || ( args[i][1] == 'I')) + { + // pause + bPauseBeforeExit = TRUE; + } + else + { + printf( "\nInvalid parameter"); + printHelp(); + goto Exit; + } + i++; + } + + printf("Running %s\n", argv[0]); + + if ( RC_BAD( rc = getTest( &pTest))) + { + printf( "ERROR: Unable to create test instance\n"); + goto Exit; + } + + if ( pTest->init( + testInfo.bLog, + testInfo.pszLogfile, + testInfo.bDisplay, + testInfo.bVerboseDisplay, + testInfo.pszConfig, + testInfo.pszEnvironment, + testInfo.pszBuild, + testInfo.pszUser) != 0) + { + printf( "\nTest initialization failed"); + goto Exit; + } + + if ( RC_BAD( rc = pTest->execute())) + { + // printf("\nTest Failed."); + goto Exit; + } + +Exit: + + if ( pTest) + { + pTest->Release(); + } + + if ( bPauseBeforeExit) + { + printf( "Press any key to exit.\n"); + getchar(); + } + + return( (int)rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE KeyIterator::next() +{ + RCODE rc = NE_XFLM_OK; + IF_DataVector * pTemp = NULL; + + if ( m_bFirstCall) + { + if ( RC_BAD( rc = m_pDb->keyRetrieve( + m_uiIndex, + NULL, + XFLM_FIRST, + m_pFoundKey))) + { + goto Exit; + } + m_bFirstCall = FALSE; + } + else + { + pTemp = m_pSearchKey; + m_pSearchKey = m_pFoundKey; + m_pFoundKey = pTemp; + + if ( RC_BAD( rc = m_pDb->keyRetrieve( + m_uiIndex, + m_pSearchKey, + XFLM_EXCL, + m_pFoundKey))) + { + goto Exit; + } + } +Exit: + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE KeyIterator::getCurrentKeyVal( + FLMUINT uiComponent, + FLMBYTE * pszKey, + FLMUINT uiBufSize, + FLMUINT * puiKeyLen, + FLMUINT64 * pui64Id) +{ + RCODE rc = NE_XFLM_OK; + + if ( RC_BAD( rc = m_pFoundKey->getUTF8( uiComponent, pszKey, &uiBufSize))) + { + goto Exit; + } + if ( puiKeyLen) + { + *puiKeyLen = uiBufSize; + } + if ( pui64Id) + { + *pui64Id = m_pFoundKey->getID( uiComponent); + } + +Exit: + return rc; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +KeyIterator::~KeyIterator() +{ + if ( m_pFoundKey) + { + m_pFoundKey->Release(); + } + + if ( m_pSearchKey) + { + m_pSearchKey->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::removeElem( FLMBYTE * pElem) +{ + FLMBOOL bElemExisted = FALSE; + + for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if ( strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0) + { + bElemExisted = TRUE; + if ( uiLoop < m_uiNumElems - 1) + { + delete[] m_ppucElemArray[ uiLoop]; + memmove( &m_ppucElemArray[ uiLoop], + &m_ppucElemArray[ uiLoop + 1], + ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + memmove( &m_pbFlagArray[ uiLoop], + &m_pbFlagArray[ uiLoop + 1], + ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + } + // Otherwise, we're at the end and decrementing to counter will suffice + + m_uiNumElems--; + } + } + return bElemExisted; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::removeElemContaining( FLMBYTE * pszSubString) +{ + FLMBOOL bElemExisted = FALSE; + + for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; ) + { + if ( containsSubstring( m_ppucElemArray[ uiLoop], pszSubString)) + { + bElemExisted = TRUE; + if ( uiLoop < m_uiNumElems - 1) + { + memmove( &m_ppucElemArray[ uiLoop], + &m_ppucElemArray[ uiLoop + 1], + ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + memmove( &m_pbFlagArray[ uiLoop], + &m_pbFlagArray[ uiLoop + 1], + ( m_uiNumElems - ( uiLoop + 1)) * sizeof( FLMBYTE *)); + } + // Otherwise, we're at the end and decrementing to counter will suffice + + m_uiNumElems--; + } + else + { + uiLoop++; + } + } + return bElemExisted; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMBOOL FlagSet::setElemFlag( FLMBYTE * pElem) +{ + FLMBOOL bIsInSet = FALSE; + + for ( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if ( strcmp( (char *)pElem, (char *)m_ppucElemArray[ uiLoop]) == 0 && + !m_pbFlagArray [uiLoop]) + { + m_pbFlagArray[ uiLoop] = TRUE; + bIsInSet = TRUE; + break; + } + } + return bIsInSet; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet FlagSet::crossProduct( FlagSet& fs2) +{ + FlagSet fsCross; + FLMUINT uiLoop1; + FLMUINT uiCrossProductElems = this->getNumElements() * fs2.getNumElements(); + FLMBYTE ** ppszCross = new FLMBYTE*[ uiCrossProductElems]; + + for ( uiLoop1 = 0; uiLoop1 < this->getNumElements(); uiLoop1++) + { + for ( FLMUINT uiLoop2 = 0; uiLoop2 < fs2.getNumElements(); uiLoop2++) + { + FLMUINT uiIndex = uiLoop1 * fs2.getNumElements() + uiLoop2; + ppszCross[ uiIndex] = new FLMBYTE[ + strlen((char *)this->m_ppucElemArray[ uiLoop1]) + + strlen((char *)fs2.m_ppucElemArray[ uiLoop2]) + 1]; + + strcpy( (char *)ppszCross[ uiIndex], (char *)this->m_ppucElemArray[ uiLoop1]); + strcat( (char *)ppszCross[ uiIndex], (char *)fs2.m_ppucElemArray[ uiLoop2]); + } + } + fsCross.init( ppszCross, uiCrossProductElems); + + for( uiLoop1 = 0; uiLoop1 < uiCrossProductElems; uiLoop1++) + { + delete[] ppszCross[ uiLoop1]; + } + delete[] ppszCross; + + return fsCross; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet& FlagSet::operator=( const FlagSet& fs) +{ + if ( this != &fs) + { + if ( m_ppucElemArray || m_pbFlagArray) + { + this->reset(); + } + this->init( fs.m_ppucElemArray, fs.m_uiNumElems); + } + return *this; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FlagSet::FlagSet( const FlagSet& fs) +{ + m_ppucElemArray = new FLMBYTE*[ fs.m_uiNumElems]; + m_pbFlagArray = new FLMBOOL[ fs.m_uiNumElems]; + memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * fs.m_uiNumElems); + for ( FLMUINT uiLoop = 0; uiLoop < fs.m_uiNumElems; uiLoop++) + { + m_ppucElemArray[uiLoop] = new FLMBYTE[ strlen( (char *)fs.m_ppucElemArray[uiLoop]) + 1]; + strcpy( (char *)m_ppucElemArray[uiLoop], (char *)fs.m_ppucElemArray[uiLoop]); + } + m_uiNumElems = fs.m_uiNumElems; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlagSet::reset() +{ + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + delete [] m_ppucElemArray[ uiLoop]; + } + delete[] m_ppucElemArray; + delete[] m_pbFlagArray; + + m_uiNumElems = 0; + m_ppucElemArray = NULL; + m_pbFlagArray = NULL; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void FlagSet::init( FLMBYTE ** ppucElemArray, FLMUINT uiNumElems) +{ + reset(); + m_ppucElemArray = new FLMBYTE*[ uiNumElems]; + m_pbFlagArray = new FLMBOOL[ uiNumElems]; + memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * uiNumElems); + for ( FLMUINT uiLoop = 0; uiLoop < uiNumElems; uiLoop++) + { + m_ppucElemArray[uiLoop] = new FLMBYTE[ strlen( (char *)ppucElemArray[uiLoop]) + 1]; + strcpy( (char *)m_ppucElemArray[uiLoop], (char *)ppucElemArray[uiLoop]); + } + m_uiNumElems = uiNumElems; +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE createUnitTest( + const char * configPath, + const char * buildNum, + const char * environment, + const char * user, + unitTestData * uTD) +{ + RCODE rc = NE_XFLM_OK; + F_FileHdl configFileHdl; + F_FileHdl csvFileHdl; + char buffer[ MAX_BUFFER_SIZE] = ""; + FLMUINT size = MAX_BUFFER_SIZE; + FLMUINT64 ui64Tmp; + char * strPos1 = NULL; + char * strPos2 = NULL; + + //printf("Starting the Unit Test Recorder\n"); + if (!configPath || !buildNum || !environment || !uTD || !user) + { + flmAssert(0); + } + + if(f_strlen(user) > MAX_SMALL_BUFFER_SIZE) + { + //printf(" ERROR user is too large.\n"); + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + else + { + f_strcpy(uTD->userName, user); + } + + if(f_strlen(environment) > MAX_SMALL_BUFFER_SIZE) + { + //printf(" ERROR environment is too large.\n"); + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + else + { + f_strcpy(uTD->environment, environment); + } + + if(f_strlen(buildNum) > MAX_SMALL_BUFFER_SIZE) + { + //printf(" ERROR bulidNum is too large.\n"); + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + else + { + f_strcpy(uTD->buildNumber, buildNum); + } + + /* Now read the configuration data from the configPath + Config Format: + FOLDER: folder1\folder2 + ATTRIBUTES: Attr1, Attr2 + CSVFILE: csvfile + */ + + if (configPath [0]) + { + // VISIT: if the config file doesn't exist, just ignore the error and continue + + if ( RC_BAD( rc = configFileHdl.Open( + configPath, + XFLM_IO_RDONLY | XFLM_IO_SH_DENYNONE))) + { + goto Exit; + } + + if ( RC_BAD( rc = configFileHdl.Size( &ui64Tmp))) + { + goto Exit; + } + + size = (FLMUINT)ui64Tmp; + + if ( RC_BAD( rc = configFileHdl.Read( + 0, size, buffer, &size))) + { + goto Exit; + } + + #ifdef FLM_WIN + { + char * temp = new char[ size]; + char * tempbegin = temp; + size_t newsize = size; + + for( unsigned int i = 0; i < size; i++) + { + if ( ( ( i + 1) < size) + && ( buffer[i] == 0x0D && buffer[i + 1] == 0x0A)) + { + *temp++ = 0x0A; + i++; + newsize--; + } + else + { + *temp++ = buffer[i]; + } + } + + f_memcpy( buffer, tempbegin, (FLMSIZET)newsize); + size = newsize; + } + #endif + + // Get the FOLDER + + strPos1 = f_strchr(buffer, ':'); + strPos2 = f_strchr(strPos1, '\n'); + if(!strPos1 || !strPos2) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + for( strPos1++; *strPos1 == ' ' || *strPos1 == '\t'; strPos1++); + + if(strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( NE_XFLM_BUFFER_OVERFLOW); + goto Exit; + } + + f_strncpy(uTD->folder, strPos1, strPos2-strPos1); + uTD->folder[strPos2-strPos1] = '\0'; + + // Get the ATTRIBUTES + + strPos1 = f_strchr(strPos1, ':'); + strPos2 = f_strchr(strPos1, '\n'); + + if(!strPos1 || !strPos2) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + for(strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); + + if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + f_strncpy(uTD->attrs, strPos1, strPos2-strPos1); + uTD->attrs[strPos2-strPos1] = '\0'; + + // Get the CSVFILE + + strPos1 = f_strchr(strPos1, ':'); + strPos2 = f_strchr(strPos1, '\n'); + + // Allow for possible \r + + if( *( --strPos2) != '\r') + { + strPos2++; + } + + if( !strPos1 || !strPos2) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + for( strPos1++;*strPos1 == ' ' || *strPos1 == '\t';strPos1++); + + if( strPos2-strPos1 > MAX_SMALL_BUFFER_SIZE) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + f_strncpy(uTD->csvFilename, strPos1, strPos2-strPos1); + uTD->csvFilename[strPos2-strPos1] = '\0'; + + rc = csvFileHdl.Open( + uTD->csvFilename, + XFLM_IO_RDWR | XFLM_IO_SH_DENYNONE); + + if( RC_BAD( rc)) + { + if ( rc == NE_XFLM_IO_PATH_NOT_FOUND) + { + // Create the file and write the header + + if (RC_BAD( rc = f_filecat( uTD->csvFilename, DATA_ORDER))) + { + goto Exit; + } + } + } + else + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: After each unit test call this to record the unit test status + to a CSV file. + uTD - contains the configuration information + testName - a Unique for this module unite test name. + testDescr - A description of the unit test. + steps - The steps the unit test performs. + status - Maybe be PASS or FAIL + resultDetails - details that explain the result status. +****************************************************************************/ +RCODE recordUnitTestResults( + unitTestData * uTD, + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails, + const char * elapsedTime) +{ + RCODE rc = NE_XFLM_OK; + char buffer[MAX_BUFFER_SIZE]; + + if( !testName || !testDescr || !steps || !status || !resultDetails || !uTD ) + { + flmAssert(0); + } + + //VISIT - re-enable the elapsed time reporting when the TCB can support it + + (void)elapsedTime; + + f_sprintf( buffer, "%s,%s,%s,%s,%s,%s,%s,%s,"/*%s,*/"%s,%s\n", + testName, uTD->userName, testDescr, steps, uTD->buildNumber, status, + uTD->environment, resultDetails, /*elapsedTime ? elapsedTime : "",*/ + uTD->attrs, uTD->folder); + + if( RC_BAD( rc = f_filecat( uTD->csvFilename, buffer))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser) +{ + RCODE rc = NE_XFLM_MEM; + + if( (m_pDbSystem = f_new F_DbSystem) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDbSystem->init())) + { + goto Exit; + } + + // VISIT: here -- disable asserts on FLAIM errors via a config call!!! + + m_bLog = bLog; + m_bDisplay = bDisplay; + + // Set up logger and displayer if true + + if( m_bLog) + { + if( ( m_pLogger = f_new IFlmTestLogger) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pLogger->init( pszLogfile))) + { + goto Exit; + } + } + + if( m_bDisplay) + { + if( (m_pDisplayer = f_new IFlmTestDisplayer) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pDisplayer->init())) + { + goto Exit; + } + } + + if( (m_pReporter = f_new ITestReporter) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = m_pReporter->init( pszConfigFile, pszBuild, + pszEnvironment, pszUser))) + { + goto Exit; + } + m_bDisplayVerbose = bVerboseDisplay; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::beginTest( + const char * pszTestName, + const char * pszTestDesc, + const char * pszTestSteps, + const char * pszDetails) +{ + char szTemp[ 256]; + + m_pszTestName = pszTestName; + m_pszTestDesc = pszTestDesc; + m_pszSteps = pszTestSteps; + + if( m_bDisplayVerbose) + { + displayLine( + "========================================" + "======================================="); + + f_sprintf( szTemp, "Test Name: %s", m_pszTestName); + displayLine( szTemp); + + f_sprintf( szTemp, "Test Description: %s", m_pszTestDesc); + displayLine( szTemp); + + f_sprintf( szTemp, "Steps: %s", m_pszSteps); + displayLine( szTemp); + } + else + { + f_sprintf( szTemp, "Test Name: %s ... ", m_pszTestName); + display( szTemp); + } + + f_strcpy( m_szDetails, pszDetails); + FLM_TIMER_UNITS_TO_SECS( FLM_GET_TIMER(), m_ui64StartMs); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::endTest( + const char * pszTestResult) +{ + FLM_TIMER_UNITS_TO_SECS( FLM_GET_TIMER(), m_ui64EndMs); + outputAll( pszTestResult, m_ui64EndMs - m_ui64StartMs); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::log( + const char * pszString) +{ + if( m_bLog) + { + m_pLogger->appendString( pszString); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::display( + const char * pszString) +{ + if( m_bDisplay) + { + m_pDisplayer->appendString( pszString); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::displayLine( + const char * pszString) +{ + if( m_bDisplay) + { + m_pDisplayer->appendString( pszString); + m_pDisplayer->appendString( "\n"); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::displayTime( + FLMUINT64 ui64Milli, + const char * pszIntro) +{ + const char * pszDefault = "Elapsed Time: "; + char szTimeBuf[ 64]; + char * pszTempBuf = NULL; + + if( pszIntro) + { + pszTempBuf = new char[ strlen( pszIntro) + sizeof( szTimeBuf)]; + strcpy( pszTempBuf, pszIntro); + } + else + { + pszTempBuf = new char[ strlen( pszDefault) + sizeof( szTimeBuf)]; + strcpy( pszTempBuf, pszDefault); + } + + normalizeTime( ui64Milli, szTimeBuf); + strcat( pszTempBuf, szTimeBuf); + displayLine( pszTempBuf); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +void TestBase::normalizeTime( + FLMUINT64 ui64Milli, + char * pszString) +{ + FLMUINT64 ui64Ms; + FLMUINT64 ui64S; + FLMUINT64 ui64M; + FLMUINT64 ui64H; + + ui64Ms = ui64Milli % 1000; + ui64S = ui64Milli / 1000; + + ui64M = ui64S / 60; + ui64S %= 60; + + ui64H = ui64M / 60; + ui64M %= 60; + + f_sprintf( pszString, "%02I64u:%02I64u:%02I64u.%03I64u", + ui64H, ui64M, ui64S, ui64Ms); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::initCleanTestState( + const char * pszDibName) +{ + RCODE rc = NE_XFLM_OK; + XFLM_CREATE_OPTS createOpts; + + // Create the database + + f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + + if ( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, + NULL, &createOpts, &m_pDb))) + { + if( rc == NE_XFLM_FILE_EXISTS) + { + if( RC_BAD( rc = m_pDbSystem->dbRemove( pszDibName, NULL, NULL, TRUE))) + { + goto Exit; + } + } + + if( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, + NULL, &createOpts, &m_pDb))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::openTestState( + const char * pszDibName) +{ + XFLM_CREATE_OPTS createOpts; + IF_FileSystem * pFileSys = NULL; + RCODE rc = NE_XFLM_OK; + + m_pDbSystem->getFileSystem( &pFileSys); + + if ( RC_BAD( rc = pFileSys->Exists( pszDibName))) + { + // Create the database + + f_memset( &createOpts, 0, sizeof( XFLM_CREATE_OPTS)); + + if ( RC_BAD( rc = m_pDbSystem->dbCreate( pszDibName, NULL, NULL, NULL, + NULL, &createOpts, &m_pDb))) + { + goto Exit; + } + } + else + { + // Open the existing database + + if( RC_BAD( rc = m_pDbSystem->dbOpen( pszDibName, NULL, NULL, + NULL, FALSE, &m_pDb))) + { + goto Exit; + } + } + +Exit: + + if( pFileSys) + { + pFileSys->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::shutdownTestState( + const char * pszDibName, + FLMBOOL bRemoveDib) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiRefCount; + + if( bRemoveDib) + { + if( m_pDb) + { + uiRefCount = m_pDb->Release(); + flmAssert( uiRefCount == 0); + m_pDb = NULL; + } + + if( RC_BAD( rc = m_pDbSystem->dbRemove( + pszDibName, NULL, NULL, TRUE))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::checkQueryResults( + const char ** ppszResults, + FLMUINT uiNumResultsExpected, + IF_Query * pQuery, + char * pszDetails) +{ + RCODE rc = NE_XFLM_OK; + FlagSet flagSet; + IF_DOMNode * pReturn = NULL; + FLMUINT uiLoop; + char szBuffer[ 500]; + + flagSet.init( (FLMBYTE **)ppszResults, uiNumResultsExpected); + + for( uiLoop = 0; ; uiLoop++) + { + if( !uiLoop) + { + if( RC_BAD( rc = pQuery->getFirst( m_pDb, &pReturn))) + { + MAKE_FLM_ERROR_STRING( "getFirst failed.", pszDetails, rc); + goto Exit; + } + } + else + { + if( RC_BAD( rc = pQuery->getNext( m_pDb, &pReturn))) + { + if( uiLoop < uiNumResultsExpected - 1) + { + MAKE_FLM_ERROR_STRING( "getNext failed.", pszDetails, rc); + goto Exit; + } + else + { + rc = NE_XFLM_OK; + break; + } + } + } + + if( RC_BAD( rc = pReturn->getUTF8( m_pDb, (FLMBYTE *)szBuffer, + sizeof(szBuffer), 0, sizeof( szBuffer) - 1))) + { + MAKE_FLM_ERROR_STRING( "getUTF8 failed.", pszDetails, rc); + goto Exit; + } + + if ( !flagSet.setElemFlag( (FLMBYTE *)szBuffer)) + { + rc = NE_XFLM_FAILURE; + MAKE_FLM_ERROR_STRING( "Unexpected result received.", pszDetails, rc); + goto Exit; + } + } + + if( !flagSet.allElemFlagsSet()) + { + rc = NE_XFLM_FAILURE; + MAKE_FLM_ERROR_STRING( "Expected results not received.", + pszDetails, rc); + goto Exit; + } + +Exit: + + if( pReturn) + { + pReturn->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::doQueryTest( + const char * pszQueryString, + const char ** ppszExpectedResults, + FLMUINT uiNumResultsExpected, + IF_Query * pQuery, + char * pszDetails, + FLMUINT uiRequestedIndex, + FLMUINT * puiIndexUsed) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bHaveMult; + + if( pszQueryString) + { + if( RC_BAD( rc = pQuery->setupQueryExpr( m_pDb, pszQueryString))) + { + MAKE_FLM_ERROR_STRING( "setupQueryExpr failed.", pszDetails, rc); + goto Exit; + } + } + + if( uiRequestedIndex) + { + if( RC_BAD( rc = pQuery->setIndex( uiRequestedIndex))) + { + MAKE_FLM_ERROR_STRING( "setIndex failed.", pszDetails, rc); + goto Exit; + } + } + + if( RC_BAD( rc = checkQueryResults( ppszExpectedResults, + uiNumResultsExpected, pQuery, pszDetails))) + { + goto Exit; + } + + if( puiIndexUsed) + { + if( RC_BAD( rc = pQuery->getIndex( m_pDb, puiIndexUsed, &bHaveMult))) + { + goto Exit; + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::logTestResults( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime) +{ + RCODE rc = NE_XFLM_OK; + char * pszTemp = NULL; + char szTime[ 64]; + + if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) + { + goto Exit; + } + + if( ui64ElapsedTime != ~((FLMUINT64)0)) + { + normalizeTime( ui64ElapsedTime, szTime); + } + else + { + f_strcpy( szTime, "Not Recorded"); + } + + log( + "========================================" + "======================================="); + + f_sprintf( pszTemp, "Test Name: %s", m_pszTestName); + log( pszTemp); + + f_sprintf( pszTemp, "Test Description: %s", m_pszTestDesc); + log( pszTemp); + + f_sprintf( pszTemp, "Steps: %s", m_pszSteps); + log( pszTemp); + + f_sprintf( pszTemp, "Test Result: %s", pszTestResult); + log( pszTemp); + + f_sprintf( pszTemp, "Details: %s", m_szDetails); + log( pszTemp); + + f_sprintf( pszTemp, "Elapsed Time: %s", szTime); + log( pszTemp); + + log( + "========================================" + "======================================="); + +Exit: + + if( pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::displayTestResults( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime) +{ + RCODE rc = NE_XFLM_OK; + char * pszTemp = NULL; + char szTime[ 64]; + + if( RC_BAD( rc = f_alloc( DETAILS_BUF_SIZ + 64, &pszTemp))) + { + goto Exit; + } + + if( ui64ElapsedTime != ~((FLMUINT64)0)) + { + normalizeTime( ui64ElapsedTime, szTime); + } + else + { + f_strcpy( szTime, "Not Recorded"); + } + + if( m_bDisplayVerbose) + { + f_sprintf( pszTemp, "Test Result: %s", pszTestResult); + displayLine( pszTemp); + + f_sprintf( pszTemp, "Details: %s", m_szDetails); + displayLine( pszTemp); + + f_sprintf( pszTemp, "Elapsed Time: %s", szTime); + displayLine( pszTemp); + + displayLine( + "========================================" + "======================================="); + } + else + { + f_sprintf( pszTemp, "Result: %s", pszTestResult); + displayLine( pszTestResult); + } + +Exit: + + if( pszTemp) + { + f_free( &pszTemp); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::outputAll( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime) +{ + RCODE rc = NE_XFLM_OK; + char * pszTime = NULL; + + if( ui64ElapsedTime != ~((FLMUINT64)0)) + { + if( RC_BAD( rc = f_alloc( 64, &pszTime))) + { + goto Exit; + } + + normalizeTime( ui64ElapsedTime, pszTime); + } + + if( RC_BAD( rc = displayTestResults( pszTestResult, ui64ElapsedTime))) + { + goto Exit; + } + + if( RC_BAD( rc = logTestResults( pszTestResult, ui64ElapsedTime))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pReporter->recordUnitTestResults( + m_pszTestName, m_pszTestDesc, m_pszSteps, pszTestResult, m_szDetails, + pszTime))) + { + goto Exit; + } + +Exit: + + if( pszTime) + { + f_free( &pszTime); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::importBuffer( + const char * pszBuffer, + FLMUINT uiCollection) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_pDbSystem->openBufferIStream( pszBuffer, + f_strlen( pszBuffer), &m_pInputStream))) + { + MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->import( m_pInputStream, uiCollection))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::importDocument( + const char * pszBuffer, + FLMUINT uiCollection) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_pDbSystem->openBufferIStream( pszBuffer, + f_strlen( pszBuffer), &m_pInputStream))) + { + MAKE_FLM_ERROR_STRING( "openBufferIStream failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->importDocument( m_pInputStream, uiCollection))) + { + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::importFile( + const char * pszFilename, + FLMUINT uiCollection) +{ + RCODE rc = NE_XFLM_OK; + + if( RC_BAD( rc = m_pDbSystem->openFileIStream( pszFilename, + &m_pInputStream))) + { + MAKE_FLM_ERROR_STRING( "openFileIStream failed.", m_szDetails, rc); + goto Exit; + } + + if( RC_BAD( rc = m_pDb->import( m_pInputStream, uiCollection))) + { + MAKE_FLM_ERROR_STRING( "file import failed.", m_szDetails, rc); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +TestBase::~TestBase() +{ + if( m_pLogger) + { + m_pLogger->Release(); + } + + if( m_pDisplayer) + { + m_pDisplayer->Release(); + } + + if( m_pReporter) + { + m_pReporter->Release(); + } + + if( m_pInputStream) + { + m_pInputStream->Release(); + } + + if( m_pDb) + { + m_pDb->Release(); + } + + if( m_pDbSystem) + { + m_pDbSystem->exit(); + m_pDbSystem->Release(); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMINT TestBase::unicmp( + FLMUNICODE * puzStr1, + FLMUNICODE * puzStr2) +{ + while( *puzStr1 == *puzStr2 && *puzStr1) + { + puzStr1++; + puzStr2++; + } + + return( (FLMINT)*puzStr1 - (FLMINT)*puzStr2); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE TestBase::createCompoundDoc( + ELEMENT_NODE_INFO * pElementNodes, + FLMUINT uiNumElementNodes, + FLMUINT64 * pui64DocId) +{ + IF_DOMNode * pDocRoot = NULL; + IF_DOMNode * pNode = NULL; + FLMUINT uiLoop; + RCODE rc = NE_XFLM_OK; + + if( pui64DocId) + { + *pui64DocId = 0; + } + + if( RC_BAD( rc = m_pDb->createRootElement( + XFLM_DATA_COLLECTION, ELM_ANY_TAG, &pDocRoot))) + { + MAKE_FLM_ERROR_STRING( "createRootElement failed.", m_szDetails, rc); + goto Exit; + } + + for( uiLoop = 0; uiLoop < uiNumElementNodes; uiLoop++) + { + if( isPlaceHolder(pElementNodes[uiLoop])) + { + continue; + } + + if( RC_BAD( rc = pDocRoot->createNode( + m_pDb, ELEMENT_NODE, pElementNodes[uiLoop].uiDictNum, + XFLM_LAST_CHILD, &pNode))) + { + MAKE_FLM_ERROR_STRING( "createNode failed.", m_szDetails, rc); + goto Exit; + } + + switch( pElementNodes[uiLoop].uiDataType) + { + case XFLM_TEXT_TYPE: + { + if( RC_BAD( rc = pNode->setUTF8( + m_pDb, (FLMBYTE *)pElementNodes[uiLoop].pvData))) + { + goto Exit; + } + + break; + } + + case XFLM_NUMBER_TYPE: + { + if ( RC_BAD( rc = pNode->setUINT( + m_pDb, (FLMUINT)pElementNodes[uiLoop].pvData))) + { + goto Exit; + } + + break; + } + + case XFLM_BINARY_TYPE: + { + if ( RC_BAD( rc = pNode->setBinary( + m_pDb, (FLMBYTE*)pElementNodes[uiLoop].pvData, + pElementNodes[uiLoop].uiDataSize))) + { + goto Exit; + } + break; + } + + default: + { + flmAssert( 0); + } + } + } + + if( RC_BAD ( rc = m_pDb->documentDone( pDocRoot))) + { + MAKE_FLM_ERROR_STRING( "documentDone failed.", m_szDetails, rc); + goto Exit; + } + + if( pui64DocId) + { + if( RC_BAD( rc = pDocRoot->getNodeId( m_pDb, pui64DocId))) + { + goto Exit; + } + } + +Exit: + + if( pNode) + { + pNode->Release(); + } + + if( pDocRoot) + { + pDocRoot->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ArgList::ArgList() +{ + m_uiCapacity = INIT_SIZE; + m_uiNumEntries = 0; + + f_alloc( m_uiCapacity * sizeof( char *), &m_ppszArgs); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +ArgList::~ArgList() +{ + FLMUINT uiLoop; + + if( m_ppszArgs) + { + for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) + { + f_free( &( m_ppszArgs[uiLoop])); + } + + f_free( &m_ppszArgs); + } +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::resize( void) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + char ** ppszTemp = NULL; + + if( RC_BAD( rc = f_alloc( m_uiCapacity * GROW_FACTOR * sizeof(char*), + &ppszTemp))) + { + goto Exit; + } + + m_uiCapacity *= GROW_FACTOR; + + for( uiLoop = 0; uiLoop < m_uiNumEntries; uiLoop++) + { + if( RC_BAD( rc = f_alloc( f_strlen( m_ppszArgs[uiLoop]) + 1, + &ppszTemp[uiLoop]))) + { + f_free( &ppszTemp); + goto Exit; + } + + f_strcpy( ppszTemp[uiLoop], m_ppszArgs[uiLoop]); + } + + f_free( &m_ppszArgs); + m_ppszArgs = ppszTemp; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +FLMUINT ArgList::getNumEntries( void) +{ + return( m_uiNumEntries); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::addArg( + const char * pszArg) +{ + RCODE rc = NE_XFLM_OK; + + if( m_uiNumEntries >= m_uiCapacity) + { + if( RC_BAD( rc = resize())) + { + goto Exit; + } + } + + if( RC_BAD( rc = f_alloc( f_strlen( pszArg) + 1, + &m_ppszArgs[m_uiNumEntries]))) + { + goto Exit; + } + + f_strcpy( m_ppszArgs[ m_uiNumEntries++], pszArg); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * ArgList::getArg( + FLMUINT uiIndex) +{ + return( m_ppszArgs[ uiIndex]); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::expandFileArgs( + const char * pszFilename) +{ + RCODE rc = NE_XFLM_OK; + char token[64]; + F_FileSystem fileSystem; + F_FileHdl * pFileHdl = NULL; + + if( RC_BAD( rc = fileSystem.Open( pszFilename, XFLM_IO_RDWR, + (IF_FileHdl **)&pFileHdl))) + { + goto Exit; + } + + while( RC_OK( rc = getTokenFromFile(token, pFileHdl))) + { + if( token[0] == '@') + { + if( RC_BAD( rc = expandFileArgs( &token[1]))) + { + goto Exit; + } + } + else + { + flmAssert(*token); + if( RC_BAD( rc = addArg( token))) + { + goto Exit; + } + } + } + + if( rc == NE_XFLM_IO_END_OF_FILE) + { + rc = NE_XFLM_OK; + } + +Exit: + + if( pFileHdl) + { + pFileHdl->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::expandArgs( + char ** ppszArgs, + FLMUINT uiNumArgs) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < uiNumArgs; uiLoop++) + { + if( ppszArgs[uiLoop][0] == '@') + { + if( RC_BAD( rc = expandFileArgs( &ppszArgs[uiLoop][ 1]))) + { + goto Exit; + } + } + else + { + if( RC_BAD( rc = addArg( ppszArgs[ uiLoop]))) + { + goto Exit; + } + } + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +const char * ArgList::operator []( FLMUINT uiIndex) +{ + return( m_ppszArgs[ uiIndex]); +} + +/**************************************************************************** +Desc: +****************************************************************************/ +RCODE ArgList::getTokenFromFile( + char * pszToken, + F_FileHdl * pFileHdl) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT uiSize = 1; + FLMUINT64 ui64Offset = 0; + FLMUINT64 ui64TrueOffset = 0; + char c; + + for(;;) + { +Skip_WS_And_Comments: + + if ( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + + // Skip whitespace + + while( isWhitespace(c)) + { + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + } + + if( c == '#') + { + // Skip comment + + for (;;) + { + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + +#ifdef FLM_UNIX + // On unix platforms, an EOL is indicated by an LF + + if( c == 0x0A) + { + break; + } +#else + // On Windows and NetWare we need to look for CR/LF + + if( c == 0x0D) + { + + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + + // Newline found + + if( c == 0x0A) + { + break; + } + else + { + // Rewind + + ui64Offset = 0; + ui64TrueOffset = 0; + + if( RC_BAD( rc = pFileHdl->Tell( &ui64Offset))) + { + goto Exit; + } + + ui64Offset--; + + if( RC_BAD( rc = pFileHdl->Seek( ui64Offset, + XFLM_IO_SEEK_SET, &ui64TrueOffset))) + { + goto Exit; + } + } + } +#endif + } + + goto Skip_WS_And_Comments; + } + + while( !isWhitespace( c)) + { + if( c == '#') + { + break; + } + + *pszToken++ = c; + if( RC_BAD( rc = pFileHdl->Read( 0, 1, &c, &uiSize))) + { + goto Exit; + } + } + + // Put the char back + + if( RC_BAD( rc = pFileHdl->Tell( &ui64Offset))) + { + goto Exit; + } + ui64Offset--; + + if( RC_BAD( rc = pFileHdl->Seek( ui64Offset, + XFLM_IO_SEEK_SET, &ui64TrueOffset))) + { + goto Exit; + } + break; + } + +Exit: + + *pszToken = '\0'; + return( rc); +} diff --git a/version5/util/flmunittest.h b/version5/util/flmunittest.h new file mode 100644 index 0000000..e1a1059 --- /dev/null +++ b/version5/util/flmunittest.h @@ -0,0 +1,670 @@ +//------------------------------------------------------------------------------ +// Desc: Interface definition that all unit tests must implement +// +// 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: testdef.h 2961 2005-11-14 20:18:15Z ahodgkinson $ +//------------------------------------------------------------------------------ + +#ifndef FLMUNITTEST_H +#define FLMUNITTEST_H + +#include +#include "flaimsys.h" + +// Status codes passed to recordUnitTestResults + +#define PASS "PASS" +#define FAIL "FAIL" + +#define MAX_SMALL_BUFFER_SIZE 255 +#define MAX_BUFFER_SIZE 2500 + +#define DATA_ORDER "Testcase Name,Owner,Description,Steps,Build,Status,Environment," \ + "ResDetails,"/*Elapsed Time,*/"Attributes,Folder\n" + +#ifndef ELEMCOUNT + #define ELEMCOUNT(a) \ + sizeof(a) / sizeof(a[0]) +#endif + +#define MAKE_ERR_STRING( str, buf) \ + sprintf( buf, str" file: %s. line: %u.", __FILE__, __LINE__); \ + flmAssert( 0) + +#define MAKE_ERROR_STRING( str, buf, rcode) \ + sprintf( buf, str" "#rcode" == %X. file: %s. line: %u.", \ + (unsigned)rcode, __FILE__, __LINE__); \ + flmAssert( 0) + +#define MAKE_FLM_ERROR_STRING( str, buf, rcode) \ + sprintf( buf, str" "#rcode" == %X : %s. file: %s. line: %u.", \ + (unsigned)rcode, m_pDbSystem->errorString( rcode), __FILE__, __LINE__); \ + flmAssert( 0); + +#define MAKE_GENERIC_ERROR_STRING( str, buf, num) \ + sprintf( buf, str": %lX file: %s. line: %u.", \ + (unsigned long)num, __FILE__, __LINE__); \ + flmAssert( 0); + +#define MAKE_GENERIC_ERROR_STRING64( str, buf, num) \ + sprintf( buf, str": %llX file: %s. line: %u.", \ + (unsigned long long)num, __FILE__, __LINE__); \ + flmAssert( 0); + +// Error Codes + +#define UNITTEST_INVALID_CSVFILE -101 +#define UNITTEST_INVALID_CONFIGFILE -102 +#define UNITTEST_CONFIGPATH_READ_FAILED -103 +#define UNITTEST_BUFFER_TOO_SMALL -104 +#define UNITTEST_INVALID_PASSWORD -105 +#define UNITTEST_INVALID_USER_NAME -106 +#define UNITTEST_INVALID_PARAM -107 +#define UNITTEST_INVALID_CONFIGPATH -108 + +typedef struct KeyCompInfo +{ + void * pvComp; + FLMUINT uiDataType; + FLMUINT uiDataSize; +} KEY_COMP_INFO; + +typedef struct ElementNodeInfo +{ + void * pvData; + FLMUINT uiDataType; + FLMUINT uiDataSize; + FLMUINT uiDictNum; +} ELEMENT_NODE_INFO; + +typedef struct unitTestData_t +{ + char userName[ MAX_SMALL_BUFFER_SIZE]; + char buildNumber[ MAX_SMALL_BUFFER_SIZE]; + char environment[ MAX_SMALL_BUFFER_SIZE]; + char folder[ MAX_SMALL_BUFFER_SIZE]; + char attrs[ MAX_SMALL_BUFFER_SIZE]; + char csvFilename[ MAX_SMALL_BUFFER_SIZE]; +} unitTestData; + +RCODE createUnitTest( + const char * configPath, + const char * buildNum, + const char * user, + const char * environment, + unitTestData * uTD); + +RCODE recordUnitTestResults( + unitTestData * uTD, + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails, + const char * elapsedTime = NULL); + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTest : public XF_RefCount +{ +public: + + virtual RCODE init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser) = 0; + + virtual const char * getName( void) = 0; + + virtual RCODE execute( void) = 0; +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestDisplayer : public XF_Base, public XF_RefCount +{ +public: + + IFlmTestDisplayer(); + + RCODE init( void); + + void appendString( + const char * pszString); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class ITestReporter : public XF_Base, public XF_RefCount +{ + unitTestData m_uTD; + FLMBOOL m_bInitialized; + +public: + + ITestReporter() + { + m_bInitialized = false; + f_memset( &m_uTD, 0, sizeof( unitTestData)); + } + + virtual ~ITestReporter(); + + RCODE init( + const char * configFile, + const char * buildNum, + const char * environment, + const char * userName); + + RCODE recordUnitTestResults( + const char * testName, + const char * testDescr, + const char * steps, + const char * status, + const char * resultDetails, + const char * elapsedTime); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class IFlmTestLogger : public XF_Base, public XF_RefCount +{ +private: + + FLMBOOL m_bInitialized; + char m_szFilename[ 128]; + +public: + + IFlmTestLogger() + { + m_bInitialized = FALSE; + m_szFilename[ 0] = '\0'; + } + + RCODE init( + const char * pszFilename); + + RCODE appendString( + const char * pszString); +}; + +/****************************************************************************** +Desc: Makes iterating through the keys of an index more convenient +******************************************************************************/ +class KeyIterator +{ +public: + + KeyIterator() + { + m_pSearchKey = NULL; + m_pFoundKey = NULL; + m_pDbSystem = NULL; + m_bFirstCall = TRUE; + m_pDb = NULL; + m_uiIndex = 0; + } + + ~KeyIterator(); + + RCODE next( void); + + RCODE getCurrentKeyVal( + FLMUINT uiComponent, + FLMBYTE * pszKey, + FLMUINT uiBufSize, + FLMUINT * puiKeyLen, + FLMUINT64 * pui64Id = NULL); + + FINLINE FLMUINT getIndexNum( void) + { + return m_uiIndex; + } + + FINLINE void setIndexNum( + FLMUINT uiIndex) + { + m_uiIndex = uiIndex; + + // Changing the index necessitates a reset + + reset(); + } + + FINLINE RCODE init( + FLMUINT uiIndex, + IF_DbSystem * pDbSystem, + IF_Db * pDb) + { + RCODE rc = NE_XFLM_OK; + + m_pDbSystem = pDbSystem; + m_pDb = pDb; + + if( RC_BAD( rc = m_pDbSystem->createIFDataVector( &m_pSearchKey))) + { + goto Exit; + } + + if( RC_BAD( rc = m_pDbSystem->createIFDataVector( &m_pFoundKey))) + { + goto Exit; + } + + m_uiIndex = uiIndex; + + Exit: + + return( rc); + } + + FINLINE void reset( void) + { + if( m_pSearchKey) + { + m_pSearchKey->reset(); + } + + if( m_pFoundKey) + { + m_pFoundKey->reset(); + } + + m_bFirstCall = TRUE; + } + +private: + + IF_DataVector * m_pSearchKey; + IF_DataVector * m_pFoundKey; + IF_DbSystem * m_pDbSystem; + IF_Db * m_pDb; + FLMBOOL m_bFirstCall; + FLMUINT m_uiIndex; +}; + +/****************************************************************************** +Desc: Quick, dirty, and wholly inadequate subsititute for the STL BitSet. + This only works with strings. +******************************************************************************/ +class FlagSet +{ +public: + + FlagSet() + { + m_pbFlagArray = NULL; + m_ppucElemArray = NULL; + m_uiNumElems = 0; + } + + FlagSet( const FlagSet& fs); + + ~FlagSet() + { + reset(); + } + + FlagSet crossProduct( FlagSet& fs2); + + FlagSet& operator=( const FlagSet& fs); + + FLMBOOL removeElem( + FLMBYTE * pElem); + + FLMBOOL removeElemContaining( + FLMBYTE * pszSubString); + + FLMBOOL setElemFlag( + FLMBYTE * pElem); + + FINLINE FLMBOOL allElemFlagsSet() + { + FLMBOOL bAllSet = TRUE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( m_pbFlagArray[uiLoop] == FALSE) + { + bAllSet = FALSE; + } + } + + return( bAllSet); + } + + FINLINE FLMBOOL noElemFlagsSet( void) + { + FLMBOOL bNoneSet = TRUE; + + for( FLMUINT uiLoop = 0; uiLoop < m_uiNumElems; uiLoop++) + { + if( m_pbFlagArray[uiLoop] == TRUE) + { + bNoneSet = FALSE; + } + } + + return( bNoneSet); + } + + FINLINE void unsetAllFlags( void) + { + memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * m_uiNumElems); + } + + FINLINE FLMUINT getNumElements( void) + { + return m_uiNumElems; + } + + void reset( void); + + void clearFlags( void) + { + memset( m_pbFlagArray, 0, sizeof( FLMBOOL) * m_uiNumElems); + } + + void init( + FLMBYTE ** ppucElemArray, + FLMUINT uiNumElems); + +private: + + FLMBOOL containsSubstring( + FLMBYTE * pszString, + FLMBYTE * pszSub) + { + FLMBYTE * pszStringTemp = NULL; + FLMBYTE * pszSubTemp = NULL; + FLMBOOL bContainsSub = FALSE; + + // First scan quickly through the two strings looking for a + // single-character match. When it's found, then compare the + // rest of the substring. + + pszSubTemp = pszSub; + if( *pszSub == 0) + { + goto Exit; + } + + for( ; *pszString != 0; pszString++) + { + if( *pszString != *pszSubTemp) + { + continue; + } + + pszStringTemp = pszString; + for( ;;) + { + if( *pszSubTemp == 0) + { + bContainsSub = TRUE; + goto Exit; + } + + if( *pszStringTemp++ != *pszSubTemp++) + { + break; + } + } + + pszSubTemp = pszSub; + + } + +Exit: + + return( bContainsSub); + } + + FLMBOOL * m_pbFlagArray; + FLMBYTE ** m_ppucElemArray; + FLMUINT m_uiNumElems; +}; + +/****************************************************************************** +Desc: Makes iterating through the keys of an index more convenient +******************************************************************************/ +FINLINE FLMBOOL isPlaceHolder( const ELEMENT_NODE_INFO & elm) +{ + if( elm.pvData == NULL && + elm.uiDataType == XFLM_NODATA_TYPE && + elm.uiDataSize == 0 && + elm.uiDictNum == 0) + { + return ( TRUE); + } + + return ( FALSE); +} + +/****************************************************************************** +Desc: Makes iterating through the keys of an index more convenient +******************************************************************************/ +class TestBase : public IFlmTest +{ + +public: + + TestBase() + { + m_bLog = FALSE; + m_bDisplay = FALSE; + m_bDisplayVerbose = FALSE; + m_pLogger = NULL; + m_pDisplayer = NULL; + m_pReporter = NULL; + m_pDbSystem = NULL; + m_pDb = NULL; + m_pInputStream = NULL; + m_pszTestName = NULL; + m_pszTestDesc = NULL; + m_pszSteps = NULL; + m_ui64StartMs = 0; + m_ui64EndMs = 0; + } + + virtual ~TestBase(); + + RCODE init( + FLMBOOL bLog, + const char * pszLogfile, + FLMBOOL bDisplay, + FLMBOOL bVerboseDisplay, + const char * pszConfigFile, + const char * pszEnvironment, + const char * pszBuild, + const char * pszUser); + +protected: + + FLMBOOL m_bLog; + FLMBOOL m_bDisplay; + FLMBOOL m_bDisplayVerbose; + IFlmTestLogger * m_pLogger; + IFlmTestDisplayer * m_pDisplayer; + ITestReporter * m_pReporter; + IF_DbSystem * m_pDbSystem; + IF_Db * m_pDb; + IF_PosIStream * m_pInputStream; +#define DETAILS_BUF_SIZ 1024 + char m_szDetails[ DETAILS_BUF_SIZ]; + const char * m_pszTestName; + const char * m_pszTestDesc; + const char * m_pszSteps; + FLMUINT64 m_ui64StartMs; + FLMUINT64 m_ui64EndMs; + + void beginTest( + const char * pszTestName, + const char * pszTestDesc, + const char * pszTestSteps, + const char * pszDetails); + + void endTest( + const char * pszTestResult); + + void log( + const char * pszString); + + void display( + const char * pszString); + + void displayLine( + const char * pszString); + + void displayTime( + FLMUINT64 ui64Milli, + const char * pszIntro = NULL); + + void normalizeTime( + FLMUINT64 ui64Milli, + char * pszString); + + RCODE logTestResults( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime = ~((FLMUINT64)0)); + + RCODE displayTestResults( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime = ~((FLMUINT64)0)); + + RCODE outputAll( + const char * pszTestResult, + FLMUINT64 ui64ElapsedTime = ~((FLMUINT64)0)); + + RCODE initCleanTestState( + const char * pszDibName); + + RCODE openTestState( + const char * pszDibName); + + RCODE shutdownTestState( + const char * pszDibName, + FLMBOOL bRemoveDib); + + RCODE checkQueryResults( + const char ** ppszResults, + FLMUINT uiNumResultsExpected, + IF_Query * pQuery, + char * pszDetails); + + RCODE doQueryTest( + const char * pszQueryString, + const char ** ppszExpectedResults, + FLMUINT uiNumResultsExpected, + IF_Query * pQuery, + char * pszDetails, + FLMUINT uiRequestedIndex = 0, + FLMUINT * puiIndexUsed = NULL); + + RCODE importBuffer( + const char * pszBuffer, + FLMUINT uiCollection); + + RCODE importDocument( + const char * pszBuffer, + FLMUINT uiCollection); + + RCODE importFile( + const char * pszFilename, + FLMUINT uiCollection); + + FLMINT unicmp( + FLMUNICODE * puzStr1, + FLMUNICODE * puzStr2); + + RCODE createCompoundDoc( + ELEMENT_NODE_INFO * pElementNodes, + FLMUINT uiNumElementNodes, + FLMUINT64 * pui64DocId); +}; + +/**************************************************************************** +Desc: +****************************************************************************/ +class ArgList +{ +public: + + ArgList(); + + ~ArgList(); + + const char * operator[]( + FLMUINT uiIndex); + + const char * getArg( + FLMUINT uiIndex); + + RCODE addArg( + const char * pszArg); + + FLMUINT getNumEntries( void); + + RCODE expandArgs( + char ** ppszArgs, + FLMUINT uiNumArgs); + + RCODE expandFileArgs( + const char * pszFilename); + +private: + + enum + { + INIT_SIZE = 10, + GROW_FACTOR = 2 + }; + + char ** m_ppszArgs; + FLMUINT m_uiCapacity; + FLMUINT m_uiNumEntries; + + RCODE resize( void); + + RCODE getTokenFromFile( + char * pszToken, + F_FileHdl * pFileHdl); + + FLMBOOL isWhitespace( + char c) + { + return (c == ' ' || c == '\t' || c == 0xd || c == 0xa) + ? TRUE + : FALSE; + } +}; + +#endif diff --git a/version5/util/fshell.cpp b/version5/util/fshell.cpp new file mode 100644 index 0000000..e0223a9 --- /dev/null +++ b/version5/util/fshell.cpp @@ -0,0 +1,9420 @@ +//------------------------------------------------------------------------------ +// Desc: Command-line environment for FLAIM utilities +// +// 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: fshell.cpp 3133 2006-01-25 12:00:01 -0700 (Wed, 25 Jan 2006) dsanders $ +//------------------------------------------------------------------------------ + +#include "fshell.h" +#include "domedit.h" + +#ifdef FLM_WIN + #include +#endif + +// Imported global variables. + +extern FTX_INFO * gv_pFtxInfo; +extern FLMBOOL gv_bShutdown; + +// XML Import Error Handling + +#define flmErrorCodeEntry( c) { c, #c } +#define MAX_IMPORT_ERROR_STRING 65 + +typedef struct +{ + RCODE rc; + char * pszErrorStr; +} XSHELL_ERROR_CODE_MAP; + +XSHELL_ERROR_CODE_MAP gv_XMLParseErrors[ + XML_NUM_ERRORS - 1] = +{ + flmErrorCodeEntry( XML_ERR_BAD_ELEMENT_NAME), + flmErrorCodeEntry( XML_ERR_XMLNS_IN_ELEMENT_NAME), + flmErrorCodeEntry( XML_ERR_ELEMENT_NAME_MISMATCH), + flmErrorCodeEntry( XML_ERR_PREFIX_NOT_DEFINED), + flmErrorCodeEntry( XML_ERR_EXPECTING_GT), + flmErrorCodeEntry( XML_ERR_EXPECTING_ELEMENT_LT), + flmErrorCodeEntry( XML_ERR_EXPECTING_EQ), + flmErrorCodeEntry( XML_ERR_MULTIPLE_XMLNS_DECLS), + flmErrorCodeEntry( XML_ERR_MULTIPLE_PREFIX_DECLS), + flmErrorCodeEntry( XML_ERR_EXPECTING_QUEST_GT), + flmErrorCodeEntry( XML_ERR_INVALID_XML_MARKUP), + flmErrorCodeEntry( XML_ERR_MUST_HAVE_ONE_ATT_DEF), + flmErrorCodeEntry( XML_ERR_EXPECTING_NDATA), + flmErrorCodeEntry( XML_ERR_EXPECTING_SYSTEM_OR_PUBLIC), + flmErrorCodeEntry( XML_ERR_EXPECTING_LPAREN), + flmErrorCodeEntry( XML_ERR_EXPECTING_RPAREN_OR_PIPE), + flmErrorCodeEntry( XML_ERR_EXPECTING_NAME), + flmErrorCodeEntry( XML_ERR_INVALID_ATT_TYPE), + flmErrorCodeEntry( XML_ERR_INVALID_DEFAULT_DECL), + flmErrorCodeEntry( XML_ERR_EXPECTING_PCDATA), + flmErrorCodeEntry( XML_ERR_EXPECTING_ASTERISK), + flmErrorCodeEntry( XML_ERR_EMPTY_CONTENT_INVALID), + flmErrorCodeEntry( XML_ERR_CANNOT_MIX_CHOICE_AND_SEQ), + flmErrorCodeEntry( XML_ERR_XML_ILLEGAL_PI_NAME), + flmErrorCodeEntry( XML_ERR_ILLEGAL_FIRST_NAME_CHAR), + flmErrorCodeEntry( XML_ERR_ILLEGAL_COLON_IN_NAME), + flmErrorCodeEntry( XML_ERR_EXPECTING_VERSION), + flmErrorCodeEntry( XML_ERR_INVALID_VERSION_NUM), + flmErrorCodeEntry( XML_ERR_UNSUPPORTED_ENCODING), + flmErrorCodeEntry( XML_ERR_EXPECTING_YES_OR_NO), + flmErrorCodeEntry( XML_ERR_EXPECTING_QUOTE_BEFORE_EOL), + flmErrorCodeEntry( XML_ERR_EXPECTING_SEMI), + flmErrorCodeEntry( XML_ERR_UNEXPECTED_EOL_IN_ENTITY), + flmErrorCodeEntry( XML_ERR_INVALID_CHARACTER_NUMBER), + flmErrorCodeEntry( XML_ERR_UNSUPPORTED_ENTITY), + flmErrorCodeEntry( XML_ERR_EXPECTING_QUOTE), + flmErrorCodeEntry( XML_ERR_INVALID_PUBLIC_ID_CHAR), + flmErrorCodeEntry( XML_ERR_EXPECTING_WHITESPACE), + flmErrorCodeEntry( XML_ERR_EXPECTING_HEX_DIGIT), + flmErrorCodeEntry( XML_ERR_INVALID_BINARY_ATTR_VALUE), + flmErrorCodeEntry( XML_ERR_CREATING_CDATA_NODE), + flmErrorCodeEntry( XML_ERR_CREATING_COMMENT_NODE), + flmErrorCodeEntry( XML_ERR_CREATING_PI_NODE), + flmErrorCodeEntry( XML_ERR_CREATING_DATA_NODE), + flmErrorCodeEntry( XML_ERR_CREATING_ROOT_ELEMENT), + flmErrorCodeEntry( XML_ERR_CREATING_ELEMENT_NODE) +}; + +#define FSMI_ENTRY_ELEMENT 2 +#define FSMI_INTERNAL_ENTRY_FLAGS_ATTR 21 +#define FSMI_ENTRY_MODIFY_TIME_ATTR 22 +#define FSMI_ENTRY_FLAGS_ATTR 23 +#define FSMI_PARTITION_ID_ATTR 24 +#define FSMI_CLASS_ID_ATTR 25 +#define FSMI_PARENT_ID_ATTR 26 +#define FSMI_ALT_ID_ATTR 27 +#define FSMI_SUBORDINATE_COUNT_ATTR 28 +#define FSMI_RDN_ATTR 29 +#define FSMI_FIRST_CHILD_ATTR 30 +#define FSMI_LAST_CHILD_ATTR 31 +#define FSMI_NEXT_SIBLING_ATTR 32 +#define FSMI_PREV_SIBLING_ATTR 33 + +#define FSMI_ATTR_GVTS_ATTR 50 +#define FSMI_ATTR_DTS_ATTR 51 +#define FSMI_VALUE_FLAGS_ATTR 52 +#define FSMI_VALUE_MTS_ATTR 53 +#define FSMI_TTL_ATTR 54 +#define FSMI_POLICY_DN_ATTR 55 + +typedef struct OVERHEAD_INFO +{ + FLMUINT64 ui64DOMOverhead; + FLMUINT64 ui64ValueBytes; +} OVERHEAD_INFO; + +typedef struct ATTR_NODE_INFO +{ + FLMUINT uiAttrNameId; + char * pszAttrName; + FLMUINT64 ui64NumValues; + OVERHEAD_INFO GVTS; + OVERHEAD_INFO DTS; + OVERHEAD_INFO ValueFlags; + OVERHEAD_INFO ValueMTS; + OVERHEAD_INFO TTL; + OVERHEAD_INFO PolicyDN; + OVERHEAD_INFO OtherNodes; +} ATTR_NODE_INFO; + +class Entry_Info +{ +public: + + Entry_Info(); + + ~Entry_Info(); + + FINLINE void resetOverheadInfo( + OVERHEAD_INFO * pOverhead) + { + pOverhead->ui64DOMOverhead = 0; + pOverhead->ui64ValueBytes = 0; + } + + FINLINE void initAttrInfo( + FLMUINT uiAttrNameId, + ATTR_NODE_INFO * pAttrInfo) + { + pAttrInfo->uiAttrNameId = uiAttrNameId; + pAttrInfo->pszAttrName = NULL; + pAttrInfo->ui64NumValues = 0; + resetOverheadInfo( &pAttrInfo->GVTS); + resetOverheadInfo( &pAttrInfo->DTS); + resetOverheadInfo( &pAttrInfo->ValueFlags); + resetOverheadInfo( &pAttrInfo->ValueMTS); + resetOverheadInfo( &pAttrInfo->TTL); + resetOverheadInfo( &pAttrInfo->PolicyDN); + resetOverheadInfo( &pAttrInfo->OtherNodes); + } + + void getOverheadInfo( + OVERHEAD_INFO * pOverhead); + + RCODE getXMLAttrInfo( + IF_Db * pDb, + IF_DOMNode * pNode, + OVERHEAD_INFO * pOverhead, + FLMUINT uiAttrNameId); + + RCODE getAttrValueInfo( + IF_Db * pDb, + IF_DOMNode * pValueNode, + ATTR_NODE_INFO * pAttrNodeInfo); + + RCODE processAttrValues( + IF_Db * pDb, + IF_DOMNode * pAttrNode, + ATTR_NODE_INFO * pAttrNodeInfo); + + FLMBOOL findDirAttr( + FLMUINT uiAttrNameId, + FLMUINT * puiInsertPos); + + RCODE getDirAttrInfo( + IF_Db * pDb, + IF_DOMNode * pAttrNode); + + RCODE processEntryAttrs( + IF_Db * pDb, + IF_DOMNode * pEntryNode); + + RCODE addEntryInfo( + IF_Db * pDb, + IF_DOMNode * pEntryNode); + + FLMUINT64 m_ui64NumEntries; + FLMUINT64 m_ui64TotalBytes; + OVERHEAD_INFO m_EntryNode; + OVERHEAD_INFO m_AttrNode; + OVERHEAD_INFO m_InternalEntryFlags; + OVERHEAD_INFO m_ModifyTime; + OVERHEAD_INFO m_EntryFlags; + OVERHEAD_INFO m_PartitionID; + OVERHEAD_INFO m_ClassID; + OVERHEAD_INFO m_ParentID; + OVERHEAD_INFO m_AlternateID; + OVERHEAD_INFO m_SubordinateCount; + OVERHEAD_INFO m_RDN; + OVERHEAD_INFO m_FirstChild; + OVERHEAD_INFO m_LastChild; + OVERHEAD_INFO m_NextSibling; + OVERHEAD_INFO m_PrevSibling; + ATTR_NODE_INFO * m_pAttrList; + FLMUINT m_uiAttrListSize; + FLMUINT m_uiNumAttrs; + F_NodeInfo m_nodeInfo; + F_Pool m_Pool; +}; + +// Local prototypes + +RCODE flmBackupProgFunc( + FLMUINT uiStatusType, + void * Parm1, + void * Parm2, + void * UserData); + +FLMINT fshellFileSystemTest( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell); + +FSTATIC void format64BitNum( + FLMUINT64 ui64Num, + char * pszBuf, + FLMBOOL bOutputHex, + FLMBOOL bAddCommas = FALSE); + +RCODE flmXmlImportStatus( + eXMLStatus eStatusType, + void * pvArg1, + void * pvArg2, + void * pvArg3, + void * pvUserData); + +RCODE importXmlFiles( + IF_Db * pDb, + FLMUINT uiCollection, + FlmShell * pShell, + char * pszPath, + XFLM_IMPORT_STATS * pImportStats); + +RCODE getErrFromFile( + F_FileIStream * fileIStream, + FLMUINT uiCurrLineFilePos, + FLMUINT uiCurrLineBytes, + XMLEncoding eXMLEncoding, + char * pszErrorString); + +FSTATIC void domDisplayError( + FTX_WINDOW * pWindow, + char * pszError, + RCODE errRc); + +FSTATIC void domOutputValues( + FTX_WINDOW * pWindow, + FLMUINT * puiLineCount, + IF_FileHdl * pFileHdl, + char * pszLabel, + FLMUINT64 ui64Bytes, + FLMUINT uiPercent, + FLMUINT64 ui64Count); + +FSTATIC void domDisplayLine( + FTX_WINDOW * pWindow, + FLMUINT * puiLineCount, + IF_FileHdl * pFileHdl, + char * pszLine, + char * pszWaitPrompt = NULL); + +FSTATIC void domDisplayValue( + FTX_WINDOW * pWindow, + FLMUINT * puiLineCount, + IF_FileHdl * pFileHdl, + char * pszLabel, + FLMUINT uiPercent, + FLMUINT64 ui64Value); + +FSTATIC void domDisplayInfo( + FTX_WINDOW * pWindow, + FLMUINT * puiLineCount, + IF_FileHdl * pFileHdl, + char * pszDomOverheadLabel, + char * pszValueBytesLabel, + OVERHEAD_INFO * pInfo, + FLMUINT64 ui64TotalBytes); + +char * errorToString( + XMLParseError errorType); + +// Local class definitions +class F_LocalRestore : public F_FSRestore +{ +public: + + ~F_LocalRestore() {} + F_LocalRestore() + { + } + + FINLINE RCODE setup( + char * pszDbPath, + char * pszBackupSetPath, + char * pszRflDir) + { + return( F_FSRestore::setup( pszDbPath, + pszBackupSetPath, pszRflDir)); + } + + FINLINE RCODE XFLMAPI openRflFile( + FLMUINT uiFileNum) + { + return( F_FSRestore::openRflFile( uiFileNum)); + } + + FINLINE RCODE XFLMAPI read( + FLMUINT uiLength, + void * pvBuffer, + FLMUINT * puiBytesRead) + { + return( F_FSRestore::read( uiLength, pvBuffer, puiBytesRead)); + } + +private: + +}; + +class F_LocalRestoreStatus : public F_DefaultRestoreStatus +{ +public: + + F_LocalRestoreStatus( + FlmShell * pShell) + { + m_pShell = pShell; + m_bFirstStatus = TRUE; + m_uiTransCount = 0; + m_uiAddCount = 0; + m_uiDeleteCount = 0; + m_uiModifyCount = 0; + m_uiReserveCount = 0; + m_uiIndexCount = 0; + m_uiRflFileNum = 0; + m_ui64RflBytesRead = 0; + } + + RCODE XFLMAPI reportProgress( + eRestoreAction * peAction, + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone); + + RCODE XFLMAPI reportError( + eRestoreAction * peAction, + RCODE rcErr); + + RCODE XFLMAPI reportBeginTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId); + + RCODE XFLMAPI reportCommitTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId); + + RCODE XFLMAPI reportAbortTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId); + + RCODE XFLMAPI reportOpenRflFile( + eRestoreAction * peAction, + FLMUINT uiFileNum) + { + m_uiRflFileNum = uiFileNum; + updateCountDisplay(); + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportRflRead( + eRestoreAction * peAction, + FLMUINT uiFileNum, + FLMUINT uiBytesRead) + { + (void)uiFileNum; + + m_ui64RflBytesRead += uiBytesRead; + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportBlockChainFree( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT64, // ui64MaintDocNum, + FLMUINT, // uiStartBlkAddr, + FLMUINT, // uiEndBlkAddr, + FLMUINT // uiCount + ) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportEnableEncryption( + eRestoreAction * peAction, + FLMUINT64 ui64TransId); + + RCODE XFLMAPI reportWrapKey( + eRestoreAction * peAction, + FLMUINT64 ui64TransId); + + RCODE XFLMAPI reportRollOverDbKey( + eRestoreAction * peAction, + FLMUINT64) // ui64TransId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportDocumentDone( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64) // ui64NodeId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeCreate( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64RefNodeId, + eDomNodeType, // eNodeType, + FLMUINT, // uiNameId, + eNodeInsertLoc) // eLocation) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeDelete( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64) // ui64NodeId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeChildrenDelete( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64NodeId, + FLMUINT) // uiNameId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportInsertBefore( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64ParentId, + FLMUINT64, // ui64NewChildId, + FLMUINT64) // ui64RefChildId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeUpdate( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64) // ui64NodeId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeSetValue( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64) // ui64NodeId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeFlagsUpdate( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64NodeId, + FLMUINT, // uiFlags, + FLMBOOL) // bAdd) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeSetPrefixId( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64NodeId, + FLMUINT) // uiPrefixId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportSetNextNodeId( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64) // ui64NextNodeId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeSetMetaValue( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64NodeId, + FLMUINT64 // ui64MetaValue + ) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportAttributeDelete( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64ElementId, + FLMUINT) // uiAttrName) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportAttributeSetValue( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64ElementId, + FLMUINT) // uiNameId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + + RCODE XFLMAPI reportNodeSetPrefixId( + eRestoreAction * peAction, + FLMUINT64, // ui64TransId, + FLMUINT, // uiCollection, + FLMUINT64, // ui64NodeId, + FLMUINT, // uiAttrName, + FLMUINT) // uiPrefixId) + { + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + return( NE_XFLM_OK); + } + +private: + + RCODE report_preamble( + FTX_WINDOW * pWin); + + RCODE report_postamble( + FTX_WINDOW * pWin); + + void updateCountDisplay( void); + + FlmShell * m_pShell; + FLMBOOL m_bFirstStatus; + FLMUINT m_uiTransCount; + FLMUINT m_uiAddCount; + FLMUINT m_uiDeleteCount; + FLMUINT m_uiModifyCount; + FLMUINT m_uiReserveCount; + FLMUINT m_uiIndexCount; + FLMUINT m_uiRflFileNum; + FLMUINT64 m_ui64RflBytesRead; +}; + +class F_LocalBackupClient : public F_DefaultBackupClient +{ +public: + + F_LocalBackupClient( + FlmShell * pShell, + char * pszBackupPath) : F_DefaultBackupClient( pszBackupPath) + { + m_pShell = pShell; + } +private: + + FlmShell * m_pShell; +}; + +class F_LocalBackupStatus : public IF_BackupStatus, public XF_Base +{ +public: + + F_LocalBackupStatus( + FlmShell * pShell) + { + m_pShell = pShell; + } + + RCODE XFLMAPI backupStatus( + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone); + + FINLINE FLMUINT getRefCount( void) + { + return( IF_BackupStatus::getRefCount()); + } + + virtual FINLINE FLMUINT32 XFLMAPI AddRef( void) + { + return( IF_BackupStatus::AddRef()); + } + + virtual FINLINE FLMUINT32 XFLMAPI Release( void) + { + return( IF_BackupStatus::Release()); + } + +private: + + FlmShell * m_pShell; +}; + +// Methods + +/**************************************************************************** +Desc: +*****************************************************************************/ +FlmShell::FlmShell( void) : FlmThreadContext() +{ + m_HistPool.poolInit( 512); + m_ArgPool.poolInit( 512); + + f_memset( m_DbList, 0, MAX_SHELL_OPEN_DB * sizeof( IF_Db *)); + m_pTitleWin = NULL; + m_iCurrArgC = 0; + m_ppCurrArgV = NULL; + m_iLastCmdExitCode = 0; + m_bPagingEnabled = FALSE; + m_pSharedContext = NULL; + f_memset( m_ppCmdList, 0, sizeof( FlmCommand *) * MAX_REGISTERED_COMMANDS); + f_memset( m_ppHistory, 0, sizeof( char *) * MAX_SHELL_HISTORY_ITEMS); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FlmShell::~FlmShell( void) +{ + FLMUINT uiLoop; + + m_HistPool.poolFree(); + m_ArgPool.poolFree(); + + // Free the command objects. + + for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) + { + if( m_ppCmdList[ uiLoop] != NULL) + { + m_ppCmdList[ uiLoop]->Release(); + } + } + + // Free the history items + + for( uiLoop = 0; uiLoop < MAX_SHELL_HISTORY_ITEMS; uiLoop++) + { + if( m_ppHistory[ uiLoop]) + { + f_free( &m_ppHistory[ uiLoop]); + } + } + + // Close all open databases + + for( uiLoop = 0; uiLoop < MAX_SHELL_OPEN_DB; uiLoop++) + { + if( m_DbList[ uiLoop]) + { + m_DbList[ uiLoop]->Release(); + } + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::setup( + FlmSharedContext * pSharedContext) +{ + FlmCommand * pCommand = NULL; + RCODE rc = NE_XFLM_OK; + + flmAssert( pSharedContext != NULL); + + m_pSharedContext = pSharedContext; + + if( RC_BAD( rc = FlmThreadContext::setup( m_pSharedContext, + "X-FLAIM Shell", NULL, NULL))) + { + goto Exit; + } + + // Register dbopen command + + if( (pCommand = f_new FlmDbOpenCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register dbclose command + + if( (pCommand = f_new FlmDbCloseCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register dbcopy, dbrename, and dbremove command handler + + if( (pCommand = f_new FlmDbManageCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register trans command + + if( (pCommand = f_new FlmTransCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register backup command + + if( (pCommand = f_new FlmBackupCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register restore command + + if( (pCommand = f_new FlmRestoreCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register database config command + + if( (pCommand = f_new FlmDbConfigCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register database get config command + + if( (pCommand = f_new FlmDbGetConfigCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register sysinfo command + + if( (pCommand = f_new FlmSysInfoCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the hex conversion command + + if( (pCommand = f_new FlmHexConvertCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the base64 conversion command + + if( (pCommand = f_new FlmBase64ConvertCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + + // Register the file delete command + + if( (pCommand = f_new FlmFileSysCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the import command + + if( (pCommand = f_new FlmImportCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the dom edit command + + if( (pCommand = f_new FlmDomEditCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the export command + + if( (pCommand = f_new FlmExportCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register the wrap key command + + if( (pCommand = f_new FlmWrapKeyCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register nodeinfo command + + if( (pCommand = f_new FlmNodeInfoCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + + // Register btreeinfo command + + if( (pCommand = f_new FlmBTreeInfoCommand) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = registerCmd( pCommand))) + { + goto Exit; + } + pCommand = NULL; + +Exit: + + if( pCommand) + { + pCommand->Release(); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::registerDatabase( + IF_Db * pDb, + FLMUINT * puiDbId) +{ + FLMUINT uiLoop; + RCODE rc = NE_XFLM_OK; + + *puiDbId = 0xFFFFFFFF; + + for( uiLoop = 0; uiLoop < MAX_SHELL_OPEN_DB; uiLoop++) + { + if( !m_DbList[ uiLoop]) + { + m_DbList[ uiLoop] = pDb; + *puiDbId = uiLoop; + break; + } + } + + if( *puiDbId == 0xFFFFFFFF) + { + rc = RC_SET( NE_XFLM_TOO_MANY_OPEN_DATABASES); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::getDatabase( + FLMUINT uiDbId, + IF_Db ** ppDb) +{ + RCODE rc = NE_XFLM_OK; + + if( uiDbId >= MAX_SHELL_OPEN_DB) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + *ppDb = m_DbList[ uiDbId]; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::deregisterDatabase( + FLMUINT uiDbId) +{ + RCODE rc = NE_XFLM_OK; + + if( uiDbId >= MAX_SHELL_OPEN_DB) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + m_DbList[ uiDbId] = NULL; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::con_printf( + char * pszFormat, ...) +{ + char szBuffer[ 512]; + f_va_list args; + + if( m_pWindow) + { + f_va_start( args, pszFormat); + f_vsprintf( szBuffer, pszFormat, &args); + f_va_end( args); + FTXWinPrintStr( m_pWindow, szBuffer); + } + + return( NE_XFLM_OK); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::parseCmdLine( + char * pszString) +{ + FLMUINT uiArgCount = 0; + FLMUINT uiCurrToken = 0; + FLMUINT uiTokenLen; + char * pszCurrToken; + FLMBOOL bQuoted; + FlmParse Parser; + RCODE rc = NE_XFLM_OK; + + m_ArgPool.poolReset( NULL); + m_iCurrArgC = 0; + m_ppCurrArgV = NULL; + m_pszOutputFile = NULL; + + Parser.setString( pszString); + while( Parser.getNextToken()) + { + uiArgCount++; + } + + if (RC_BAD( rc = m_ArgPool.poolCalloc( uiArgCount * sizeof( char *), + (void **)&m_ppCurrArgV))) + { + goto Exit; + } + + uiCurrToken = 0; + Parser.setString( pszString); + while( (pszCurrToken = Parser.getNextToken()) != NULL) + { + bQuoted = FALSE; + if( *pszCurrToken == '\"') + { + // Skip the quote character + pszCurrToken++; + bQuoted = TRUE; + } + + uiTokenLen = f_strlen( pszCurrToken); + if (!bQuoted && uiTokenLen >= 2 && *pszCurrToken == '>' && !m_pszOutputFile) + { + if (RC_BAD( rc = m_ArgPool.poolCalloc( uiTokenLen, + (void **)&m_pszOutputFile))) + { + goto Exit; + } + f_strcpy( m_pszOutputFile, pszCurrToken + 1); + } + else + { + if (RC_BAD( rc = m_ArgPool.poolCalloc( uiTokenLen + 1, + (void **)&m_ppCurrArgV [uiCurrToken]))) + { + goto Exit; + } + + f_strcpy( m_ppCurrArgV[ uiCurrToken], pszCurrToken); + + if( bQuoted) + { + // Strip off the trailing quote + m_ppCurrArgV[ uiCurrToken][ uiTokenLen - 1] = '\0'; + } + uiCurrToken++; + } + } + + m_iCurrArgC = (FLMINT)uiCurrToken; + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::executeCmdLine( void) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bValidCommand = FALSE; + FLMUINT uiLoop; + + if( !m_iCurrArgC) + { + goto Exit; + } + + // Process internal commands + + if( f_stricmp( m_ppCurrArgV[ 0], "cls") == 0) + { + FTXWinClear( m_pWindow); + bValidCommand = TRUE; + } + else if( f_stricmp( m_ppCurrArgV[ 0], "exit") == 0) + { + setShutdownFlag(); + bValidCommand = TRUE; + } +#if defined (FLM_NLM) + else if( f_stricmp( m_ppCurrArgV[ 0], "realpath") == 0) + { + bValidCommand = TRUE; + char newPath[ 256]; + int err = 0; + + if( m_iCurrArgC !=2) + { + con_printf( "Wrong Number Of Params\n"); + } + else + { + if( !realpath( m_ppCurrArgV[ 1], newPath)) + { + con_printf("Error getting real path"); + } + else + { + con_printf( "%s\n", newPath); + } + } + } +#endif + else if( f_stricmp( m_ppCurrArgV[ 0], "echo") == 0) + { + FLMBOOL bNewline = FALSE; + + if( m_iCurrArgC > 1 && + f_stricmp( m_ppCurrArgV[ 1], "-n") == 0) + { + bNewline = TRUE; + uiLoop = 2; + } + else + { + uiLoop = 1; + } + + for( ; uiLoop < (FLMUINT)m_iCurrArgC; uiLoop++) + { + con_printf( "%s", (char *)m_ppCurrArgV[ uiLoop]); + } + + if( bNewline) + { + con_printf( "\n"); + } + + bValidCommand = TRUE; + } + else if( f_stricmp( m_ppCurrArgV[ 0], "shell") == 0) + { + FlmShell * pTmpShell; + + if( (pTmpShell = f_new FlmShell) != NULL) + { + if( RC_BAD( pTmpShell->setup( m_pSharedContext))) + { + pTmpShell->Release(); + } + else + { + m_pSharedContext->spawn( pTmpShell); + } + } + bValidCommand = TRUE; + } + else if( f_stricmp( m_ppCurrArgV[ 0], "qp") == 0) + { + F_XPath xpathObj; + F_BufferIStream bufIStream; + F_Query query; + RCODE tmpRc; + + bValidCommand = TRUE; + if( m_iCurrArgC != 3) + { + con_printf( "Invalid number of arguments.\n\n"); + } + else + { + FLMUINT uiDbId; + F_Db * pDb; + + uiDbId = f_atol( m_ppCurrArgV[ 1]); + if( RC_BAD( rc = getDatabase( uiDbId, (IF_Db **)&pDb))) + { + con_printf( "Invalid database ID.\n\n"); + goto Exit; + } + + bufIStream.open( (FLMBYTE *)m_ppCurrArgV[ 2], + f_strlen( m_ppCurrArgV[ 2])); + tmpRc = xpathObj.parseQuery( pDb, &bufIStream, &query); + con_printf( "Result: 0x%08X\n\n", (unsigned)tmpRc); + } + } + else if( f_stricmp( m_ppCurrArgV[ 0], "meta") == 0) + { + F_BufferIStream bufIStream; + FLMUINT uiMeta; + FLMUINT uiAltMeta; + RCODE tmpRc; + + bValidCommand = TRUE; + if( m_iCurrArgC != 2) + { + con_printf( "Invalid number of arguments.\n\n"); + } + else + { + bufIStream.open( (FLMBYTE *)m_ppCurrArgV[ 1], + f_strlen( m_ppCurrArgV[ 1])); + for( ;;) + { + if( RC_BAD( tmpRc = flmGetNextMetaphone( &bufIStream, + &uiMeta, &uiAltMeta))) + { + if( tmpRc == NE_XFLM_EOF_HIT) + { + tmpRc = NE_XFLM_OK; + break; + } + + goto MetaExit; + } + + con_printf( "Meta = 0x%04X, AltMeta = 0x%04X\n", + uiMeta, uiAltMeta); + } + +MetaExit: + + if( RC_BAD( tmpRc)) + { + con_printf( "Error: 0x%04X\n", tmpRc); + } + } + } + else if( f_stricmp( m_ppCurrArgV[ 0], "help") == 0 || + f_stricmp( m_ppCurrArgV[ 0], "?") == 0 || + f_stricmp( m_ppCurrArgV[ 0], "h") == 0) + { + if( m_iCurrArgC < 2) + { + con_printf( "Commands:\n"); + displayCommand( "help, ?, h", "Show help"); + displayCommand( "shell", "Start a new shell"); + displayCommand( "echo", "Echo typed in command"); + displayCommand( "cls", "Clear screen"); + displayCommand( "exit", "Exit shell"); + displayCommand( "echo", "Echo typed in command"); + for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) + { + if( m_ppCmdList[ uiLoop] != NULL) + { + m_ppCmdList[ uiLoop]->displayHelp( this, NULL); + } + } + } + else + { + for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) + { + if( m_ppCmdList[ uiLoop] != NULL) + { + if (m_ppCmdList[ uiLoop]->canPerformCommand( + (char *)m_ppCurrArgV [1])) + { + m_ppCmdList[ uiLoop]->displayHelp( this, + (char *)m_ppCurrArgV [1]); + break; + } + } + } + } + bValidCommand = TRUE; + } + else + { + for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) + { + if( m_ppCmdList[ uiLoop] != NULL) + { + if( m_ppCmdList[ uiLoop]->canPerformCommand( + (char *)m_ppCurrArgV[ 0])) + { + m_ppCmdList[ uiLoop]->execute( m_iCurrArgC, m_ppCurrArgV, this); + bValidCommand = TRUE; + break; + } + } + } + } + + if( !bValidCommand) + { + FTXWinPrintf( m_pWindow, "Unrecognized command: %s\n", m_ppCurrArgV[ 0]); + rc = RC_SET( NE_XFLM_NOT_IMPLEMENTED); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::registerCmd( + FlmCommand * pCmd) +{ + RCODE rc = NE_XFLM_OK; + FLMBOOL bRegistered = FALSE; + FLMUINT uiLoop; + + for( uiLoop = 0; uiLoop < MAX_REGISTERED_COMMANDS; uiLoop++) + { + if( m_ppCmdList[ uiLoop] == NULL) + { + m_ppCmdList[ uiLoop] = pCmd; + bRegistered = TRUE; + break; + } + } + + if( !bRegistered) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::addCmdHistory( + char * pszCmd) +{ + FLMUINT uiLoop; + FLMUINT uiSlot; + FLMUINT uiCmdLen; + RCODE rc = NE_XFLM_OK; + + // If the command line is too long, don't store it in the + // history buffer + + if( (uiCmdLen = f_strlen( pszCmd)) > MAX_CMD_LINE_LEN) + { + goto Exit; + } + + // Look for a duplicate history item + + for( uiLoop = 0; uiLoop < MAX_SHELL_HISTORY_ITEMS; uiLoop++) + { + if( m_ppHistory[ uiLoop] && + f_strcmp( pszCmd, m_ppHistory[ uiLoop]) == 0) + { + // Remove the command from the history list and compress + // the history table + + f_free( &m_ppHistory[ uiLoop]); + + if( uiLoop < MAX_SHELL_HISTORY_ITEMS - 1) + { + f_memmove( &m_ppHistory[ uiLoop], &m_ppHistory[ uiLoop + 1], + sizeof( char *) * (MAX_SHELL_HISTORY_ITEMS - uiLoop - 1)); + m_ppHistory[ MAX_SHELL_HISTORY_ITEMS - 1] = NULL; + break; + } + } + } + + // Find an empty slot for the new history item + + for( uiSlot = MAX_SHELL_HISTORY_ITEMS; uiSlot > 0; uiSlot--) + { + if( m_ppHistory[ uiSlot - 1]) + { + break; + } + } + + if( uiSlot == MAX_SHELL_HISTORY_ITEMS) + { + f_free( &m_ppHistory[ 0]); + f_memmove( &m_ppHistory[ 0], &m_ppHistory[ 1], + sizeof( char *) * (MAX_SHELL_HISTORY_ITEMS - 1)); + m_ppHistory[ MAX_SHELL_HISTORY_ITEMS - 1] = NULL; + uiSlot = MAX_SHELL_HISTORY_ITEMS - 1; + } + + if( RC_BAD( rc = f_alloc( uiCmdLen + 1, &m_ppHistory[ uiSlot]))) + { + goto Exit; + } + + f_strcpy( m_ppHistory[ uiSlot], pszCmd); + +Exit: + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE FlmShell::execute( void) +{ + char szBuffer[ MAX_CMD_LINE_LEN + 1]; + char szThreadName[ MAX_THREAD_NAME_LEN + 1]; + FLMUINT uiTermChar; + FLMUINT uiRow; + FLMUINT uiLastHistorySlot = MAX_SHELL_HISTORY_ITEMS; + RCODE rc = NE_XFLM_OK; + char szDir [F_PATH_MAX_SIZE]; + DirectoryIterator directoryIterator; + char * pszTabCompleteBegin = NULL; + + + if( FTXScreenInit( m_pSharedContext->getFtxInfo(), + "xshell main", &m_pScreen) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if( FTXScreenInitStandardWindows( m_pScreen, WPS_RED, WPS_WHITE, WPS_BLUE, + WPS_WHITE, FALSE, FALSE, NULL, &m_pTitleWin, &m_pWindow) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + if( FTXScreenDisplay( m_pScreen) != FTXRC_SUCCESS) + { + rc = RC_SET( NE_XFLM_FAILURE); + goto Exit; + } + + FTXScreenSetShutdownFlag( m_pScreen, getShutdownFlagAddr()); + + szBuffer[ 0] = '\0'; + for( ;;) + { + // Refresh the title bar + getName( szThreadName); + FTXWinSetCursorPos( m_pTitleWin, 0, 0); + FTXWinPrintf( m_pTitleWin, "%5.5u: %s", + (unsigned)getID(), szThreadName); + FTXWinClearToEOL( m_pTitleWin); + + // Check for shutdown + if( getShutdownFlag()) + { + break; + } + + FTXWinGetCursorPos( m_pWindow, NULL, &uiRow); + FTXWinSetCursorPos( m_pWindow, 0, uiRow); + FTXWinClearToEOL( m_pWindow); + +#if defined( FLM_NLM) + szDir [0] = 0; +#elif defined( FLM_WIN) + if (_getcwd( (char *)szDir, F_PATH_MAX_SIZE) == NULL) + { + szDir [0] = '\0'; + } +#elif defined( FLM_UNIX) + if (getcwd( (char *)szDir, F_PATH_MAX_SIZE) == NULL) + { + szDir [0] = '\0'; + } +#else + #error "This platform is not supported" +#endif + + FTXWinPrintf( m_pWindow, "%s>", szDir); + + if( FTXLineEdit( m_pWindow, szBuffer, + MAX_CMD_LINE_LEN, 255, 0, &uiTermChar)) + { + break; + } + + if( uiTermChar == WPK_TAB) + { + char szBase[ 255]; + char szWildcard[ 255]; + + szWildcard[0] = '\0'; + + pszTabCompleteBegin = positionToPath( szBuffer); + + if ( f_strchr( pszTabCompleteBegin, '\"')) + { + // remove quotes + removeChars( pszTabCompleteBegin, '\"'); + } + + // If we have not initialized our iterator to scan this directory + // or if the command-line does not contain a path that we provided + // we need to reinitialize the iterator. + + if( !directoryIterator.isInitialized() || + !pszTabCompleteBegin || + !directoryIterator.isInSet( pszTabCompleteBegin)) + { + + extractBaseDirAndWildcard( pszTabCompleteBegin, szBase, szWildcard); + + directoryIterator.reset(); + directoryIterator.setupForSearch( szDir, szBase, szWildcard); + } + + if ( !directoryIterator.isEmpty()) + { + // Copy in the next entry along with its full path. + + directoryIterator.next( pszTabCompleteBegin, TRUE); + + } + else + { + ftxBeep(); + } + + // If the completed path contains spaces, quote it + if ( f_strchr( pszTabCompleteBegin, ASCII_SPACE)) + { + f_memmove( pszTabCompleteBegin + 1, pszTabCompleteBegin, + f_strlen( pszTabCompleteBegin) + 1); + pszTabCompleteBegin[0] = '\"'; + + f_strcat( pszTabCompleteBegin, "\""); + } + continue; + } + + directoryIterator.reset(); + + if( uiTermChar == WPK_UP) + { + for(; uiLastHistorySlot > 0; uiLastHistorySlot--) + { + if( m_ppHistory[ uiLastHistorySlot - 1]) + { + f_strcpy( szBuffer, m_ppHistory[ uiLastHistorySlot - 1]); + uiLastHistorySlot--; + break; + } + } + + continue; + } + + if( uiTermChar == WPK_DOWN) + { + for(; uiLastHistorySlot < MAX_SHELL_HISTORY_ITEMS - 1; uiLastHistorySlot++) + { + if( m_ppHistory[ uiLastHistorySlot + 1]) + { + f_strcpy( szBuffer, m_ppHistory[ uiLastHistorySlot + 1]); + uiLastHistorySlot++; + break; + } + } + continue; + } + + if( uiTermChar == WPK_ESCAPE) + { + szBuffer[ 0] = '\0'; + continue; + } + + if( uiTermChar == WPK_F1) + { + FLMUINT uiLen; + F_FileIStream fileIStream; + + addCmdHistory( szBuffer); + + if( RC_BAD( rc = fileIStream.open( szBuffer))) + { + goto BatchError; + } + + for( ;;) + { + FTXWinPrintf( m_pWindow, "\n"); + + uiLen = MAX_CMD_LINE_LEN; + if( RC_BAD( rc = flmReadLine( + &fileIStream, (FLMBYTE *)szBuffer, &uiLen))) + { + if( rc == NE_XFLM_EOF_HIT) + { + rc = NE_XFLM_OK; + if( !uiLen) + { + break; + } + } + goto BatchError; + } + + parseCmdLine( szBuffer); + szBuffer[ 0] = '\0'; + + if( RC_BAD( rc = executeCmdLine())) + { + goto BatchError; + } + } + +BatchError: + + if( RC_BAD( rc)) + { + FTXWinPrintf( m_pWindow, + "Error: %e. Batch execution halted.\n", rc); + rc = NE_XFLM_OK; + } + continue; + } + + uiLastHistorySlot = MAX_SHELL_HISTORY_ITEMS; + + if( szBuffer [0]) + { + FTXWinPrintf( m_pWindow, "\n"); + addCmdHistory( szBuffer); + parseCmdLine( szBuffer); + executeCmdLine(); + szBuffer[0] = '\0'; + + continue; + } + + FTXWinPrintf( m_pWindow, "\n"); + } + +Exit: + + if( m_pWindow) + { + FTXWinFree( &m_pWindow); + } + + if( m_pScreen) + { + FTXScreenFree( &m_pScreen); + } + + return( rc); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FlmParse::FlmParse( void) +{ + m_szString [0] = 0; + m_pszCurPos = &m_szString [0]; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmParse::setString( + char * pszString) +{ + if( pszString) + { + f_strcpy( m_szString, pszString); + } + else + { + m_szString [0]= 0; + } + + m_pszCurPos = &m_szString [0]; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +char * FlmParse::getNextToken( void) +{ + char * pszTokenPos = &m_szToken [0]; + FLMBOOL bQuoted = FALSE; + + while( *m_pszCurPos && *m_pszCurPos == ' ') + { + m_pszCurPos++; + } + + if( *m_pszCurPos == '$') + { + *pszTokenPos++ = *m_pszCurPos++; + while( *m_pszCurPos) + { + if( (*m_pszCurPos >= 'A' && *m_pszCurPos <= 'Z') || + (*m_pszCurPos >= 'a' && *m_pszCurPos <= 'z') || + (*m_pszCurPos >= '0' && *m_pszCurPos <= '9') || + (*m_pszCurPos == '_')) + { + *pszTokenPos++ = *m_pszCurPos++; + } + else + { + break; + } + } + } + else if( *m_pszCurPos == '=') + { + *pszTokenPos++ = *m_pszCurPos++; + } + else + { + while( *m_pszCurPos && (*m_pszCurPos != ' ' || bQuoted)) + { + if( *m_pszCurPos == '\"') + { + *pszTokenPos++ = *m_pszCurPos++; + if( bQuoted) + { + break; + } + else + { + bQuoted = TRUE; + } + } + else + { + *pszTokenPos++ = *m_pszCurPos++; + } + } + } + + *pszTokenPos = '\0'; + + if( m_szToken [0] == 0) + { + return( NULL); + } + + return( &m_szToken [0]); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmDbOpenCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMINT iExitCode = 0; + IF_Db * pDb = NULL; + FLMUINT uiDbId; + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + char * pszRflDir = NULL; + char * pszPassword = NULL; + char * pszAllowLimited; + FLMBOOL bAllowLimited = FALSE; + + if( iArgC < 2) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if( iArgC >= 3) + { + pszRflDir = ppszArgV[ 2]; + } + + if (iArgC >=4) + { + pszPassword = ppszArgV[ 3]; + } + + if (iArgC >=5) + { + pszAllowLimited = ppszArgV[ 4]; + + if (f_strnicmp( pszAllowLimited, "TRUE", 4) == 0) + { + bAllowLimited = TRUE; + } + } + + if( RC_BAD( rc = dbSystem.dbOpen( ppszArgV[ 1], + NULL, pszRflDir, pszPassword, bAllowLimited, &pDb))) + { + if( rc != NE_XFLM_IO_PATH_NOT_FOUND) + { + goto Exit; + } + + if( RC_BAD( rc = dbSystem.dbCreate( + ppszArgV[ 1], NULL, pszRflDir, NULL, NULL, NULL, &pDb))) + { + goto Exit; + } + } + + if( RC_BAD( rc = pShell->registerDatabase( pDb, &uiDbId))) + { + goto Exit; + } + pDb = NULL; + + pShell->con_printf( "Database #%u opened.\n", (unsigned)uiDbId); + +Exit: + + if( pDb) + { + pDb->Release(); + } + + if( RC_BAD( rc)) + { + pShell->con_printf( "Error opening database: %e\n", rc); + iExitCode = -1; + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmDbOpenCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbopen", "Open a database"); + } + else + { + pShell->con_printf("Usage:\n" + " dbopen [ [ []]]\n"); + pShell->con_printf(" : TRUE | FALSE \n"); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmDbOpenCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "dbopen", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmDbCloseCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMINT iExitCode = 0; + FLMUINT uiDbId; + IF_Db * pDb; + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + + if( iArgC != 2) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if( !f_stricmp( ppszArgV[ 1], "kill")) + { + dbSystem.deactivateOpenDb( NULL, NULL); + pShell->con_printf( "All handles killed, but not necessarily closed.\n"); + } + else if( !f_stricmp( ppszArgV[ 1], "all")) + { + for( uiDbId = 0; uiDbId < MAX_SHELL_OPEN_DB; uiDbId++) + { + if( RC_BAD( rc = pShell->getDatabase( uiDbId, &pDb))) + { + goto Exit; + } + + if( pDb) + { + if( RC_BAD( rc = pShell->deregisterDatabase( uiDbId))) + { + goto Exit; + } + pDb->Release(); + pShell->con_printf( "Database #%u closed.\n", (unsigned)uiDbId); + } + } + + dbSystem.closeUnusedFiles( 0); + } + else + { + uiDbId = f_atol( ppszArgV[ 1]); + if( RC_BAD( rc = pShell->getDatabase( uiDbId, &pDb))) + { + goto Exit; + } + + if( pDb) + { + if( RC_BAD( rc = pShell->deregisterDatabase( uiDbId))) + { + goto Exit; + } + pDb->Release(); + pShell->con_printf( "Database #%u closed.\n", (unsigned)uiDbId); + } + else + { + pShell->con_printf( "Database #%u already closed.\n", (unsigned)uiDbId); + } + } + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "Error closing database: %e\n", rc); + iExitCode = -1; + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmDbCloseCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbclose", "Close a database"); + } + else + { + pShell->con_printf("Usage:\n" + " dbclose \n"); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmDbCloseCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "dbclose", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmTransCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMINT iExitCode = 0; + FLMUINT uiDbId; + FLMUINT uiTimeout; + eDbTransType eTransType; + IF_Db * pDb; + RCODE rc = NE_XFLM_OK; + + if( iArgC < 2) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + // Get the database ID and handle + + uiDbId = f_atol( ppszArgV[ 1]); + if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) + { + pShell->con_printf( "Invalid database.\n"); + iExitCode = -1; + goto Exit; + } + + eTransType = ((F_Db *)pDb)->getTransType(); + if( f_stricmp( ppszArgV [0], "trbegin") == 0) + { + if( iArgC < 3) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if( eTransType != XFLM_NO_TRANS) + { + pShell->con_printf( "%s transaction is already active on database %u.\n", + (char *)(eTransType == XFLM_READ_TRANS + ? "A read" + : "An update"), (unsigned)uiDbId); + iExitCode = -1; + goto Exit; + } + + if( !f_stricmp( ppszArgV[ 2], "read")) + { + if( RC_BAD( rc = pDb->transBegin( XFLM_READ_TRANS))) + { + goto Exit; + } + } + else if( !f_stricmp( ppszArgV[ 2], "update")) + { + uiTimeout = 10; + if( iArgC > 4) + { + uiTimeout = f_atol( ppszArgV[ 3]); + } + + if( RC_BAD( rc = pDb->transBegin( XFLM_UPDATE_TRANS, uiTimeout))) + { + goto Exit; + } + } + else + { + pShell->con_printf( "Invalid parameter: %s\n", ppszArgV[ 3]); + iExitCode = -1; + goto Exit; + } + + pShell->con_printf( "Transaction on %u started.\n", (unsigned)uiDbId); + } + else if( f_stricmp( ppszArgV[ 0], "trcommit") == 0) + { + if( eTransType == XFLM_NO_TRANS) + { + pShell->con_printf( "There is no active transaction on database %u.\n", + (unsigned)uiDbId); + iExitCode = -1; + goto Exit; + } + + if( RC_BAD( rc = pDb->transCommit())) + { + goto Exit; + } + pShell->con_printf( "Transaction committed on database %u.\n", + (unsigned)uiDbId); + } + else if( f_stricmp( ppszArgV[ 0], "trabort") == 0) + { + if( eTransType == XFLM_NO_TRANS) + { + pShell->con_printf( "There is no active transaction on database %u.\n", + (unsigned)uiDbId); + iExitCode = -1; + goto Exit; + } + + if( RC_BAD( rc = pDb->transAbort())) + { + goto Exit; + } + pShell->con_printf( "Transaction aborted on database %u.\n", + (unsigned)uiDbId); + } + else + { + // should never be able to get here! + flmAssert( 0); + iExitCode = -1; + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmTransCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "trbegin", "Begin a transaction"); + pShell->displayCommand( "trcommit", "Commit a transaction"); + pShell->displayCommand( "trabort", "Abort a transaction"); + } + else + { + pShell->con_printf("Usage:\n"); + if (f_stricmp( pszCommand, "trbegin") == 0) + { + pShell->con_printf( " trbegin db# [read | update ]\n"); + } + else + { + pShell->con_printf( " %s db#\n", pszCommand); + } + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmTransCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "trbegin", pszCommand) == 0 || + f_stricmp( "trcommit", pszCommand) == 0 || + f_stricmp( "trabort", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: Status class for reporting progress of database copy +*****************************************************************************/ +class FSHELL_CopyStatus : public IF_DbCopyStatus +{ +public: + FSHELL_CopyStatus( + FlmShell * pShell) + { + m_pShell = pShell; + m_pWin = m_pShell->getWindow(); + } + + virtual ~FSHELL_CopyStatus() + { + } + + RCODE XFLMAPI dbCopyStatus( + FLMUINT64 ui64BytesToCopy, + FLMUINT64 ui64BytesCopied, + FLMBOOL bNewSrcFile, + const char * pszSrcFileName, + const char * pszDestFileName) + { + RCODE rc = NE_XFLM_OK; + + if (bNewSrcFile) + { + FTXWinPrintf( m_pWin, "\nCopying %s to %s ...\n", + pszSrcFileName, pszDestFileName); + } + + if( m_pShell->getShutdownFlag()) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + + FTXWinPrintf( m_pWin, " %,I64u of %,I64u bytes copied\r", + ui64BytesCopied, ui64BytesToCopy); + f_yieldCPU(); + #ifdef FLM_WIN + f_sleep( 0); + #endif + + if (FTXWinTestKB( m_pWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( m_pWin, &uiChar); + if (uiChar == WPK_ESC) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + + Exit: + + return( rc); + } + +private: + + FlmShell * m_pShell; + FTX_WINDOW * m_pWin; +}; + +/**************************************************************************** +Desc: Status class for reporting progress of database rename +*****************************************************************************/ +class FSHELL_RenameStatus : public IF_DbRenameStatus +{ +public: + FSHELL_RenameStatus( + FlmShell * pShell) + { + m_pShell = pShell; + } + + virtual ~FSHELL_RenameStatus() + { + } + + FINLINE RCODE XFLMAPI dbRenameStatus( + const char * pszSrcFileName, + const char * pszDstFileName) + { + m_pShell->con_printf( "Renaming %s to %s ...\n", pszSrcFileName, + pszDstFileName); + return( NE_XFLM_OK); + } + +private: + FlmShell * m_pShell; +}; + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmDbManageCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMINT iExitCode = 0; + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + + if( iArgC < 2) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if (f_stricmp( ppszArgV [0], "dbremove") == 0) + { + if (RC_BAD( rc = dbSystem.dbRemove( ppszArgV[ 1], NULL, NULL, TRUE))) + { + goto Exit; + } + } + else + { + if( iArgC < 3) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + if (f_stricmp( ppszArgV [0], "dbcopy") == 0) + { + FSHELL_CopyStatus copyStatus( pShell); + + if (RC_BAD( rc = dbSystem.dbCopy( ppszArgV [1], NULL, NULL, + ppszArgV [2], NULL, NULL, ©Status))) + { + goto Exit; + } + pShell->con_printf( "\n\n"); + } + else + { + FSHELL_RenameStatus renameStatus( pShell); + + if (RC_BAD( rc = dbSystem.dbRename( ppszArgV [1], NULL, NULL, + ppszArgV [2], TRUE, &renameStatus))) + { + goto Exit; + } + pShell->con_printf( "\n\n"); + } + } + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmDbManageCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbcopy", "Copy a database"); + pShell->displayCommand( "dbrename", "Rename a database"); + pShell->displayCommand( "dbremove", "Delete a database"); + } + else + { + pShell->con_printf("Usage:\n"); + if (f_stricmp( pszCommand, "dbremove") == 0) + { + pShell->con_printf( " dbremove \n"); + } + else + { + pShell->con_printf( " %s \n", + pszCommand); + } + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmDbManageCommand::canPerformCommand( + char * pszCommand ) +{ + return( (f_stricmp( "dbcopy", pszCommand) == 0 || + f_stricmp( "dbrename", pszCommand) == 0 || + f_stricmp( "dbremove", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmBackupCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMUINT uiDbId; + FLMUINT uiIncSeqNum; + IF_Db * pDb; + IF_Backup * pBackup = NULL; + IF_BackupClient * pBackupClient = NULL; + IF_BackupStatus * pBackupStatus = NULL; + FLMINT iExitCode = 0; + eDbBackupType eBackupType = XFLM_FULL_BACKUP; + RCODE rc = NE_XFLM_OK; + FLMBOOL bUsePasswd = FALSE; + + if( iArgC < 3) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if (iArgC > 3) + { + bUsePasswd = TRUE; + } + + if( iArgC > 4) + { + if( f_strnicmp( ppszArgV[ 3], "inc", 3) == 0) + { + eBackupType = XFLM_INCREMENTAL_BACKUP; + } + } + + // Get the database ID and handle + uiDbId = f_atol( ppszArgV[ 1]); + if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) + { + pShell->con_printf( "Invalid database.\n"); + iExitCode = -1; + goto Exit; + } + + if( RC_BAD( rc = pDb->backupBegin( eBackupType, + XFLM_READ_TRANS, 0, &pBackup))) + { + goto Exit; + } + + if( (pBackupClient = f_new F_LocalBackupClient( + pShell, ppszArgV[ 2])) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( (pBackupStatus = f_new F_LocalBackupStatus( pShell)) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pBackup->backup( + ppszArgV[ 2], + bUsePasswd?ppszArgV[3]:NULL, + pBackupClient, pBackupStatus, &uiIncSeqNum))) + { + goto Exit; + } + + if( RC_BAD( rc = pBackup->endBackup())) + { + goto Exit; + } + + pShell->con_printf( "\nBackup complete.\n"); + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + if( pBackup) + { + pBackup->Release(); + } + + if( pBackupClient) + { + pBackupClient->Release(); + } + + if( pBackupStatus) + { + pBackupStatus->Release(); + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmBackupCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbbackup", "Backup a database"); + } + else + { + pShell->con_printf("Usage:\n"); + pShell->con_printf( " %s [ [\"INC\"]]\n", pszCommand); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmBackupCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "dbbackup", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +RCODE F_LocalBackupStatus::backupStatus( + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + if( m_pShell->getShutdownFlag()) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + + FTXWinPrintf( pWin, "%,I64u / %,I64u bytes backed up\r", + ui64BytesDone, ui64BytesToDo); + f_yieldCPU(); +#ifdef FLM_WIN + f_sleep( 0); +#endif + + if( pWin && FTXWinTestKB( pWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pWin, &uiChar); + if (uiChar == WPK_ESC) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + +Exit: + + return( rc); +} + + +/************************************************************************ +If we want status info for backups, we need to implement an IF_BackupClient..... + +RCODE flmBackupProgFunc( + FLMUINT uiStatusType, + void * Parm1, + void * Parm2, + void * UserData) +{ + RCODE rc = NE_XFLM_OK; + FLMUINT64 ui64BytesDone; + FLMUINT64 ui64BytesToDo; + FlmShell * pShell = (FlmShell *)UserData; + FTX_WINDOW * pWin = pShell->getWindow(); + + F_UNREFERENCED_PARM( Parm2); + F_UNREFERENCED_PARM( UserData); + + if( pShell->getShutdownFlag()) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + + if( uiStatusType == FLM_DB_BACKUP_STATUS) + { + pDbBackupInfo = (DB_BACKUP_INFO *)Parm1; + FTXWinPrintf( pWin, "%,I64u / %,I64u bytes backed up\r", + ui64BytesDone, ui64BytesToDo); + } + + f_yieldCPU(); + + if( pWin && FTXWinTestKB( pWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pWin, &uiChar); + if (uiChar == WPK_ESC) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } + +Exit: + + return( rc); +} +***********************************************************************/ + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmRestoreCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + char * pszRflDir = NULL; + FLMINT iExitCode = 0; + F_LocalRestore * pRestore = NULL; + F_LocalRestoreStatus restoreStatus( pShell); + RCODE rc = NE_XFLM_OK; + F_DbSystem dbSystem; + FLMBOOL bUsePasswd = FALSE; + + if( iArgC < 3) + { + pShell->con_printf( "Wrong number of parameters.\n"); + iExitCode = -1; + goto Exit; + } + if( iArgC > 3) + { + bUsePasswd = TRUE; + } + if( iArgC > 4) + { + pszRflDir = ppszArgV[ 4]; + } + + if( (pRestore = f_new F_LocalRestore) == NULL) + { + rc = RC_SET( NE_XFLM_MEM); + goto Exit; + } + + if( RC_BAD( rc = pRestore->setup( + ppszArgV[ 1], ppszArgV[ 2], pszRflDir))) + { + goto Exit; + } + + if( RC_BAD( rc = dbSystem.dbRestore( ppszArgV[ 1], NULL, NULL, NULL, + bUsePasswd?ppszArgV[3]:NULL, pRestore, &restoreStatus))) + { + goto Exit; + } + + pShell->con_printf( "\nRestore complete.\n"); + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + if( pRestore) + { + pRestore->Release(); + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmRestoreCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbrestore", "Restore a database"); + } + else + { + pShell->con_printf("Usage:\n"); + pShell->con_printf( " %s [ []]\n", pszCommand); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmRestoreCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "dbrestore", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: The report* functions are all status callbacks that allow XFlaim to + pass some information back out +*****************************************************************************/ +void F_LocalRestoreStatus::updateCountDisplay( void) +{ + FTX_WINDOW * pWin = m_pShell->getWindow(); + + if (pWin) + { + FTXWinSetCursorPos( pWin, 0, 2); + FTXWinPrintf( pWin, + "RFLFile#: %-10u TotalCnt: %-10u RflKBytes: %uK\n" + "AddCnt: %-10u DelCnt: %-10u ModCnt: %u\n" + "TrCnt: %-10u RsrvCnt: %-10u IxSetCnt: %u", + m_uiRflFileNum, + m_uiTransCount + m_uiAddCount + m_uiDeleteCount + + m_uiModifyCount + m_uiReserveCount + m_uiIndexCount, + (unsigned)(m_ui64RflBytesRead / 1024), + m_uiAddCount, m_uiDeleteCount, m_uiModifyCount, + m_uiTransCount, m_uiReserveCount, m_uiIndexCount); + } +} + +// Contains some common code that all of the report* functions call after +// their primary processing... +RCODE F_LocalRestoreStatus::report_preamble( + FTX_WINDOW * pWin) +{ + RCODE rc = NE_XFLM_OK; + + if( m_pShell->getShutdownFlag()) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + + if( m_bFirstStatus) + { + FTXWinClear( pWin); + m_bFirstStatus = FALSE; + } +Exit: + return rc; +} + +// Contains some common code that all of the report* functions call after +// their primary processing... +RCODE F_LocalRestoreStatus::report_postamble( + FTX_WINDOW * pWin) +{ + RCODE rc = NE_XFLM_OK; + + FTXWinSetCursorPos( pWin, 0, 5); + + f_yieldCPU(); + + if( pWin && FTXWinTestKB( pWin) == FTXRC_SUCCESS) + { + FLMUINT uiChar; + + FTXWinInputChar( pWin, &uiChar); + if (uiChar == WPK_ESC) + { + rc = RC_SET( NE_XFLM_USER_ABORT); + goto Exit; + } + } +Exit: + return rc; + +} + +RCODE F_LocalRestoreStatus::reportProgress( + eRestoreAction * peAction, + FLMUINT64 ui64BytesToDo, + FLMUINT64 ui64BytesDone) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 1); + FTXWinPrintf( pWin, "%,I64u / %,I64u bytes restored", ui64BytesDone, + ui64BytesToDo); + FTXWinClearToEOL( pWin); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + + return rc; +} + +RCODE F_LocalRestoreStatus::reportError( + eRestoreAction * peAction, + RCODE rcErr) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + FLMUINT uiChar; + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 6); + FTXWinClearToEOL( pWin); + FTXWinPrintf( pWin, "Error: %s. Retry (Y/N): ", + F_DbSystem::_errorString( (RCODE)rcErr)); + if( FTXWinInputChar( pWin, &uiChar) != FTXRC_SUCCESS) + { + uiChar = 0; + goto Exit; + } + + if( uiChar == 'Y' || uiChar == 'y') + { + *peAction = XFLM_RESTORE_ACTION_RETRY; + } + + FTXWinClearToEOL( pWin); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; + +} + +RCODE F_LocalRestoreStatus::reportBeginTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 5); + FTXWinPrintf( pWin, "BEGIN_TRANS: ID = 0x%I64X", ui64TransId); + FTXWinClearToEOL( pWin); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; +} + +RCODE F_LocalRestoreStatus::reportCommitTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 5); + FTXWinPrintf( pWin, "COMMIT_TRANS: ID = 0x%I64X", ui64TransId); + FTXWinClearToEOL( pWin); + m_uiTransCount++; + updateCountDisplay(); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; +} + +RCODE F_LocalRestoreStatus::reportAbortTrans( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 5); + FTXWinPrintf( pWin, "ABORT_TRANS: ID = 0x%I64X", ui64TransId); + FTXWinClearToEOL( pWin); + m_uiTransCount++; + updateCountDisplay(); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; +} + + +RCODE F_LocalRestoreStatus::reportEnableEncryption( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 5); + FTXWinPrintf( pWin, "ENABLE_ENCRYPTION: ID = 0x%I64X", ui64TransId); + FTXWinClearToEOL( pWin); + m_uiTransCount++; + updateCountDisplay(); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; +} + + +RCODE F_LocalRestoreStatus::reportWrapKey( + eRestoreAction * peAction, + FLMUINT64 ui64TransId) +{ + RCODE rc = NE_XFLM_OK; + FTX_WINDOW * pWin = m_pShell->getWindow(); + + *peAction = XFLM_RESTORE_ACTION_CONTINUE; + + if (RC_BAD(rc = report_preamble( pWin))) + { + goto Exit; + } + + FTXWinSetCursorPos( pWin, 0, 5); + FTXWinPrintf( pWin, "WRAP_KEY: ID = 0x%I64X", ui64TransId); + FTXWinClearToEOL( pWin); + m_uiTransCount++; + updateCountDisplay(); + + if (RC_BAD(rc = report_postamble( pWin))) + { + goto Exit; + } + +Exit: + return rc; +} + + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmDbConfigCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMUINT uiDbId; + IF_Db * pDb; + FLMINT iExitCode = 0; + RCODE rc = NE_XFLM_OK; + + if( iArgC < 3) + { + pShell->con_printf( "Too few parameters.\n"); + iExitCode = -1; + goto Exit; + } + + // Get the database ID and handle + uiDbId = f_atol( ppszArgV[ 1]); + if( RC_BAD( pShell->getDatabase( uiDbId, &pDb)) || !pDb) + { + pShell->con_printf( "Invalid database.\n"); + iExitCode = -1; + goto Exit; + } + if( f_stricmp( ppszArgV[ 2], "rflkeepfiles") == 0) + { + FLMBOOL bEnable; + + if( iArgC < 4) + { + pShell->con_printf( "Too few parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if (f_stricmp( ppszArgV[ 3], "on") == 0) + { + bEnable = TRUE; + } + else if (f_stricmp( ppszArgV[ 3], "off") == 0) + { + bEnable = FALSE; + } + else + { + pShell->con_printf( "Invalid value, must be 'on' or 'off'.\n"); + iExitCode = -1; + goto Exit; + } + if( RC_BAD( rc = ((F_Db *)pDb)->setRflKeepFilesFlag( bEnable))) + { + goto Exit; + } + } + else if( f_stricmp( ppszArgV[ 2], "rfldir") == 0) + { + if( iArgC < 4) + { + pShell->con_printf( "Too few parameters.\n"); + iExitCode = -1; + goto Exit; + } + + if( RC_BAD( rc = ((F_Db *)pDb)->setRflDir( ppszArgV[ 3]))) + { + goto Exit; + } + } + else if( f_stricmp( ppszArgV[ 2], "rflfilelimits") == 0) + { + FLMUINT uiRflMinSize; + FLMUINT uiRflMaxSize; + + if( iArgC < 5) + { + pShell->con_printf( "Too few parameters.\n"); + iExitCode = -1; + goto Exit; + } + + uiRflMinSize = f_atol( ppszArgV[ 3]); + uiRflMaxSize = f_atol( ppszArgV[ 4]); + if( RC_BAD( rc = ((F_Db *)pDb)->setRflFileSizeLimits( + uiRflMinSize, uiRflMaxSize))) + { + goto Exit; + } + } + else if( f_stricmp( ppszArgV[ 2], "rflrolltonextfile") == 0) + { + if( RC_BAD( rc = ((F_Db *)pDb)->rflRollToNextFile())) + { + goto Exit; + } + } + else + { + pShell->con_printf( "Invalid option.\n"); + iExitCode = -1; + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmDbConfigCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbconfig", "Configure a database"); + } + else + { + pShell->con_printf("Usage:\n"); + pShell->con_printf( " %s rflkeepfiles \n", pszCommand); + pShell->con_printf( " %s rfldir \n", pszCommand); + pShell->con_printf( " %s rflfilelimits \n", + pszCommand); + pShell->con_printf( " %s rolltonextfile\n", pszCommand); + } +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMBOOL FlmDbConfigCommand::canPerformCommand( + char * pszCommand) +{ + return( (f_stricmp( "dbconfig", pszCommand) == 0) + ? TRUE + : FALSE); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FSTATIC void format64BitNum( + FLMUINT64 ui64Num, + char * pszBuf, + FLMBOOL bOutputHex, + FLMBOOL bAddCommas + ) +{ + char szTmpBuf [60]; + FLMUINT uiDigit; + FLMUINT uiChars = 0; + FLMUINT uiCharsBetweenCommas; + + if (bOutputHex) + { + while (ui64Num) + { + uiDigit = (FLMUINT)(ui64Num & 0xF); + szTmpBuf [uiChars++] = (char)(uiDigit + '0'); + ui64Num >>= 4; + } + } + else + { + uiCharsBetweenCommas = 0; + while (ui64Num) + { + if (bAddCommas && uiCharsBetweenCommas == 3) + { + szTmpBuf [uiChars++] = ','; + uiCharsBetweenCommas = 0; + } + uiDigit = (FLMUINT)(ui64Num % 10); + szTmpBuf [uiChars++] = (char)(uiDigit + '0'); + ui64Num /= 10; + uiCharsBetweenCommas++; + } + } + + // Need to reverse the numbers going back out. + + while (uiChars) + { + uiChars--; + *pszBuf++ = szTmpBuf [uiChars]; + } + *pszBuf = 0; +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +FLMINT FlmDbGetConfigCommand::execute( + FLMINT iArgC, + char ** ppszArgV, + FlmShell * pShell) +{ + FLMUINT uiDbId; + IF_Db * pIDb; + F_Db * pDb; + FLMINT iExitCode = 0; + FLMUINT64 ui64Arg; + FLMUINT uiArg; + FLMUINT uiArg2; + FLMBOOL bArg; + char szTmpPath[ F_PATH_MAX_SIZE]; + char ucBuf[ 256]; + RCODE rc = NE_XFLM_OK; + FLMBOOL bDoAll = FALSE; + FLMBOOL bValidOption = FALSE; + + if( iArgC < 3) + { + pShell->con_printf( "Too few parameters.\n"); + iExitCode = -1; + goto Exit; + } + + // Get the database ID and handle + uiDbId = f_atol( ppszArgV[ 1]); + if( RC_BAD( pShell->getDatabase( uiDbId, &pIDb)) || !pIDb) + { + pShell->con_printf( "Invalid database.\n"); + iExitCode = -1; + goto Exit; + } + pDb = (F_Db *)pIDb; + if (f_stricmp( ppszArgV [2], "all") == 0) + { + bDoAll = TRUE; + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "rfldir") == 0) + { + pDb->getRflDir( szTmpPath); + pShell->con_printf( "RFL directory = %s\n", szTmpPath); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "rflfilenum") == 0) + { + pDb->getRflFileNum( &uiArg); + pShell->con_printf( "Current RFL file # = %u\n", + (unsigned)uiArg); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "rflsizelimits") == 0) + { + pDb->getRflFileSizeLimits( &uiArg, &uiArg2); + pShell->con_printf( "RFL file size limits = min:%u, max:%u\n", + (unsigned)uiArg, (unsigned)uiArg2); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "diskusage") == 0) + { + FLMUINT64 ui64DbSize; + FLMUINT64 ui64RollbackSize; + FLMUINT64 ui64RflSize; + char szBuf1 [40]; + char szBuf2 [40]; + char szBuf3 [40]; + + if( RC_BAD( rc = pDb->getDiskSpaceUsage( + &ui64DbSize, &ui64RollbackSize, &ui64RflSize))) + { + goto Exit; + } + + format64BitNum( ui64DbSize, szBuf1, FALSE); + format64BitNum( ui64RollbackSize, szBuf2, FALSE); + format64BitNum( ui64RflSize, szBuf3, FALSE); + pShell->con_printf( "Sizes = db:%s, rollback:%s, rfl:%s", + szBuf1, szBuf2, szBuf3); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "rflkeepfiles") == 0) + { + if( RC_BAD( rc = pDb->getRflKeepFlag( &bArg))) + { + goto Exit; + } + + pShell->con_printf( "Keep RFL files = %s\n", + bArg ? "Yes" : "No"); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "lastbackuptransid") == 0) + { + if( RC_BAD( rc = pDb->getLastBackupTransID( &ui64Arg))) + { + goto Exit; + } + + //VISIT: Use formatter for 64 bit unsigned + pShell->con_printf( "Last backup transaction ID = %u\n", + (unsigned)ui64Arg); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "blockschangedsincebackup") == 0) + { + if( RC_BAD( rc = pDb->getBlocksChangedSinceBackup( &uiArg))) + { + goto Exit; + } + + pShell->con_printf( "Blocks changed since last backup = %u\n", + (unsigned)uiArg); + bValidOption = TRUE; + } + + if( bDoAll || f_stricmp( ppszArgV[ 2], "serialnumber") == 0) + { + pDb->getSerialNumber( ucBuf); + + pShell->con_printf( + "Serial number = " + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", + (unsigned)ucBuf[ 0], + (unsigned)ucBuf[ 1], + (unsigned)ucBuf[ 2], + (unsigned)ucBuf[ 3], + (unsigned)ucBuf[ 4], + (unsigned)ucBuf[ 5], + (unsigned)ucBuf[ 6], + (unsigned)ucBuf[ 7], + (unsigned)ucBuf[ 8], + (unsigned)ucBuf[ 9], + (unsigned)ucBuf[ 10], + (unsigned)ucBuf[ 11], + (unsigned)ucBuf[ 12], + (unsigned)ucBuf[ 13], + (unsigned)ucBuf[ 14], + (unsigned)ucBuf[ 15]); + bValidOption = TRUE; + } + + if (!bValidOption) + { + pShell->con_printf( "Invalid option.\n"); + iExitCode = -1; + goto Exit; + } + +Exit: + + if( RC_BAD( rc)) + { + pShell->con_printf( "\nError: %e\n", rc); + if( !iExitCode) + { + iExitCode = rc; + } + } + + return( iExitCode); +} + +/**************************************************************************** +Desc: +*****************************************************************************/ +void FlmDbGetConfigCommand::displayHelp( + FlmShell * pShell, + char * pszCommand) +{ + if (!pszCommand) + { + pShell->displayCommand( "dbgetconfig", "Display DB configuration"); + } + else + { + pShell->con_printf("Usage:\n"); + pShell->con_printf( " %s